Buenos Días,
Hoy escribiré una entrada de análisis de malware en Android después de un tiempo sin escribir por aquí.
La idea era coger de nuevo soltura con las herramientas open-source que hay para el análisis de aplicaciones android.
Estuve leyendo información sobre alguna familias y me tope con Hydra (siguen apareciendo casos activos en Android (Twitter)) y me puse con ella.
Arranqué con la lectura de este blog:
Es el "padre nuestro" de la primera parte de nuestro tutorial, lo único que voy a hacer yo aquí en la primera parte es "puntualizar" algunos problemas que me he encontrado durante el análisis al seguir su tutorial.
Información de la muestra y Herramientas:
-)Muestra: 46aeb04f2f03ebe7c716fc6e58a5dea763cd9b00eb7a466d10a0744f50a7368f
-)JADX: Para poder leer el codigo Java/kotlin decompilado de la aplicación.
-)Apktool: Para hacer el dissasembler de la APK, y poder parcherla y reconstruirla.
-)Ghidra: Decompilado de librerias Nativas.
-)GEF: Debug de librerías Nativas.
-)Genymotion: Para emular la aplicación
-)Burp Suite: para capturar las comunicaciones
-)Frida: Para TODO :)
Analisis Estático
Lo primero que siempre suelo hacer es revisar el AndroidManifest.xml, este archivo nos da una gran información sobre permisos, servicios,receivers,activities y contiene información de gran valor.
Para poder echarle un vistazo a este archivo debemos de usar la aplicación apktool para extraer los componentes de la apk.
> apktool d <archivoX.apk>
> apktool d <archivoX.apk>
Posteriormente leemos el contenido del archivo AndroidManifest.xml:
Vemos que en el archivo "manifest" existen nombres de servicios, actividades y receiver que no los localizamos en el archivo .dex si lo abrimos con JADX. Esto es una gran "pista" de que la aplicación esta cargando código de manera dinámica.
Ahora hay que ver, como la aplicación está extrayendo este código que está cargando dinámicamente, si bien lo descarga de una web, o si bien lo tiene embebido dentro de la aplicación como un recurso o asset cifrado.
Como dice "Jack el destripador" vayamos por partes:
1.1) Application Entrypoint:
Comenzamos analizando esta clase, porque es lo primero que se ejecutará cuando la aplicación sea abierta, incluso antes de la actividad que este asociada a los intents: action.Main y category.Launcher
Dentro del método onCreate(), vemos que se llama al método Sctdsqres.j(), este método comprueba si la fecha es:
return new Date().getTime() >= 1553655180000L && new Date().getTime() <= 1554519180000L;
En el caso de que así sea, se lanza el método H(), que carga la librería nativa "Hoter.so", y cuando la librería esté cargada, se lanzará el siguiente método nativo fyndmmn(application obj) .
[+]HELP! He intentado crear un script en Frida para cargar la librería, intentando poner un sleep() entre que se carga en memoria la librería y se ejecuta la función nativa, para poder attachearnos con el debugger. Sin embargo, no he sido capaz y he tenido que poner el sleep() en el código smali de la aplicación (parcheandola). Si alguno sabe como hacerlo es bienvenido sea la solución en los comentarios.
1.2 )Parcheo de la aplicación:
Como se está cargando la librería (siempre y cuando se cumplan los criterios de tiempo que el malware tiene definido), para poder "debuggear" la misma necesitamos un pequeño sleep() entre que la librería este cargada en memoria y que se ejecute alguno de sus métodos.
Para que nos de tiempo a attachearnos con el debugger vamos a modificar el código smali de la aplicación añadiendo este sleep en el método H().
> nano <application_folder/smali/com/taxationtex/giristexation/qes/Sctdsqres.smali>
Original
1.2.1) Parcheado
Modificaciones incorporadas dentro del código smali:
1) .local = 2 --> Incrementamos el uso a variables locales a 2
2) const-wide/32 v0, 0xea60 --> esto modifica los valores de los registros v0 y v1 y les pone el valor 0xea60 (Por este motivo tenemos que aumentar el numero de variables locales a 2, ya que esta instrucción modifica los 2 registros).
3) invoke-static {v0, v1}, Landroid/os/SystemClock;->sleep(J)V --> Ejecutamos el sleep
3) invoke-static {v0, v1}, Landroid/os/SystemClock;->sleep(J)V --> Ejecutamos el sleep
1.2.2) Rebuild the application
> apktool b -f -d application_patched.apk
1) Generamos la clave:
> keytool -genkey -v -keystore my-release-key.keystore -alias alias_name
-keyalg RSA -keysize 2048 -validity 10000
2) Firmamos la aplicación:
>jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_application.apk alias_name
3) Alinear el apk
> ./zipalign -v 4 my_application.apk my_application-aligned.apk
*Recuerda que para llegar al método H() es necerio bypassear el control de tiempo ya sea mediante FRIDA o parcheando directamente el binario.
1.3 )Attachearnos a la libreria dinámicamente con GEF:
Necesitamos tener en nuestro emulador nuestro gdb_server para poder luego conectarnos remotamente desde nuestro terminal, ademas hay que usar el comando adb foward para indicar la comunicación entre ordenador y dispositivo móvil.
En el siguiente script se puede usar para lanzar gdb-server con el apk que queremos:
-) Modo de uso:
1) Ejecutar la aplicación
2) > ./script <packagename>
-) Utiliza el puerto de comunicación 1337 entre dispositivo y emulador
1.4 )Nos attacheamos con nuestro debugger
Para ello ejecutamos los siguientes comandos:
>gdb
> target remote:1337
Tras attachearnos a nuestro proceso lanzado con GDB server colocamos un breakpoint en el método nativo que esta siendo llamado desde el apk:
fyndmmn()
Vemos que con el comando vmmap dentro de GDB podemos extraer la dirección base (BA) de nuestra librería, esta dirección tal y como nos indica en el blog anterior, la podemos cargar en Ghidra y así nos sea mas fácil tener una idea general de nuestra aplicación y conseguir ir asociando el pseudo-código generado por GHIDRA en el proceso de debug que estamos haciendo.
Ghidra > Windows>MemoryMap>
Si queremos mejorar al pseudocódigo generado por Ghidra del método fyndmmn(), podemos añadir la información extra como que el primer parámetro que recibe esta función nativa es JNIENV *.
Vemos que en la función tenemos un valor DAT_ca7a5244 de tipo time. Si buscamos referencias a esta variable con ctrl+ shift + f podemos ver cuando ha sido utilizada:
Y vemos que hay una referencia en otra parte de la librería nativa donde el valor ha sido modificado (escrito):
#Assembly level:
Vemos que la función esta haciendo un time_check y devuelve--> TRUE or FALSE
eax = 0x37d59 + eax
ecx = 0xa3651a74
ecx = 0xa3651a74 + current_time
CMP ECX 0xd2f00
SETC al --> Salta a la dirección de al si (CF=1)
Vamos resolver la expresión matemática que se genera:
0xa3651a74 + current_time - 0xd2f00 = -1 Ejemplo
Current_time = 0xFFFFFFFF - 0xa3651a74 + 0xd2f00
Current time = 1554519179
Nos valdría o ese valor o uno menor tal y como se muestra la siguientes captura de pantalla:
Esto nos devuelve un valor del tipo Unix epoch time, al convertirlo a fecha nos encontramos el siguiente dato:
Como tenemos el debuger attacheado al proceso podemos ver en que momento nuestra librería deja de ejecutarse(trace-run) y ver así los "checks" que tiene la librería.
1st CHECK() --> Tiempo
Vemos que la salida de esta función, se comprueba con el valor FALSE
2nd CHECK() --> Localización
Dentro de la misma función, pero un poco mas abajo se comprueba que el país del operador de telefonía móvil de la tarjeta sim (getSimCountryIso) sea Turquía "tr".
Estos valores viene definidos por la ISO-3166-1.
Estos valores viene definidos por la ISO-3166-1.
Este segundo check lo podemos bypassear de 3 maneras:
-)Modificando el el salto condicional en el ensamblador, tal y como nos indican en blog de referencia.
-)Modificando el valor de nuestro Operador en nuestro emulador( De "US"(por defecto) a "tr" )
-)Usando Frida para modificar la respuesta de la llamada a getSimCountryIso()
Si queremos "parchear" la librería, podemos o recompilar directamente el apk, añadiendo la librería parcheada, o modificarla dentro del siguiente path tras instalar la aplicación.
> adb shell
> # cd /data/app/<packageName+base64>/
Podemos modificar y sustituir la librería original en la ruta indicada y así la aplicación cuando cargue la librería, cargará la modificada.
1.5) Obtener información de la librería nativa.
Para poder obtener información de nuestra librería nativa podemos ir añadiendo información a Ghidra con la información que obtenemos tanto con el "debug" de la aplicación, como usando JNItrace que nos permite identificar el uso de JNI API en librerías nativas.
Además dentro de la librería nativa hay una series de funciones que contienen bloques de direcciones que esta "XOReadas", el valor lo podemos sacar colocando un breakpoint en strlen() y mostrando la zona de memoria después de ser descifrada:
Podemos modificar y sustituir la librería original en la ruta indicada y así la aplicación cuando cargue la librería, cargará la modificada.
1.5) Obtener información de la librería nativa.
Para poder obtener información de nuestra librería nativa podemos ir añadiendo información a Ghidra con la información que obtenemos tanto con el "debug" de la aplicación, como usando JNItrace que nos permite identificar el uso de JNI API en librerías nativas.
Además dentro de la librería nativa hay una series de funciones que contienen bloques de direcciones que esta "XOReadas", el valor lo podemos sacar colocando un breakpoint en strlen() y mostrando la zona de memoria después de ser descifrada:
Como este tipo de cifrado de regiones de memoria se repite a lo largo de la aplicación, hemos adaptado el script de Ghidra mencionado en el blog para hacerlo en python3 y rescribir las funciones con el valor que sea extraído de "XORear" esas posiciones de memoria.
1.6) Extracción del archivo .dex malicioso.
En esta ultima parte vamos a mencionar como el malware esta guardando la información del archivo .dex malicioso dentro de uno de los assets de la aplicación a través de la técnica de AngeCryption
Donde la parte maliciosa se suele colocar después de los 7 primeros bytes (cabecera png), de todas formas en el link que he pasado esta explicado super detallado.
Estructura del asset malicioso:
Para obtener el contenido del .dex malicioso podemos irnos a la función donde se está extrayendo en memoria y hacer un "dump" de la misma.
Otra opción, es utilizar el script en python para dumpear el archivo .dex de asset file que lo contiene.
[+] En este caso la única duda que tengo yo es como sabía a la hora de crear el script que el archivo .dex, tenía ese tamaño
> dex_size = im_y*im_x/2-255
En mi caso, ese valor yo lo saqué a través de debuggear la aplicación, y comprobé que el resultado era el mismo. Al igual que antes, si alguien me puede comentar el motivo es bienvenido,
1.6) Ejecutar con Frida la aplicación y saltarnos los checks.
Para ello lo podemos utilizar el siguiente script, y ejecutar el siguiente comando:
Para ello lo podemos utilizar el siguiente script, y ejecutar el siguiente comando:
> frida -U -l Hydra_bypass_checks.js -f <packageName> --no-pause
Con todo esto podremos sacar el .dex ofuscado, que contiene la parte maliciosa de la aplicación, en la siguiente entrada, usaré una muestra mas actual ( ya la tengo analizada) y ahondaremos un poco mas en detalle de la funcionalidad de esta familia de malware.
Mil gracias a @0xabc0 por su blog y su soporte.
Un saludo!
Mil gracias a @0xabc0 por su blog y su soporte.
Un saludo!
Comentarios
Publicar un comentario