1er Encuentro

Agenda

DIA 1 -> FUNDAMENTOS

  • Armado de entorno de trabajo
  • Sistema Operativo Android
  • ¿Qué es un APK? ¿Cómo conseguirlo?
  • Decompilar una APP
  • Análisis de cadenas de texto

DIA 2

  • Componentes de una APP
  • Análisis estático

DIA 3

  • Análisis de tráfico
  • Análisis dinámico

Laboratorio

Necesitamos una máquina de análisis y un Emulador de un dispositivio Android. LAs opciones que tenemos son las siguientes: Máquina de Analisis: Host con GNU/Linux Máquina virtual con GNU/Linux Docker containers Emulador: Genymotion Android Virtual Device (es parte de Android Studio) En vez de un Emulador es posible usar un dispositivo rooteado

La comunicación entre la máquina de análisis y el emulador se da con Android Device Bridge (ADB):

  • Herramienta de línea de comandos para conectarse con un dispositivo Android y enviar comandos.
  • En el dispositivo corre un daemon (adbd) & en la máquina de análisis/desarrollo ejecutamos un cliente que envía comandos.
  • Proporciona acceso a un shell de Unix dentro del dispositivo. Más información: https://developer.android.com/studio/command-line/adb?hl=es-419
[VM]$ adb connect <IP-Emulador>
[VM]$ adb shell
[emulador]> vbox86p:/ #

Capas de tecnologías

Hay que tener en cuenta que cuando se analizan las aplicaciones se trabaja sobre una capa que se monta sobre el Sistema Operativo y éste sobre el Hardware.

Fuente: https://citizenlab.ca/wp-content/uploads/2015/05/1.png

Fuente: https://citizenlab.ca/wp-content/uploads/2015/05/1.png

Qué es una APP?

Identificada como: com.package.nombreAPP. Por ejemplo: Telegram -> org.telegram.messenger https://play.google.com/store/apps/details?id=org.telegram.messenger

Es un archivo .apk, es decir un “instalador empaquetado” que contiene todos los elementos que la app necesita para funcionar en el celular. Se podría pensar que es como un .exe de Window$ pero .apk para Android o un JAR/ZIP file con código + recursos.

Estructura de un APK

Si descomprimimos un archivo APK, nos encontramos con los siguientes archivos:

Para eso en la términal de nuestra máquina de análisis debemos correr:

[VM]$ cd ~/Desktop/apps
[VM]$ unzip APP.apk -d output-unzip
[VM]$ apktool d APP.apk -o output-apktool

Sistema Operativo Android

Kernel con Androidismos (IPC, manejo de memoria, logging específicos de Android) + android runtime + software stack (GUI environment & frameworks). Más información en: https://developer.android.com/guide/platform

Modelo de Seguridad de Android

Sandboxing

El concepto de sandboxing o entorno aislado consiste en una “caja de arena” (sandbox), que permite ejecutar programas/apps en un espacio aislado y limitado.

Fuente: http://hiqes.com/

Fuente: http://hiqes.com/

Permite entre otras cosas:

  • la separacion de UID x app
  • contar con un directorio de datos privado
  • que las apps se ejecuten en ambientes separados

Ejemplo Estructura de archivos con Sandboxing

En un dispositivo encontramos la siguiente estructura de archivos:

  • /data/data ~ datos de apps instaladas en dispositivo
  • /data/app ~ apps instaladas por usuario

La siguiente imagen ilustra el funcionamiento de los permisos de archivos y directorios presentes en GNU/Linux.

Fuente: https://www.prored.es/los-permisos-de-unix-linux-y-mac-os/

Fuente: https://www.prored.es/los-permisos-de-unix-linux-y-mac-os/

Teniendo en cuenta ello es posible observar como diferentes aplicaciones instaladas en un dispositivo cuentan con diferentes UserIDs (UIDs) en sus respectivos directorios privados.

[VM]$ adb shell "pm list packages"

Una applicación cuenta con un UID de u0_a71 y la segunda con un UID de u0_a93.

Análisis de una aplicación

Este proceso se puede pensar de manera simplificada como compuesto de los siguientes 3 pasos.

  1. Conseguir el binario de la app para analizar.
  2. Obtener una versión decompilada del código fuente a partir del binario.
  3. Análisis inicial de información adentro de app.

Para el siguiente ejemplo se va a utilizar la aplicación “Purposefully Insecure and Vulnerable Android App” (PIVAA) disponible en: https://github.com/HTBridge/pivaa.

1. Conseguir el binario de la app para analizar.

Existen dos estrategias para obtener el .apk. La primera es descargar la app en la computadora y transferirla al celular. Para ello es posible encontrar los apks en markets alternativos a Google Play como:

  1. APKCombo -> https://apkcombo.com/apk-downloader/
  2. APKPure -> https://apkpure.com/
  3. Evozi -> https://apps.evozi.com/apk-downloader/
  4. Extension de Chrome: APK Downloader for Google Play -> https://chrome.google.com/webstore/detail/apk-downloader-for-google/idkigghdjmipnppaeahkpcoaiphjdccm?hl=es-419 Para transferirla al celular/emulador podemos arrastrar el apk directo al dispositivo (desde el host) o sino directamente usar adb para instalarla.
[VM]$ adb install app.apk

[VM]$ adb push <src> <dest>
[VM]$ adb push app.apk /sdcard/

La segunda es descargar la app en el celular y transferirla a la computadora. Para ello instalamos los apks/apps en el dispositivo usando Google Play Store (Genymotion Gapps/ Dispositivo rooteado). Y transferimos el apk desde el dispositivo a la computadora para su análisis.

[VM]$ adb shell "pm list packages"
[VM]$ adb shell "pm path com.nombre.APP"
package:/data/app/com.nombre.APP.../base.apk

[VM]$ adb pull <src> <dest>
[VM]$ adb pull /data/app/com.nombre.APP.../base.apk /tmp/vm-lab/

[VM]$ adb shell "pm path com.htbridge.pivaa"
package:/data/app/com.htbridge.pivaa-Y..==/base.apk

[VM]$ adb pull /data/app/com.htbridge.pivaa-Y..==/base.apk /tmp/out/

2. Obtener una versión decompilada del código fuente a partir del binario.

Cuando se desarrolla una aplicación se crea el código de la misma y luego de una serie de pasos se la compila en un binario .apk firmado por le desarrolladorx.

Dado que nosotrxs contamos con el binario solamente, tenemos que lograr el proceso inverso: a partir del apk obtener una versión del código fuente de la aplicación.

A este proceso se lo denomina Decompilación, y tendrá la siguiente forma:
Como se observa en la ilustración una primera estrategia podrá ser utilizar la herramienta apktool para obtener el código smali de la aplicación. El smali es similar al código assembly de un binario en GNU/Linux. Una segunda estrategia será utilizar la herramienta jadx para obtener directamente el código java. Comparando ambos resultados de la decompilación se podrá inferir que la segunda estrategia nos devuelve un código más humanamente legible.

Para obtener el código smali con apktool deberemos correr los siguientes comandos:

[VM]$ adb shell
[emulador]$ pm list packages -f | grep appName
[emulador]$ pm path appName

[VM]$ adb pull /data/app/com.app.name.../base.apk  .

[VM]$ apktool d base.apk -o output

Mientras que para obtener el código java usando jadx deberemos correr los siguientes comandos:

[VM]$ cd ~/Desktop/herramientas/jadx
[VM]$ ./gradlew dist

[VM]$ jadx app.apk  -d dir  
 
[VM]$ jadx-gui app.apk 

3. Análisis inicial de información adentro de app.

En este punto seguiremos los siguientes pasos para realizar el análisis de la aplicación:

  1. Instalarla en Emulador. (Desinstalarla antes si es necesario)
  2. Transferir el .apk del Emulador a la máquina virtual
  3. Decompilar código fuente
  4. Buscar información “hardcodeada” en el código decompilado. (Hardcodeado: información escrita directamente en el código).

Los siguientes comandos nos permitirán instalar el apk en el emulador y decompilar su código fuente.

[emulador]$ pm list packages -f | grep appName
[emulador]$ pm path appName

[VM]$ adb pull /data/app/com.htbridge.pivaa-XX==/base.apk .
[VM]$ unzip -e base.apk -d contents   dex == binary dalvik bytecode
[VM]$ apktool d base.apk              apk ~> dex ~> smali
[VM]$ jadx base.apk  -d dir           apk ~> java code
[VM]$ jadx-gui base.apk

Luego para buscar información hardcodeada en el código decompilado lo que hacemos es buscar por:

  1. palabras claves
  2. credenciales / api keys
  3. urls / endpoints
  4. información de debug
  5. secretos hardcodeados

El siguiente comando nos permitirá encontrar coincidencias para la palabra password dentro de la totalidad del código decompilado de la aplicación dentro de la aplicación PIVAA:

[VM]$ grep -R password
sources/com/htbridge/pivaa/Configuration.java:    
public String password = "verycomplicatedpassword";
...

Después de encontrar esta coincidencia restará continuar viendo el código fuente para enteneder en qué contexto se utilizan estas credenciales.

Ejemplo secretos hardcodeados

El siguiente es un ejemplo que unx investigadorx bajo el nombre de bagipro reportó a la plataforma Hackerone, luego de conseguir una filtracion de archivos por una apikey hardcodeada en el código decompilado de una aplicación. El reporte se puede leer completo en: https://hackerone.com/reports/351555 En la imagen tomada del reporte público se pueden ver las credenciales hardcodeados en el código de la aplicación y cómo estas credencialesle permiten a bagipro acceder a endpoints con información sensible y exfiltrarla.

2do Encuentro

Componentes de una aplicación

Entre los componentes de una aplicación se encuentran:

  1. MANIFEST
  2. ACTIVITIES
  3. CONTENT PROVIDERS
  4. SERVICES
  5. BROADCAST RECEIVERS
    Para analizarlos utilizaremos la herramienta jadx(disponible en: https://github.com/skylot/jadx), que puede ser usada desde la línea de comando o a través de su GUI. Esta herramienta no sólo decompila el .apk a código Java, si no que también decodifica el archivo AndroidManifest.xml. Permite la navegación a través del llamado a funciones y la búsqueda de coincidencias de texto.

Manifiesto

El Manifiesto de una aplicación se encuentra definido en el archivo Androidmanifest.xml. Es un archivo de configuración donde se especifican los permisos que declara la app, se describen sus componentes (expuestos o no) y los puntos de entrada al código.

Usando jadx-gui podemos ver de manera simple el Manifiesto de una aplicación como PIVAA por ejemplo.

[VM]$ jadx-gui ~/Desktop/apps/pivaa.apk

En el Manifiesto, entre otras cosas, se definen los permisos que solicita la aplicación. Los permisos protegen el acceso a datos sensibles, como por ejemplo el estado del dispositivo e información de contactos de une usuarie. También restringen el acceso a acciones privilegiadas, como acceso a la cámara y grabación de audio. Los permisos están declarados en el Manifiesto a través del tag <uses-permission...> y desde la versión 11 de Android son de dos tipos: los permisos “Normales” (cedidos automáticamente en tiempo de instalación) y los denominados “Peligrosos” (cedidos en tiempo de ejecución).Más información sobre este punto se puede encontrar en la documentación oficial de Android: https://developer.android.com/reference/android/Manifest.permission.

Categorización de permisos de una app con MobFS

Categorización de permisos de una app con MobFS

Activities, Servicios, Broadcasts Receivers y Content Providers

Activities: un activity es el código que corresponde a una pantalla con una interfaz de usuarie. Se llama Activity al código asociado a una actividad de le usuarie como mandar un mail o sacar una foto.

Servicios: se ejecutan en el fondo o “background” y son procesos de largo término, sin interfaz de usuarix. Se utilizan por ejemplo para descargar un archivo, sincronizar mails, reproducir música.

Broadcasts Receivers: es un componente que le permite a una app registrase frente a un evento (del sistema o de una app) para recibir mensajes en relación al mismo. Por ejemplo, frente a una llamada entrante una aplicación de música detiene la reproducción.

Content Providers: le permiten a una app encapsular información para usarla o que lo use otra app. Administran el acceso a un repositorio de datos, que puede ser una base de datos, XML o un archivo.

Intents

Los intents son mensajes que permiten la comunicación entre componentes (dentro de una misma app o de diferentes apps). Le permiten a un componente de una app solicitar una funcionalidad (y enviar datos) a otros componentes. La siguiente ilustración es un ejemplo de un componente de una aplicación que envía a otro componente de otra aplicación un Intent, en este caso un mensaje que declara la intención de Capturar una foto.

Esta otra ilustración ejemplifica cómo una aplicación de correo electrónico despliega un hipervínculo dentro de un email, que al ser presionado por le usuario hace que esa aplicación envié un Intent solicitando visualizar una página web. Esta intención es recibida por el navegador que le usuarie tiene instalado en su dispositivo, en este caso Firefox Mozilla será el encargado de navegar a esa página web.

El pseudo-código en la aplicación encargado de esta comunicación entre componentes es similar a este:

Intent msj = new Intent("android.intent.action.VIEW");
String url = "https://www.link.com/";
msj.setData(Uri.parse(url));
startActivity(msj);

Cuando un componente invoca al método startActivity() el sistema operativo se encarga de detectar que otro componente se está solicitando o qué otro componente puede responder a ese intent. Ese componente que responde ejecutará el método onCreate().

Fuente: https://developer.android.com/guide/components/intents-filters

Fuente: https://developer.android.com/guide/components/intents-filters

El pseudo-código de la comunicación entre componentes es similar a:

Ciclo de vida de las Activities

Para poder analizar el código decompilado de una aplicación debemos tener en cuenta el Ciclo de vida de ese componente, es decir, qué métodos son llamados automáticamente cuando le usuarie interactua con e Activity. Entender este flujo de código nos permitirá auditar más facilmente el código de la aplicación a analizar. Los puntos de entrada de un Activity son los siguientes:

  1. onCreate()-> se crea por primera vez.
  2. onStart() -> es visibile al usuario.
  3. onResume()-> le usr comienza a interactuar
  4. onStop() -> no es más mostrada al usr.
  5. onDestroy()-> el sistema la destruye

Une desarrolladore podrá elegir implementar estos métodos o no, según desee que la aplicación responda de cierto modo a estas acciones por parte de le usuarie.

https://developer.android.com/guide/components/activities/activity-lifecycle

https://developer.android.com/guide/components/activities/activity-lifecycle

Análisis estático

Cuando analizamos una app realizamos análisis estático cuando examinamos su código decompilado sin ejecutarla. Esta estrategia, combinada con el análisis dinámico que veremos más adelante, nos permitirá identificar código vulnerable en la app. La estrategia de qué y cómo de analiza estáticamente una app puede pensarse a partir de los siguientes disparadores.

  • Búsqueda de palabras clave o patrones vulnerables de código.
  • Búsqueda de api keys, credenciales, secretos hardcodeados.
  • Identificación de funciones importantes: autenticación, cambios de estado, PII.
  • Identificación de funciones peligrosas: uso de almacenamiento externo, ejecución de código. Sanitización.
  • Identificación de funcionalidad de debug. Presencia de comentarios en el código.
  • Identificar cómo se utiliza el input de usuarix, a partir de lo que se denomina “Tainted analysis”, es decir tomar en cuenta el origen (o “source”) de datos de usuaries que pueden ser maliciosos y entender donde esos datos terminan (el “sink” o pozo).

El proceso de análizar estáticamente una app se vale de herramientas como jadx, que como vimos nos permiten una lectura detenida del código decompilado y del Manifiesto.

3er Encuentro

Vulnerabilidades mobile

A continuación se presentan dos recursos muy útiles que resumen las vulnerabilidades que existen en aplicaciones móviles. El primero es el “Mobile Security testing guide” de OWASP (https://mobile-security.gitbook.io/mobile-security-testing-guide/).

El segundo es un checklist para testear aplicaciones en Android (https://www.xmind.net/m/GkgaYH/) creado por Harsh Bothra.
A continuación analizaremos en detalle algunas de las vulnerabilidades que se detallan en ambos recursos.

Componentes expuestos

La guía de OWASP define a esta vulnerabilidad dentro de la categoría “M1 Uso inapropiado de la plataforma” y el Checklist la define como “Bypass de autenticacion via activities exportados”. Básicamente ésta vulnerabilidad de seguridad se presenta cuando una aplicación expone de manera insegura componentes como por ejemplo Activities. Cuando analizamos este bug debemos preguntarnos:

  1. ¿De qué fuente recibe una app los mensajes que llegan/inician sus componentes?
  2. ¿Qué componentes están expuestos para recibir mensajes?
  3. ¿Quién puede iniciar los componentes de una app?

Como las actividades pueden exportarse para que otras aplicaciones puedan utilizarlas, una vulnerabilidad muy común es no declarar qué aplicaciones pueden hacer uso de ella. Si el acceso a una actividad no está restringido, cualquier aplicación puede lanzarla. Esto puede desencadenar en que una aplicación maliciosa pueda obtener información sensible del dispositivo, o realizar acciones sin el consentimiento de unx usuarix. Para identificar esta vulnerabilidad tenemos que entender qué componentes de una app están exportados sea explícita o implícitamente. Para eso nos toca analizar el Manifiesto de la app.

Como se ve en la imagen un componente está expuesto de manera explícita cuando tiene la propiedad de exported="true" definida. Mientras que estará implícitamente expuesto si define un <intent-filter>. Sea del modo que sea, ambos casos deberán ser analizados para entender si el exponer ese componente tiene implicancias de seguridad.

A modo de ejemplo analizaremos la aplicación “InsecureAPP” en busca de este tipo de vulnerabilidad en su código. Primero la instalaremos en el emulador, decompilaremos su código y, finalmente, analizaremos sus componentes y flujo de ejecución. Todo ello lo haremos ejecutando los siguientes comandos:

# transferimos el apk al emulador
[VM]$ adb connect <ip emulador>
[VM]$ adb shell "pm list packages -f" | grep insecurepass
/data/app/com.dns.insecurepass-Y25..==/base.apk

[VM]$ adb pull /data/app/com.dns.insecurepass-Y25../base.apk .

# Decompilamos su código fuente
[VM]$ jadx-gui ~/Desktop/apps/base.apk

Para hacer una prueba de concepto que explote este tipo de vulnerabilidad se puede crear una aplicación maliciosa que invoque al componente expuesto o, de manera más simple, usar adb para invocar directamente al componente expuesto simulando ser una aplicación maliciosa.

[VM]$ adb shell "pm list packages"
[VM]$ adb shell "am start -n com.nombre.app/com.nombre.app.NombreActivity"

Observando el Manifiesto de InsecureAPP vemos que expone el Activity con el nombre “com.dns.insecurepass.MainActivity”, ya que en el Manifiesto se lee lo siguiente:

<activity android:name="com.dns.insecurepass.MainActivity" android:exported="true"/>

Usando adb lograremos invocar ese activity puntualmente:

[VM]$ adb shell "am start -n com.dns.insecurepass/com.dns.insecurepass.MainActivity"

MobFS

Existen numerosas herramientas para hacer análisis de aplicaciones. Una de la más conocida es el “Mobile Security Framework” (MobSF) es una herramienta para el análisis automatizado de aplicaciones móviles (https://mobsf.github.io/docs/#/). Analiza apps de Android y iOS y cuenta con mon módulos de análisis de malware y otros vinculados a análisis de seguridad más específicos. Asímismo, permite hacer análisis estático del código del apk y análisis dinámico. Una vez instalada se ejecuta de la siguiente manera:

[VM]$ cd ~/Desktop/herramientas/Mobile-Security-Framework-MobSF
[VM]$ ./run.sh 127.0.0.1:8000

# Abrimos en el navegador http://127.0.0.1:8000 
Y analizamos la app que deseamos.

Como resultado la herramienta nos devolverá información como la que ilustra la imagen:

No sólo detallará componentes expuestos, si no también URLs presentes en el código fuente de la app, cadenas de texto, nos permitira acceder al Manifiesto y al código decompilado, entre otras cosas.

Análisis dinámico de una app

El análisis dinámico es el que realizamos en tiempo de ejecución, es decir, mientras se ejecuta la app. En general este tipo de análisis sirve para complementar el análisis estático y realizar un análisis del tráfico entre la app y el servidor, modificar paramétros y valores de retorno de funciones y evitar detecciones anti-ingeniería inversa.

Frida

Una de las herramientas más utilizadas para hacer análisis dinámico es Frida (https://frida.re/docs/android/).

Frida es una herramienta de instrumentacion dinamica de codigo que instrumenta un agente que se inyecta en tiempo de ejecución. Esto permite inyectar scripts dentro de proceso en el que se ejecuta la app. Asísmismo con Frida se puede explorar la memoria del proceso, enumerar las funciones en ejecucióny alterar el funcionamiento de una app mientras se está ejecutando.

Para analizar la app con Frida debemos ejecutar los siguientes comandos:

Usando la IP del emulador
[emulador] $ /data/local/tmp/frida-server -l 192.168.0.214 & 

[VM]$ frida -U com.package.app -l script.js

De este modo, el script de Frida se registra (“engancha”) al método que se quiere alterar. Pudiendo cambiar los parámetros de entrada o salida de la función hookeada. Entonces, cuando la aplicación invoque al método original, se ejecutará también el script.

Objection

Una segunda herramienta muy utilizada para realizar análisis dinámico es Objection (https://github.com/sensepost/objection).

Es una herramienta de instrumentacion dinamica de codigo basada en Frida, que permite alterar el comportamiento de una app mediante la intercepción de las llamadas a funciones, mensajes o eventos entre componentes.

Intercepción de tráfico entre una app y el servidor

Una parte importante del análisis de seguridad de una aplicación es poder acceder a los requests que la app hace al servidor. Para acceder a ellos se deberá usar un Proxy.

Poder interceptar este tráfico nos permitirá:

  1. Ver que hace el backend (APIs, logica)
  2. Entender mas sobre la app sin el codigo
  3. Poder ver info sensible que la app maneja/envía
  4. Identificar inputs/outputs de la app, entry points, vulnerabilidades
  5. Acelerar el testing de la app

Dos de los proxies de tráfico más conocidos son Burp y Mitm proxy. Burp tiene una versión freemium y otra paga muy cara, soporta extensiones y consume muchos recursos de RAM. En cambio Mitm proxy es software libre, cuenta con una CLI que permite crear scripts en Python y consume muchos menos recursos de RAM.

SSL Pinning

Cada vez más frecuentemente cuando queremos analizar el tráfico de una aplicación nos encontramos que no podemos debido a la implementacion de SSL pinning en la app. Cuando una app se comunica se quiere comunicar de manera segura con el servidor implementa SSL pinning, de modo que el servidor no confía en los certificados del dispositivo donde corre la app sino únicamente en certificados “pinned” en el código de la app. A grandes rasgos este mecanismo se implementa “hardcodeado” el certificado que deberá usar la app en su código fuente.

Entonces si la aplicación que estamos analizando cuenta con SSL pinning implementado, utilizar un proxy en el medio con su propio certificado detendrá todo el tráfico entre la app y el servidor. En estas ocasiones la solución viene de la mano de las herramientas de análisis dinámico que mencionamos como Frida y Objection.

Existen scripts de Frida, disponibles publicamente en https://codeshare.frida.re/ que se ocupan de implementar bypasses a las implementaciones más comunes de SSL pinning.

Se podrá correr un script como ese con los siguientes comandos:

[VM]$ adb shell "/data/local/tmp/frida-server &"

[VM]$ frida --codeshare pcipolloni/universal-android-ssl-pinning-bypass-with-frida -f APP

Cierre

A modo de cierre, en el siguiente taller hemos trabajado sobre los siguientes temas:

  • Sistema Operativo Android

    • Sandboxing
    • Componentes | Intents
  • Vulnerabilidades

    • Secretos hardcodeados
    • Componentes expuestos
  • Armado de laboratorio

    • Emulador & Máquina virtual de análisis
  • Estrategias de análisis

    • Análisis de estático: patrones, flujo de ejecucion
    • Análisis dinámico: intercepción de tráfico
  • Herramientas Jadx | Mobfs | Objection | Frida | Burp/MITM proxy