Cómo hackearon una aplicación para obtener un corte de cabello gratis
Las aplicaciones de Android se componen principalmente de un grupo de binarios agrupados, creados a partir de código compilado de Kotlin o Java.
El código fuente original se puede recompilar fácilmente mediante varias herramientas, que se encuentran con una simple búsqueda de Google.
Si eres un desarrollador de Android y no tienes el cuidado para aplicar las mejores prácticas de seguridad, no estarás contento por mucho tiempo. Todos los secretos preciosos que crees que están ocultos de forma segura en tu código, pueden y serán utilizados contra ti. Los puede utilizar alguien con las motivaciones adecuadas. Eso es precisamente lo que le sucedió a la aplicación de Android de esta cadena de barberías.
El desprecio de los desarrolladores de todo tipo de mejores prácticas de seguridad resultó en algo sencillo. En solo un par de horas, pude descompilar, aplicar ingeniería inversa e identificar varias vulnerabilidades que cualquiera puede explotar para lograr sus objetivos. En este caso el objetivo era obtener un corte de cabello gratis.
Aquí, te guiaré a través de todo el proceso de hacking y te mostraré lo importante que es seguir las mejores prácticas de seguridad. También mostraré cómo las decisiones simples pueden aumentar en gran medida la dificultad para la ingeniería inversa y la explotación de tu aplicación de Android.
La aplicación de esta cadena de peluquerías tiene un propósito muy simple. Los usuarios pueden reservar cortes de cabello en las tiendas disponibles y por cada 10 cortes de cabello, el usuario obtiene uno gratis
¿Cuál es la novedad?
Hasta ahora todo bien, todos parecen tareas bastante buenas para una aplicación. Sin embargo, después de instalar la aplicación y ver mi página de perfil, hubo un mensaje muy explícito y de alguna manera intrigante. Los obsequios solo podían generarse a través de la aplicación, no por cualquier otra plataforma. También podían usarse cuando el usuario lo deseara: solo necesita presentar la aplicación cuando fuese el tiempo de pagar.
Este mensaje definitivamente me llamó la atención.
Así que descargué y descompilé el apk para satisfacer mi curiosidad.
Tal vez si mirara el código, podría, hipotéticamente, usar la aplicación de alguna manera creativa.
Y tenía razón: lo que encontré fue un completo desastre.
Después de descompilar la aplicación y aplicar ingeniería inversa al código, identifiqué varias vulnerabilidades. Más adelante, iré una por una y discutiré lo que el desarrollador original podría haber hecho para evitarlo:
- Sin ofuscación de código, por lo que es muy fácil realizar ingeniería inversa
- Sin certificado ni ningún tipo de comprobación de integridad de la aplicación. Esto hace que sea imposible para el backend detectar aplicaciones de clientes alteradas
- Lógica empresarial importante implementada en la aplicación cliente sin más controles de confirmación en el lado del backend
- Secretos codificados en el código que hacen que la API privada del back-end sea expuesta y fácilmente explotable
El archivo APK
Las fuentes de Kotlin y Java se compilan primero para archivos .class, que consisten en instrucciones bytecode. Tradicionalmente, estas instrucciones de bytecode se ejecutarían directamente en la JVM. Sin embargo, las aplicaciones de Android se ejecutan en el Android Runtime, que utiliza instrucciones incompatibles. Por lo tanto, se requiere un adicional Dexing, donde los archivos .class se convierten en un solo archivo .dex
Aquí está el proceso de compilación:
Descompilar el APK
Hay varias herramientas en línea que descompilan apk´ss y convertir el archivo .dex en Kotlin legible o código Java. El código descompilado resultante es el punto de entrada para comprender la funcionalidad de la aplicación de destino. Empero, es probable que no sea un código 100% utilizable. En otras palabras, puedes leer la fuente, pero en realidad no puedes modificar, recompilar y construir la aplicación original con ella.
Los pasos de descompilación de apk son:
Sin embargo, como estaba realmente motivado a espiar el código, usé apktool para descompilar el apk junto con el contenido .dex del archivo.
$ apktool d target_app.apk
Como dije anteriormente, el archivo .dex es el formato que la plataforma realmente entiende. Sin embargo, no es fácil leer o modificar el código binario, por ello existen algunas otras herramientas para convertirlo a una representación legible por humanos.
El formato legible para humanos más común es un lenguaje ensamblador conocido como Smali. Este es el formato de salidas de apktool y se almacena en la carpeta /smali.
Esto es lo que se ve en un archivo smali:
Los archivos smali son los que editaremos más adelante, pero no son los que nos gustaría ver para realizar ingeniería inversa en toda la aplicación. No, a menos que nos veamos obligados a hacerlo.
Código más amigable
Para ver un código más amigable que podría ayudarme en el proceso de ingeniería inversa, convertí estos archivos pequeños en estándar archivos .class. para ello usé dex2jar (paso 3 anterior):
$ dex2jar decompiled_app/build/apk/classes.dex
¡booom! Ahora tengo acceso al código fuente completo de la aplicación en un formato muy familiar, como lo escribió el desarrollador original.
Ahora, echemos un vistazo a lo que encontré.
Vulnerabilidad 1: ¡No hay ofuscación de código!
La ofuscación y la minificación son estrategias utilizadas para reducir el código no utilizado. También para acortar los nombres de las clases y los miembros de su aplicación, generalmente reemplazándolos por algo sin sentido. Esto no solo hace que sea muy difícil de leer, sino que también reduce aún más el tamaño de tu aplicación.
Si no aplicas ningún tipo de ofuscación de código, le estás dando acceso a tu repositorio privado al hacker.
En este caso particular, fue sencillo entender y seguir el código, identificar tecnologías de terceros, pero también los secretos utilizados para autenticarlas.
Todo esto podría evitarse fácilmente mediante el uso de algún tipo de herramienta de ofuscación, comoProGuard. Eso al menos haría que los hackers tuvieran una vida mucho más difícil. Los obligaría a invertir un esfuerzo mucho más significativo para aplicar ingeniería inversa al código.
Así es como se ve el código antes y después de aplicar la ofuscación:
Vulnerabilidad 2: secretos codificados
La primera clase que observé tenía literalmente una variable codificada llamada CLIENT_SECRET, que luego se usaría para autenticar cada llamada Http al backend.
Y de repente, ahora tengo acceso a la API privada de toda la aplicación para buscar nuevos vectores de ataque.
Si realmente necesitas almacenar secretos en tus aplicaciones cliente para proporcionar algún tipo de autenticación, esto puede evitarse de muchas maneras sin comprometer estos secretos al codificarlos en tu código.
Existen varios métodos conocidos, como el uso de estrategias de derivación de claves simétricas o funciones de derivación de claves basadas en contraseña.
Vulnerabilidad 3: lógica empresarial importante en la aplicación cliente sin verificaciones de confirmación en el backend
Aquí es donde las cosas se ponen interesantes. Continuando mi búsqueda para obtener el mejor corte de pelo, por el que no tengo que pagar, todavía tenía que encontrar alguna vulnerabilidad. Esta debía permitirme que la aplicación me generara una reserva de corte de pelo gratis.
Después de pasar aproximadamente una hora mirando el código, esto es lo que aprendí:
- El backend almacena toda la información Shops y User
- El backend almacena el Bookings para cada usuario
- La aplicación cliente recupera todos los Bookings para cada usuario a través de llamadas REST
- Cada llamada REST al backend se identifica mediante un código rígido client_secret y específico del usuario basic_auth creds
Y ahora, la cereza del pastel:
- La aplicación cliente determina, no el backend, cuándo es el momento de otorgar un freebie, contando cuántas reservas exitosas el User ha hecho
- Los freebie era solo un booking estándar, una REST con un parámetro adicional indicando que la reserva es para un free haircut (corte gratis)
En este punto, puedes adivinar cuál fue la respuesta del backend cuando emití de inmediato POST a la API de reserva con el parámetro establecido en true:
> 200 OK!
Después de ver tantos errores aquí, confieso que no me sorprendió.
Consecuencias
Lo que podemos aprender de esto es que al permitir que tu aplicación cliente tome decisiones tan importantes abres una enorme brecha. Por ejemplo, al momento de otorgar una reserva gratuita, literalmente estás colocando tus valiosas reglas comerciales en el extremo más vulnerable de todo el entorno.
Peor aún, es no validar las llamadas REST en el back-end cuando todavía tienes la oportunidad. Obviamente, sería obligatorio validar si un usuario ha realizado y pagado suficientes reservas para obtener una gratis. Esto antes de aceptar y almacenar en el backend cualquier cosa entrante desde la aplicación del cliente.
Y esto me lleva a la última pero no menos importante vulnerabilidad:
Vulnerabilidad 4: no hay comprobaciones de integridad de la aplicación cliente
No debes programar decisiones importantes de lógica de negocios en la aplicación cliente, es totalmente incorrecto. Debes hacerlo si al menos puedes tener algún mecanismo para verificar la autenticidad de la aplicación.
Muchas de las vulnerabilidades anteriores podrían haberse mitigado con algún tipo de verificación de integridad de la aplicación del cliente. Esto hace posible que el servidor de back-end verifique cuándo las llamadas de la API provienen de aplicaciones legítimas; descarta las entrantes de aplicaciones comprometidas. Esto se puede lograr fácilmente firmando las solicitudes con el certificado de lanzamiento de la aplicación.
Cada apk de Android está firmado con un certificado de lanzamiento. Si el apk se descompila y se vuelve a compilar con cambios de código, mediante algún proceso similar al que describo aquí todo cambia. El nuevo apk compilado debe firmarse con un certificado diferente. El hacker no tendrá acceso al apk original utilizado para firmar la aplicación legítima que solo el desarrollador original tendría.
Alternativamente, Google mismo proporciona servicios de Licencia de aplicaciones. Esto para ayudarte a validar si la aplicación actual se instaló desde Play Store o desde alguna otra fuente desconocida (el hacker).
Cambiar los archivos de ensamblador de Smali y recompilar el APK
Entonces, ahora que estamos contentos con nuestro hacking y sabemos qué y dónde debemos colocar nuestro código malicioso, es hora de editar algunos archivos de ensamblaje y recompilar todo. ¿Recuerda smali?
Las salidas de apktool son archivos .smali por cada .class por lo tanto, sería fácil, al menos, navegar e identificar dónde deberían hacerse los cambios. Sin embargo, de ahora en adelante, estás solo: si no estás familiarizado con algún tipo de lenguaje ensamblador, este puede ser un proceso bastante doloroso.
Me alegro de no tener que cambiar demasiado para que mis solicitudes de reserva manipuladas funcionen. Solo agregar un par de parámetros y quitar un par de :gotos resultó ser suficiente
Cuando terminé de editar, simplemente usé apktool nuevamente para recompilar todo:
$ apktool b -f decompiled_app -o hacked_app.apk
Generé un par de claves aleatorias para firmar la aplicación recompilada de esta manera:
$ keytool -genkey -v -keystore some_ks.keystore -alias some_alias -keyalg RSA -keysize 2048 -validity 1000
Firmé la aplicación recompilada, utilicé jarsigner.
$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore some_ks.keystore hacked_app.apk some_alias
¡Y eso es todo
Ahora podría sentarme e instalar mi aplicación manipulada, llamar a la API privada de la peluquería con mis solicitudes manipuladas, y finalmente, ¡podría reservar cortes de cabello gratis!
En conclusión
Este fue un intento de mostrar cuán fácil es robar tus secretos y cuán vulnerable es tu código en general si no tomas las precauciones necesarias. Debes tomar precauciones para preservar tanto tu propiedad intelectual como tu lógica comercial. Varias herramientas de explotación y conocimiento general facilitan el proceso para poder vulnerar una aplicación fácilmente.
Así que será mejor que comiences a invertir algo de tiempo aprendiendo las mejores prácticas de lo contrario, simplemente estás dejando la puerta abierta cualquier hacker.