hacktricks/mobile-pentesting/android-app-pentesting/android-applications-basics.md
2023-06-03 01:46:23 +00:00

30 KiB

Fundamentos de Aplicaciones Android

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Sigue a HackenProof para aprender más sobre errores web3

🐞 Lee tutoriales sobre errores web3

🔔 Recibe notificaciones sobre nuevos programas de recompensas por errores

💬 Participa en discusiones comunitarias

Modelo de Seguridad de Android

Hay dos capas:

  • El SO, que mantiene las aplicaciones instaladas aisladas entre sí.
  • La aplicación en sí, que permite a los desarrolladores exponer ciertas funcionalidades y configurar las capacidades de la aplicación.

Separación de UID

A cada aplicación se le asigna un ID de usuario específico. Esto se hace durante la instalación de la aplicación para que la aplicación solo pueda interactuar con archivos propiedad de su ID de usuario o archivos compartidos. Por lo tanto, solo la aplicación en sí, ciertos componentes del SO y el usuario root pueden acceder a los datos de las aplicaciones.

Compartición de UID

Dos aplicaciones pueden configurarse para usar el mismo UID. Esto puede ser útil para compartir información, pero si una de ellas es comprometida, los datos de ambas aplicaciones serán comprometidos. Por eso este comportamiento está desaconsejado.
Para compartir el mismo UID, las aplicaciones deben definir el mismo valor android:sharedUserId en sus manifiestos.

Aislamiento

El Sandbox de Aplicaciones de Android permite ejecutar cada aplicación como un proceso separado bajo un ID de usuario separado. Cada proceso tiene su propia máquina virtual, por lo que el código de una aplicación se ejecuta en aislamiento de otras aplicaciones.
A partir de Android 5.0(L), se aplica SELinux. Básicamente, SELinux deniega todas las interacciones de procesos y luego crea políticas para permitir solo las interacciones esperadas entre ellos.

Permisos

Cuando se instala una aplicación y solicita permisos, la aplicación está solicitando los permisos configurados en los elementos uses-permission en el archivo AndroidManifest.xml. El elemento uses-permission indica el nombre del permiso solicitado dentro del atributo name. También tiene el atributo maxSdkVersion que deja de solicitar permisos en versiones superiores a la especificada.
Tenga en cuenta que las aplicaciones de Android no necesitan solicitar todos los permisos al principio, también pueden solicitar permisos dinámicamente pero todos los permisos deben ser declarados en el manifiesto.

Cuando una aplicación expone funcionalidad, puede limitar el acceso solo a aplicaciones que tengan un permiso especificado.
Un elemento de permiso tiene tres atributos:

  • El nombre del permiso
  • El atributo permission-group, que permite agrupar permisos relacionados.
  • El nivel de protección que indica cómo se otorgan los permisos. Hay cuatro tipos:
    • Normal: Se utiliza cuando no hay amenazas conocidas para la aplicación. El usuario no está obligado a aprobarlo.
    • Peligroso: Indica que el permiso ot

Filtro de Intenciones

Un filtro de intenciones especifica los tipos de Intenciones a los que una actividad, servicio o receptor de difusión puede responder. Especifica lo que una actividad o servicio puede hacer y qué tipos de difusiones un receptor puede manejar. Permite que el componente correspondiente reciba Intenciones del tipo declarado. Los filtros de intenciones se definen típicamente a través del archivo AndroidManifest.xml. Para el Receptor de difusión también es posible definirlos en código. Un filtro de intenciones se define por su categoría, acción y filtros de datos. También puede contener metadatos adicionales.

En Android, una actividad/servicio/proveedor de contenido/receptor de difusión es público cuando exported se establece en true, pero un componente también es público si el manifiesto especifica un filtro de intenciones para él. Sin embargo, los desarrolladores pueden hacer explícitamente privados los componentes (independientemente de cualquier filtro de intenciones) estableciendo el atributo exported en false para cada componente en el archivo de manifiesto. Los desarrolladores también pueden establecer el atributo permission para requerir un cierto permiso para acceder al componente, restringiendo así el acceso al componente.

Intenciones implícitas

Las intenciones se crean programáticamente utilizando un constructor de Intenciones:

Intent email = new Intent(Intent.ACTION_SEND, Uri.parse("mailto:"));

La Acción del intent previamente declarado es ACTION_SEND y el Extra es un Uri de correo electrónico (el Extra es la información adicional que el intent espera).

Este intent debe ser declarado dentro del manifiesto como en el siguiente ejemplo:

<activity android:name="ShareActivity">
	<intent-filter>
       <action android:name="android.intent.action.SEND" />
       <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Un intent-filter necesita coincidir con la acción, datos y categoría para recibir un mensaje.

El proceso de "resolución de intenciones" determina qué aplicación debe recibir cada mensaje. Este proceso considera el atributo de prioridad, que se puede establecer en la declaración del intent-filter, y se seleccionará el que tenga la prioridad más alta. Esta prioridad se puede establecer entre -1000 y 1000 y las aplicaciones pueden usar el valor SYSTEM_HIGH_PRIORITY. Si surge un conflicto, aparece una ventana de "selector" para que el usuario pueda decidir.

Intenciones explícitas

Una intención explícita especifica el nombre de la clase a la que se dirige:

Intent downloadIntent = new (this, DownloadService.class):

En otras aplicaciones, para acceder al intent previamente declarado, se puede utilizar:

Intent intent = new Intent();
intent.setClassName("com.other.app", "com.other.app.ServiceName");
context.startService(intent);

Intenciones pendientes

Estas permiten que otras aplicaciones realicen acciones en nombre de tu aplicación, utilizando la identidad y permisos de tu aplicación. Al construir una intención pendiente, se debe especificar una intención y la acción a realizar. Si la intención declarada no es explícita (no declara qué intención puede llamarla), una aplicación maliciosa podría realizar la acción declarada en nombre de la aplicación víctima. Además, si no se especifica una acción, la aplicación maliciosa podrá hacer cualquier acción en nombre de la víctima.

Intenciones de difusión

A diferencia de las intenciones anteriores, que solo son recibidas por una aplicación, las intenciones de difusión pueden ser recibidas por múltiples aplicaciones. Sin embargo, a partir de la versión API 14, es posible especificar la aplicación que debe recibir el mensaje utilizando Intent.set Package.

Alternativamente, también es posible especificar un permiso al enviar la difusión. La aplicación receptora necesitará tener ese permiso.

Hay dos tipos de difusiones: Normales (asincrónicas) y Ordenadas (sincrónicas). El orden se basa en la prioridad configurada dentro del receptor. Cada aplicación puede procesar, retransmitir o descartar la difusión.

Es posible enviar una difusión utilizando la función **sendBroadcast(intent, receiverPermission) ** de la clase Context.
También se puede utilizar la función sendBroadcast del LocalBroadCastManager que asegura que el mensaje nunca abandone la aplicación. Usando esto, ni siquiera necesitarás exportar un componente receptor.

Difusiones pegajosas

Este tipo de difusiones pueden ser accedidas mucho después de haber sido enviadas.
Estas fueron obsoletas en el nivel de API 21 y se recomienda no usarlas.
Permiten que cualquier aplicación husmee en los datos, pero también los modifique.

Si encuentras funciones que contienen la palabra "pegajoso" como sendStickyBroadcast o sendStickyBroadcastAsUser, verifica el impacto e intenta eliminarlas.

Enlaces profundos / Esquemas de URL

Los enlaces profundos permiten activar una intención a través de una URL. Una aplicación puede declarar un esquema de URL dentro de una actividad para que cada vez que el dispositivo Android intente acceder a una dirección utilizando ese esquema, se llame a la actividad de la aplicación:

En este caso, el esquema es myapp:// (también se observa la categoría BROWSABLE)

Si dentro del intent-filter encuentras algo como esto:

Entonces, está esperando algo como http://www.example.com/gizmos

Si encuentras algo como esto:

Significará que está esperando una URL que comience por example://gizmos
En este caso, podrías intentar abusar de la funcionalidad creando una web con las siguientes cargas útiles. Intentará navegar a páginas arbitrarias e intentará ejecutar JS:

<a href="example://gizmos/https://google.com">click here</a>
<a href="example://gizmos/javascript://%250dalert(1)">click here</a>

Para encontrar el código que se ejecutará en la aplicación, vaya a la actividad llamada por el enlace profundo y busque la función onNewIntent.

Aprenda cómo llamar enlaces profundos sin usar páginas HTML.

AIDL - Lenguaje de Definición de Interfaces de Android

El Lenguaje de Definición de Interfaces de Android (AIDL) le permite definir la interfaz de programación en la que tanto el cliente como el servicio acuerdan para comunicarse entre sí utilizando la comunicación entre procesos (IPC). En Android, un proceso normalmente no puede acceder a la memoria de otro proceso. Por lo tanto, para hablar, necesitan descomponer sus objetos en primitivas que el sistema operativo pueda entender y pasar los objetos a través de esa barrera para usted. El código para hacer esa transmisión es tedioso de escribir, por lo que Android lo maneja por usted con AIDL.

Los servicios que utilizan AIDL se denominan Servicios vinculados. En la clase del servicio encontrará el método onBind. Aquí es donde comienza la interacción, por lo que es la parte inicial del código a revisar en busca de posibles vulnerabilidades.

Un servicio vinculado es el servidor en una interfaz cliente-servidor. Permite que los componentes (como las actividades) se vinculen al servicio, envíen solicitudes, reciban respuestas y realicen comunicación entre procesos (IPC). Un servicio vinculado normalmente vive solo mientras sirve a otro componente de la aplicación y no se ejecuta en segundo plano indefinidamente.

Messenger

Un Messenger es otro tipo de mecanismo IPC. Dado que el Messenger también es un "Servicio vinculado", los datos que se pasan desde la aplicación cliente también se procesan a través del método onBind. Por lo tanto, la revisión del código debe comenzar en este método y debe buscar la invocación de funcionalidades sensibles o el manejo inseguro de datos.

Binder

Es extraño encontrar una clase Binder invocada directamente, ya que es mucho más fácil usar AIDL (que abstrae la clase Binder). Sin embargo, es bueno saber que Binder es un controlador de nivel de kernel que mueve datos de la memoria de un proceso a la de otro (https://www.youtube.com/watch?v=O-UHvFjxwZ8).

Componentes

Estos incluyen: Actividades, Servicios, Receptores de difusión y Proveedores.

Actividad de lanzamiento y otras actividades

Una actividad de Android es una pantalla de la interfaz de usuario de la aplicación de Android. De esa manera, una actividad de Android es muy similar a las ventanas en una aplicación de escritorio. Una aplicación de Android puede contener una o más actividades, lo que significa una o más pantallas.

La actividad de lanzamiento es lo que la mayoría de las personas piensan como el punto de entrada a una aplicación de Android. La actividad de lanzamiento es la actividad que se inicia cuando un usuario hace clic en el icono de una aplicación. Puede determinar la actividad de lanzamiento mirando el manifiesto de la aplicación. La actividad de lanzamiento tendrá las siguientes intenciones MAIN y LAUNCHER listadas.

Tenga en cuenta que no todas las aplicaciones tendrán una actividad de lanzamiento, especialmente las aplicaciones sin una interfaz de usuario. Ejemplos de aplicaciones sin una interfaz de usuario (y por lo tanto sin una actividad de lanzamiento) son las aplicaciones preinstaladas que realizan servicios en segundo plano, como el correo de voz.

<activity android:name=".LauncherActivity">
	<intent-filter>
    	<action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Las actividades pueden ser exportadas permitiendo que otros procesos en el dispositivo inicien la actividad. Por defecto, no están exportadas pero se pueden exportar estableciendo:

<service android:name=".ExampleExportedService" android:exported="true"/>

Ten en cuenta que la capacidad de saltarse las protecciones de actividad no siempre es una vulnerabilidad, debes comprobar a qué datos has obtenido acceso. Además, algunas actividades devuelven datos a un llamador. En estos escenarios, debes buscar el método setResult y comprobar los datos que se pasan al parámetro Intent. Si son datos sensibles, puede haber una vulnerabilidad de fuga de información y es explotable con aplicaciones capaces de comunicarse con la actividad.

El código de una actividad comienza con el método onCreate.

Subclase de aplicación

Las aplicaciones de Android pueden definir una subclase de Application. Las aplicaciones pueden, pero no tienen que definir una subclase personalizada de Application. Si una aplicación de Android define una subclase de Application, esta clase se instancia antes que cualquier otra clase en la aplicación.

Si se define el método attachBaseContext en la subclase de Application, se llama primero, antes del método onCreate.

Servicios

Los servicios se ejecutan en segundo plano sin una interfaz de usuario. Se utilizan para realizar procesos de larga duración, incluso si el usuario comienza a usar una aplicación diferente.

Hay una gran cantidad de formas en que se pueden iniciar y, por lo tanto, son un punto de entrada para las aplicaciones. La forma predeterminada en que un servicio puede iniciarse como punto de entrada a una aplicación es a través de Intents.

Cuando se llama al método startService para iniciar un servicio, se ejecuta el método onStart en el servicio. Se ejecutará indefinidamente hasta que se llame al método stopService. Si el servicio solo se necesita mientras el cliente está conectado, el cliente debe "vincularse" a él utilizando el método bindService.

Para un servicio vinculado (ver sección anterior), los datos se pasarán al método onBind.

Por ejemplo, un servicio podría reproducir música en segundo plano mientras el usuario está en una aplicación diferente, o podría recuperar datos a través de la red sin bloquear la interacción del usuario con una actividad.

Un servicio puede ser exportado, lo que permite que otros procesos en el dispositivo inicien el servicio. Por defecto, los servicios no se exportan, pero se pueden configurar en el Manifiesto:

<service android:name=".ExampleExportedService" android:exported="true"/>

Receptores de difusión

Las difusiones se pueden considerar como un sistema de mensajería y los receptores de difusión son los oyentes. Si una aplicación ha registrado un receptor para una difusión específica, el código en ese receptor se ejecuta cuando el sistema envía la difusión. Tenga en cuenta que en este caso varias aplicaciones pueden recibir el mismo mensaje.

Hay 2 formas en que una aplicación puede registrar un receptor: en el Manifiesto de la aplicación o registrado dinámicamente en el código de la aplicación utilizando la llamada de API registerReceiver. En el manifiesto, puede limitar las difusiones que acepta a través del uso de permisos dentro del elemento receptor. Cuando se define dinámicamente, puede pasar el permiso al método registerReceiver.

En ambos casos, para registrar el receptor, se establecen los filtros de intención para el receptor. Estos filtros de intención son las difusiones que deben activar el receptor.

Cuando se envían las difusiones específicas para las que se ha registrado el receptor, se ejecuta el método onReceive en la clase BroadcastReceiver.

Una aplicación puede registrar un receptor para el mensaje de batería baja, por ejemplo, y cambiar su comportamiento en función de esa información.

La difusión puede ser asincrónica (cada receptor la recibe) o sincrónica (la difusión se recibe de manera ordenada según la prioridad establecida para recibirla).

{% hint style="danger" %} Tenga en cuenta que cualquier aplicación puede establecerse como la máxima prioridad para recibir una difusión. {% endhint %}

Para examinar el código implementado en un receptor de difusión, debe buscar el método onReceive de la clase del receptor.
Tenga en cuenta que las difusiones ordenadas pueden descartar la intención recibida o incluso modificarla utilizando uno de los métodos setter. Por lo tanto, los receptores deben validar los datos.

Proveedor de contenido

Los proveedores de contenido son la forma en que las aplicaciones comparten datos estructurados, como bases de datos relacionales. Por lo tanto, es muy importante usar permisos y establecer el nivel de protección adecuado para protegerlos.
Los proveedores de contenido pueden usar los atributos readPermission y writePermission para especificar qué permisos debe tener una aplicación. Estos permisos tienen prioridad sobre el atributo de permiso.
Además, también pueden permitir excepciones temporales estableciendo grantUriPermission en verdadero y luego configurando los parámetros apropiados en el elemento grant-uri-permission dentro del elemento proveedor dentro del archivo de manifiesto.

El grant-uri-permission tiene tres atributos: path, pathPrefix y pathPattern:

  • path: Permite especificar la ruta completa para excluir
  • pathPrefix: Permite especificar el comienzo de la ruta
  • pathPattern: Permite el uso de comodines y reemplazos simbólicos para obtener un control más granular.

Es importante validar y sanear la entrada recibida para evitar posibles vulnerabilidades como la inyección SQL.

Características del proveedor de contenido:

  • El componente del proveedor de contenido suministra datos de una aplicación a otras a petición.
  • Puede almacenar los datos en el sistema de archivos, una base de datos SQLite, en la web o en cualquier otra ubicación de almacenamiento persistente a la que su aplicación pueda acceder.
  • A través del proveedor de contenido, otras aplicaciones pueden consultar o incluso modificar los datos (si el proveedor de contenido lo permite).
  • El proveedor de contenido es útil en casos en los que una aplicación desea compartir datos con otra aplicación.
  • Es muy similar a las bases de datos y tiene cuatro métodos.
    • insert()
    • update()
    • delete()
    • query()

FileProvider

Este es un tipo de proveedor de contenido que compartirá archivos desde una carpeta. Puede declarar un proveedor de archivos de esta manera:

<provider android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:grantUriPermissions="true" android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
</provider>

Ten en cuenta el atributo android:exported porque si es true las aplicaciones externas podrán acceder a las carpetas compartidas.
Ten en cuenta que la configuración android:resource="@xml/filepaths" indica que el archivo res/xml/filepaths.xml contiene la configuración de qué carpetas este FileProvider va a compartir. Este es un ejemplo de cómo indicar que se comparta una carpeta en ese archivo:

<paths>
    <files-path path="images/" name="myimages" />
</paths>

Compartir algo como path="." podría ser peligroso incluso si el proveedor no está exportado si hay otra vulnerabilidad en alguna parte del código que intentó acceder a este proveedor.
Podrías acceder a una imagen dentro de esa carpeta con content://com.example.myapp.fileprovider/myimages/default_image.jpg

El elemento <paths> puede tener varios hijos, cada uno especificando un directorio diferente para compartir. Además del elemento <files-path>, puedes usar el elemento <external-path> para compartir directorios en el almacenamiento externo, y el elemento <cache-path> para compartir directorios en tu directorio de caché interno.
Para obtener más información sobre los atributos específicos de los proveedores de archivos, ve aquí.

Más información sobre FileProviders aquí.

WebViews

Los WebViews son efectivamente navegadores web integrados en aplicaciones de Android.
El contenido de los WebViews puede ser extraído de sitios remotos o pueden ser archivos incluidos en la aplicación.
Los WebViews son vulnerables a las mismas vulnerabilidades que afectan a cualquier navegador web. Sin embargo, hay algunas configuraciones que pueden ser útiles para limitar la superficie de ataque.

Hay dos tipos de WebViews en Android:

  • El WebViewClient, más adecuado para la representación de HTML simple. Esto no ejecutará la función de alerta JS. Por lo tanto, las pruebas XSS que utilizan esa función serán inválidas.
  • El cliente WebChrome, es un navegador Chrome.

Tenga en cuenta que los navegadores WebView no tienen acceso a las cookies del navegador nativo.

Para cargar una URL o archivo, es posible utilizar las funciones loadUrl, loadData o loadDataWithBaseURL. Es importante acceder sólo a URLs sanitizadas.
La seguridad de WebView se puede configurar a través del objeto WebSettings.
Por ejemplo, la ejecución de código JS se puede desactivar utilizando el método setJavaScriptEnabled con el valor false. Esto eliminará la posibilidad de una vulnerabilidad de XSS y otras vulnerabilidades relacionadas con JS.

La funcionalidad de JavaScript "Bridge" inyecta objetos Java en un WebView haciéndolos accesibles a JS. A partir de Android 4.2, los métodos deben estar anotados con @JavascriptInterface para que sean accesibles desde JavaScript.

Si se pasa true a setAllowContentAccess, los WebViews podrán acceder a los Proveedores de Contenido a través del esquema content://. Esto obviamente plantea un riesgo de seguridad. Tenga en cuenta que si se da este acceso, es muy importante asegurarse de que la URL content:// es segura.

Por defecto, los archivos locales pueden ser accedidos por los WebViews a través de las URLs file://, pero hay varias formas de evitar este comportamiento:

  • Pasando false a setAllowFileAccess, se evita el acceso al sistema de archivos con la excepción de los activos a través de file:///android_asset y file:///android_res. Estas rutas deben ser utilizadas sólo para datos no sensibles (como imágenes), por lo que esto debería ser seguro.
  • El método setAllowFileAccess indica si una ruta de una URL file:// debe ser capaz de acceder al contenido de otras URLs de esquema de archivo.
  • El método setAllowUniversalAccessFromFileURLs indica si una ruta de una URL file:// debe ser capaz de acceder al contenido de cualquier origen.

Otros componentes de la aplicación

Firma de la aplicación

  • Android requiere que todas las aplicaciones estén firmadas digitalmente con un certificado antes de que puedan ser instaladas. Android utiliza este certificado para identificar al autor de una aplicación.
  • Para ejecutar la aplicación en el dispositivo, debe estar firmada. Cuando se instala la aplicación en un dispositivo, el administrador de paquetes verifica si la aplicación ha sido correctamente firmada con el certificado del archivo apk o no.
  • La aplicación puede ser auto-firmada o puede ser firmada a través de una CA.
  • La firma de la aplicación asegura que una aplicación no pueda acceder a ninguna otra aplicación excepto a través de IPC bien definidos y también que se pase sin modificaciones al dispositivo.

Verificación de la aplicación

  • Android 4.2 y posteriores admiten la verificación de aplicaciones. Los usuarios pueden optar por habilitar "Verificar aplicaciones" y hacer que las aplicaciones sean evaluadas por un verificador de aplicaciones antes de la instalación.
  • La verificación de la aplicación puede alertar al usuario si intenta instalar una aplicación que podría ser perjudicial; si una aplicación es especialmente mala, puede bloquear la instalación.

Gestión de dispositivos móviles

MDM o Mobile Device Management son suites de software que se utilizan para asegurar un control y requisitos de seguridad sobre los dispositivos móviles. Estas suites utilizan las características referidas como API de administración de dispositivos y requieren que se instale una aplicación de Android.

Generalmente, las soluciones de MDM realizan funciones como la imposición de políticas de contraseñas, la obligación de cifrar el almacenamiento y la posibilidad de borrar los datos del dispositivo de forma remota.

Sigue a HackenProof para aprender más sobre errores web3

🐞 Lee tutoriales de errores web3

🔔 Recibe notificaciones sobre nuevos programas de recompensas por errores

💬 Participa en discusiones comunitarias

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥