El pasado 17 de junio publicamos el desafío ESET #40 en el que los participantes debían analizar un archivo APK para descubrir un mensaje oculto. En esta oportunidad, además de presentar la solución al desafío, anunciamos quiénes fueron los dos participantes que pudieron resolverlo a tiempo y que ganaron una licencia de ESET Mobile Security para Android:
- Everth Gallegos Puma
- Rextco
- Miguel Baez
Los ganadores deberán comunicarse por mensaje privado a través de alguna de nuestras redes sociales, como Facebook, Twitter o Instagram, indicando su nombre y dirección de correo para que podamos hacer entrega del premio. Desde ya, agradecemos a todos los que participaron y dedicaron parte de su tiempo intentando resolver el desafío. A continuación, presentamos la solución al reto.
Resolución del desafío #40 de ESET
El desafío puede resolverse aplicando solamente herramientas de análisis estático. Sin embargo, para algunas tareas resulta más rápido ejecutar el código, por lo que utilizaré Genymotion para mostrar el comportamiento de la app en cada etapa del desafío. Tras instalar la aplicación en el entorno de virtualización, se iniciará una actividad que nos revela la presencia de código antianálisis.
Deberemos ver el código para entender qué tipo de comprobaciones está realizando. Para ello, abrimos el APK con jadx. Inmediatamente encontraremos la clase Login, que según el AndroidManifest es la que se llama al momento de inicializar la aplicación. El método onCreate() llama a los métodos de la clase f: mientras que chk (Context context) intenta detectar un emulador comparando algunas propiedades del sistema contra las strings ofuscadas de la clase d, chk() chequea si existe algún depurador adherido al proceso.
En este punto, podemos utilizar un módulo anti-antiemulación con alguna herramienta como Xposed, o podemos editar el código del APK para bypassear la validación. Dado que la segunda también resuelve el problema de la depuración, modificaremos los métodos de la clase f para que siempre devuelvan false.
Para principiantes, una herramienta intuitiva para editar código smali es APKStudio, que reemplaza con un botón la seguidilla de comandos para firmar el nuevo APK. Tiempo atrás escribí un post sobre LazyDroid, un script para “analistas perezosos” que intenta automatizar tareas frecuentes como esta. En la misma línea, disponemos de APK Multi-tool que cumple el mismo propósito. No estoy segura de que todas estas herramientas estén actualizadas a la fecha porque personalmente prefiero utilizar simplemente apktool con estos comandos:
java -jar apktool_2.4.0.jar d -f -o <ruta-carpeta-output> <ruta-apk-original>
java -jar apktool_2.4.0.jar b -o <ruta-apk-recompilado> <ruta-carpeta-output>
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore <ruta-keystore> <ruta-apk-recompilado> <alias-name>
El código final queda como se ve en la siguiente imagen.
Volvemos a correr la aplicación y ahora vemos un cuadro de texto donde deberemos ingresar una clave.
Al analizar el código de Login, vemos que el método checkPassword compara el SHA-256 de esa contraseña con “f99503075f30f6b162b7e979c154131c9c9e4bbec3368cb519012599353f3bba” y, de ser correcto, procede a utilizar esa clave para descifrar algo más. Antes de calcular el hash, se comprueba que la clave tenga 6 caracteres y esté compuesta por una combinación de números y caracteres en minúscula.
En estos casos, lo primero que podríamos hacer es intentar romper el hash en línea con algún sitio como CrackStation, pero vemos que no se encuentra disponible.
Procedemos a bruteforcear la contraseña con un script en Python similar al que vemos a continuación. Tras un tiempo de procesamiento deberíamos dar con la clave que es “c3rs31”.
Al ingresar la clave y presionar el botón “Start!” nos aparece una notificación: “¡Contraseña correcta!”; pero, ¿qué más ocurre? La aplicación llama al método c.chkwb pasándole por parámetro un texto descifrado que es la URL del post sobre interceptación de tráfico. Esto nos insinúa que quizás debamos configurar un proxy para resolver la siguiente etapa.
El método chkwb utiliza Jsoup para encontrar una etiqueta HTML “got”, que no se encuentra en el sitio web original. Luego, comprueba que el contenido de ese elemento sea un texto con 11 secciones separadas por “-”, donde el hash de cada sección debe coincidir con los hashes almacenados en el archivo strings.xml.
Nueve de esos hashes son MD5, por lo que podemos encontrarlos en línea. Para ello volvemos a CrackStation y nos daremos con el siguiente resultado.
Entonces, el componente HTML debiese tener el formato <got>what_do_we_say_to_the_god_of_death?_ALGO_ALGO</got>. Quienes estén al día con Game of Thrones de seguro reconocerán la respuesta a esa pregunta. Para los que no, incluí una imagen al final del post que peticiona la app. Entonces, “not” y “today” son las secciones que nos faltan para completar el texto, cosa que podemos corroborar calculando si el SHA-256 de cada palabra es igual al que se encuentra almacenado en strings.xml.
El método chkwb tiene otra pequeña trampita: llama a isWalkingWithShame para comprobar que, tras haberte descubierto analizando la aplicación, estés cumpliendo tu penitencia en la peregrinación de la vergüenza en King’s Landing. Deberemos configurar las coordenadas GPS del emulador para que sean las correctas, o editar nuevamente el código smali.
Ahora deberemos agregar la etiqueta en el HTML interceptando el tráfico de la aplicación.
Vemos que el código toma la string “nottoday” y la pasa como parámetro al método viserion que se encuentra en las librerías con código JNI. Una alternativa al proxy es recompilar el APK para que llame a viserion con una cadena de texto hardcodeada, o llamar al método con alguna herramienta como Frida.
Si todo se ejecutó correctamente, veremos en el logcat que un mensaje ha sido impreso: “El flag está en memoria”.
Ahora podemos usar alguna herramienta como Fridump para extraer el flag, que es desafio_eset_Dr4cArY5.
Alternativamente, podríamos simplemente analizar con un desensamblador como radare2, Ghidra, IDA o incluso este servicio en línea, qué instrucciones está realizando la librería native-lib. Veremos que calcula el flag operando sobre los valores ordinales de los caracteres en “nottoday”, o bien, realizando un xor con caracteres hardcodeados.
¡Hasta aquí llega el desafío! Un tanto largo, pero sencillo. Espero que les haya parecido entretenido y hasta la próxima. Valar morghulis.