A lo largo del tiempo, como consultor de seguridad especializado en aplicaciones móviles, me he convencido cada vez más de que estas aplicaciones guardan y pueden llegar a exponer información sensible y confidencial de las empresas.

En otras palabras, para un cibercriminal es posible robar datos de una compañía usando aplicaciones de Android o iOS, porque desde ellas se puede obtener información sensible que en ocasiones puede comprometer la totalidad del servidor.

¿Qué tipo de información es accesible? Normalmente durante el análisis podemos encontrar diversos descuidos por parte de los desarrolladores o información almacenada de manera insegura dentro de las apps. Desde URL internas, direcciones que apuntan a entornos pre-productivos o de desarrollo o rutas sobre producción que no conocíamos hasta archivos relacionados a la puesta en producción (.gitignore, etc).

un desarrollo inseguro puede derivar en la exposición de información sensible de todo tipo

En ocasiones también he encontrado servicios utilizados por los desarrolladores (FTP, SFTP, SSH, etc.) dentro del código decompilado de la aplicación; archivos .XML dentro de la carpeta “asset” o dentro de otras carpetas internas que poseían archivos de configuración como “config.xml”, “routes_config.xml” o archivos de “test cases” de la aplicación con información sensible (usuarios, contraseñas, direcciones IP, servicio, etc).

Finalmente, es posible encontrar información variada almacenada dentro de las bases de datos u otros archivos generados de manera local dentro de directorios de caché, librerías de terceros o de uso interno.

En esta publicación revisaremos algunos métodos por los cuales un atacante podría obtener el código de una aplicación y tres “enfoques de análisis” iniciales que permiten detectar cuándo una de ellas puede generar una fuga de información interna.

Describiré estos métodos desde una perspectiva black-box, es decir, suponiendo que el atacante no tiene más información previa que una app disponible en Google Play Store o el nombre de esta para utilizar algún enlace público de descarga. A partir de eso comenzaremos el análisis para ver qué información se podría revelar.

¿Cómo podría un atacante obtener tu aplicación?

Vamos a ver tres métodos por los que un atacante puede obtener una aplicación, basados en el enfoque black-box anteriormente descrito:

1. Por URL externa

Cuando buscamos una aplicación en Google Play Store nuestra barra de direcciones muestra una URL que podemos copiar fácilmente; esta URL almacena el ID con el cual se identifica a la aplicación dentro de la tienda.

Obteniendo la URL de una aplicación

Descargando una aplicación a través de su ID

Luego bastaría buscar distintas URL que brindan el servicio de “descarga” de una aplicación determinada (básicamente utilizan el enlace de Google Play Store o el ID de la aplicación). Con lo cual, una vez descargada, podremos analizarla estática y dinámicamente.

2. Por medio de una aplicación instalada en el dispositivo (método aplicación de tercero)

Este escenario contempla un dispositivo sin usuario root y permite obtener la aplicación de interés utilizando otra aplicación.

Nuevamente, se la puede buscar en Google Play Store, descargarla e instalarla en nuestro dispositivo, e instalar también otra aplicación que permita la extracción del primer APK instalado. Así, bastaría abrir la segunda para buscar la primera y extraer el APK instalado en una ruta del teléfono a la cual se tiene acceso, por ejemplo en el almacenamiento externo del teléfono: /storage/sdcard0/.

Luego, haría falta copiarla a nuestra computadora de trabajo utilizando la herramienta adb pull.

Selección de la aplicación a extraer.

Extracción usando otra app.

3. Por medio de un celular con root

Este sería el método más confiable, porque garantiza la integridad de la aplicación. Hay que tener en cuenta que en el primer método se usó una URL desconocida, sobre la que no se tiene control, por lo que es prácticamente imposible determinar si el APK que se descarga es el correcto, si se encuentra en su última versión y sin alteraciones ni agregados.

En el segundo método también se usó una aplicación de un tercero y el escenario es el mismo. Pero con este último método eso no sucede.

Consiste en ir a Google Play Store, buscar y descargar la aplicación de interés, e instalarla dentro del almacenamiento interno. Como el dispositivo tiene permisos de “root”, se la puede extraer y analizar directamente desde esa ruta, por ejemplo transfiriéndola a nuestra PC utilizando nuevamente adb pull, esta vez sin que haya intermediarios entre el dispositivo y la aplicación: ningún sitio, ninguna app de terceros, solo una descarga directa desde Google Play Store.

Extracción utilizando "adb pull"

¿Cómo se podría analizar o "atacar" esa aplicación?

A grandes rasgos, para ver qué información puede revelar esta aplicación obtenida podemos señalar tres tipos de enfoques de análisis iniciales:

  • Análisis estático

Básicamente se busca obtener el código de la aplicación en forma clara y transparente para poder inspeccionarlo, es decir, obtener el .java a partir del APK descargado. Para ello se debe utilizar un decompilador de Java como Jadx, Procyon, CFR o JD-GUI, entre otros, quien será el encargado de facilitarnos el código .java.

Una vez decompilada la aplicación deberíamos abrirla con un editor de textos (en mi caso utilicé Sublime), el cual permite analizar e interpretar fácilmente el código obtenido de la aplicación, y recorrer las distintas rutas y subcarpetas internas de la aplicación en busca de vulnerabilidades dentro de la misma.

La facilidad de encontrar vulnerabilidades u otros hallazgos dependerá de factores como, por ejemplo, si la aplicación está ofuscada o si sus desarrolladores tuvieron malas prácticas, como el caso clásico de encontrar información sensible “hardcodeada”.

También es posible buscar por medio de expresiones regulares direcciones IP públicas o privadas, dominios asociados a la aplicación, configuraciones SMTP, etc.

  • Análisis de transferencia de información

Principalmente consiste en hacer un ataque Man In The Middle a la aplicación; si esta no implementa HTTPS, simplemente basta con redirigir el tráfico del dispositivo a nuestra computadora. Caso contrario, es necesario instalar una CA de confianza dentro del dispositivo, configurar el proxy y redirigir todo el tráfico generado por la aplicación hacia nuestra computadora.

Así, el atacante podría ver todo el tráfico enviado y recibido para poder analizarlo y manipularlo de forma arbitraria, y lograr su cometido. También gracias a lo anterior y dependiendo de la configuración del servidor, podemos saber qué versión y tecnología corre el backend, y que métodos utiliza y acepta para hacer la comunicación cliente-servidor (GET, POST, DELETE, PUT, etc).

Gracias a este análisis podemos ver cómo se implementan dichos métodos y si sería posible que un atacante inyectara código de manera arbitraria para explotar algún tipo de vulnerabilidad asociada al backend (XSS, SQLi, XXE, etc).

Suponiendo que se detectó una SQL Injection, el siguiente paso para el atacante sería explotar esa vulnerabilidad para obtener la base de datos, y lograr extraer usuarios y contraseñas; luego, ver si están en texto plano o si están cifradas y necesitan ser “crackeadas” por medio de ataques de fuerza bruta sobre los hashes obtenidos.

Una vez que se tiene acceso al usuario y contraseña, bastaría con buscar el panel de administración utilizando alguna técnica o herramienta como puede ser DIRB para enumerar archivos y directorios a través de una lista predefinida de estos.

  • Información almacenada en el dispositivo

Este método es una conjunción de los dos anteriores. ¿Por qué digo esto? Porque analizando estáticamente la aplicación quizá podemos encontrar algún indicio o alguna pista de una mala implementación. Utilizando la aplicación y “navegando sus casos de uso” como si fuéramos un usuario promedio, podemos generar información de manera local que puede ser interpretada y analizada luego de su generación.

Dentro de la información almacenada en el dispositivo podemos hacer foco sobre el almacenamiento interno y externo. Para el primer caso vamos a mirar si la aplicación genera información dentro de la carpeta “Shared Prefs”, si genera alguna base de datos relacional (SQLite) dentro de “databases”, si genera algún otro tipo de carpeta o archivo dentro de su almacenamiento interno que nos sirva para obtener información, o, por último, la implementación de Content Providers que sirvan para almacenar información e interactuar con otra aplicación.

Normalmente lo que ocurre es que se almacena información sensible de los usuarios o de la aplicación dentro del almacenamiento interno y en texto plano. Esto significa que un atacante con acceso al dispositivo puede fácilmente hacerse con esa información.

Suele ser un caso bastante raro encontrar aplicaciones que cifren localmente la información generada dentro de /data/data/app/shared_prefs o dentro de /data/data/app/databases, utilizando distintas soluciones que garanticen la confidencialidad de la información.

¿Qué podría ver el atacante aprovechando esto? Información sensible de todo tipo, según he comprobado a lo largo de mis años como Security Consultant. Todo depende de qué implementó el desarrollador; puede haber claves "hardcodeadas” que utilizará la aplicación (una API) o el usuario, un nombre de usuario, email, UID, contraseña, archivos de configuración, etc.

Lecciones aprendidas: ¿todo esto en manos de un atacante?

Potencialmente sí, a todo esto podría acceder un atacante, y lo único que tuvo que hacer es obtener una aplicación y utilizarla como un usuario legítimo.

Como lecciones generales, podemos concluir que una aplicación diseñada con bajos niveles de seguridad puede derivar en la exposición de:

  • Información sensible sobre la aplicación, el usuario o la organización.
  • En ocasiones, información sobre redes y segmentos internos/externos.
  • Información hardcodeada dentro del código de la aplicación o de los directorios internos de la misma.
  • Usuarios y contraseñas de protocolos como FTP, SFTP, HTTP/S, etc.

En suma, puede verse afectada la confidencialidad, disponibilidad e integridad de la información.

Todo esto sin contar, además, el daño a la imagen de una empresa si expone a sus clientes por una mala configuración de sus productos móviles, así como la posibilidad de que estafadores hagan fraude por medio de la app lícita.

Como vimos a través de los distintos métodos, para un atacante podría ser posible explotar una aplicación móvil tanto desde el lado del cliente como desde el lado del servidor, por lo que es imperioso considerar una serie de medidas de seguridad que abarquen distintos frentes.

¿Cuáles serían las recomendaciones para los desarrolladores?

La idea sería agregar protección en “capas” o la mayor cantidad de protecciones binarias sobre nuestra aplicación, tanto del lado del cliente como del lado del servidor, para complementarlas.

Algunas de las implementaciones que podríamos realizar del lado del cliente son:

  • Lexical Obfuscation

Consiste en ofuscar el código de la aplicación antes de subirla a Google Play Store. Básicamente se renombran clases, métodos, variables, nombres de parámetros, etc., con el único fin de dificultar el análisis de un tercero por medio de la decompilación de la aplicación. Se generan permutaciones en el código, pero que no modifican el comportamiento de la aplicación.

  • Anti-Tampering

Se valida si dentro del código existe algún tipo de modificación sobre la aplicación en tiempo de ejecución y, en tal caso, finaliza su ejecución.

  • Anti-Debugging

Se controla si a la aplicación se la puede analizar con un “debugger”, ya sea porque lo declara por defecto o puede modificarse la misma para realizar un “attach” al proceso específico de la aplicación y de esta forma analizarla.

Con lo cual, si se implementa a nivel de código esta validación, en ocasiones se puede evitar la utilización de “debuggers” sobre nuestra aplicación.

  • Anti-Emulation

Consiste en analizar si la aplicación puede ser ejecutada en un entorno virtualizado, con lo cual se puede restringir el uso de emuladores para el análisis de nuestra aplicación validando IMEI, numero de teléfono, strings particulares de emulación, etc.

  • Validación de root

Consiste en analizar si la aplicación está instalada en un dispositivo con privilegios de root. Se la debería analizar en tiempo de ejecución y avisar al usuario final o cortar su ejecución.

  • Cifrado de SQLite y Shared Preferences

Es importante cifrar la información almacenada internamente, en particular toda aquella dentro de SQLite y dentro de los archivos .XML de tipo "clave:valor" de la carpeta Shared Prefs.

  • Otras validaciones

Se debería validar también la CA que se implementa en la aplicación para realizar una comunicación segura, el Hostname Verifier, si se utiliza e implementa Certificate Pinning, si se evita de alguna forma el uso de herramientas como “apktool” o la instrumentación dinámica utilizando Frida, por ejemplo.

La implementación debería estar basada en el funcionamiento de las anteriores analizando componentes de las mismas y viendo si en algún momento estas son utilizadas contra nuestra aplicación.

Conclusión

Está claro que son muchos los riesgos que evitamos apostando por un desarrollo seguro. Estas son algunas de las implementaciones que podemos hacer desde el lado del cliente, aunque existen muchas más, pero sin duda estas ayudarán a que nuestra aplicación sea mas segura.

Por último, es fundamental implementar también validaciones que acompañen a las anteriores del lado del servidor.

Se puede obtener bastante información y referencia en el proyecto en desarrollo de Mobile Security Testing Guide de OWASP.

Juan Urbano Stordeur
ESET Security Consultant
@juanurss