viernes, 24 de octubre de 2014

Análisis de un phishing

El método de propagación de los phishings, en su gran mayoría y casi por definición, sigue siendo el mismo desde hace bastantes años, a través de email. Hace unos días recibimos uno del Banco Santander Chile en el correo del laboratorio. El email, en su conjunto, estaba bien construido, con un lenguaje correcto y sencillo, que nos indicaba que nuestra "Clave 3.0" estaba bloqueada porque aún no se había verificado y nos invitaba a entrar para verificar los datos a través de un enlace.

El phishing que nos mostraba al pulsar en el enlace del correo se encontraba en un sitio vulnerado, cuyo tiempo de carga era un poco largo, por lo tanto esto nos hizo sospechar que el contenido no estaba alojado realmente en el mismo servidor, o que algo raro sucedía, como explicaremos más adelante.

A través de una escalada de directorios, nos encontramos que el servidor, por defecto, listaba el contenido de sus carpetas, y además, nos encontramos un archivo zip llamado "www.santander.cl.zip". Los atacantes suelen subir el kit de phishing comprimido a la página vulnerada y posteriormente extraerlo para tener el phishing listo. En este caso, olvidaron borrar el archivo zip después de extraerlo.

Teniendo ya el zip en nuestro poder nos permite analizarlo al detalle. Este tipo de oportunidades no se deben dejar pasar, pues suponen una valiosa fuente de información para nuestro trabajo de antifraude en el laboratorio.

Al descargarlo y descomprimirlo, observamos que como suponíamos, eran todos los archivos del phishing, así que procedimos a reproducirlo en local para estudiar su funcionamiento, que por su interés mostramos a continuación.

Lo que ve el usuario 

En primer lugar vamos a mostrar el funcionamiento desde el punto de vista del usuario/cliente afectado y posteriormente cómo el phishing almacena y utiliza los datos que le proporcionamos.

Al acceder a la página, se carga una copia falsa de la portada principal del banco. En ella, como buenos usuarios confiados, procedemos a introducir el RUT (número de identificación nacional para los habitantes de Chile) y la clave:


En este punto el phishing hace tres comprobaciones. En primer lugar el RUT y la clave deben tener un formato válido. Además, comprueba contra la página real del banco (por supuesto, en segundo plano), que las credenciales son correctas. En caso de que el RUT y clave tengan el "formato" adecuado pero no seamos clientes del banco (o nos hayamos equivocado en alguno de ellos), nos mostrará la siguiente pantalla de error, pidiéndonos que accedamos 15 minutos después, achacando un error en sus sistemas (y además con faltas ortográficas).


En caso contrario, es decir, somos clientes y nuestro RUT y clave son correctos, pasa a una pantalla en la que solicita la tarjeta de coordenadas completa, que además plantea de manera desorganizada, para parecer más real. Hay dos pequeños detalles en esta pantalla, y es que el phishing ha conseguido ya obtener el nombre y apellido del cliente y el número identificativo de la tarjeta de coordenadas (señalados en verde).


Una vez se rellenan todos los datos, reenvía al usuario a la página real del banco para levantar menos sospechas. En caso de que no se rellenaran todas las casillas o alguna de ellas no tuviera el formato correcto (2 dígitos numéricos), avisaría con un error y se quedaría a la espera para que se corrija.



Este es el proceso "visible" para el usuario, pero vamos a estudiar el código que hay detrás de esto.

Como se realiza el trabajo "sucio"

En primer lugar, el atacante hace uso en todo momento de la librería "cURL" de PHP. Esta facilita la realización de conexión HTTP sobre otro servidor, siendo capaz de mantener las sesiones y cookies, para facilitar bastante el trabajo.

En la primera pantalla, carga el contenido de la página original del banco "http://www.santander.cl/" haciendo uso de cURL. Esto hace que el tiempo de carga aumente, que fue una de las cuestiones que nos llamó la atención.


En el primer bloque de código hace la petición a la página real del banco y descarga el contenido en la variable "$html". En el segundo bloque reemplaza las rutas relativas por rutas absolutas, para que se carguen correctamente las imágenes y demás recursos de la web (JS, CSS) y que además los enlaces sean correctos. En el último bloque modifica la dirección de envío del formulario de identificación, escribiendo una dirección que mantiene bajo su control "?STP=login".

Posteriormente, para comprobar si el RUT y la clave introducidas por el usuario son correctas, vuelve a hacer una petición con cURL sobre la dirección real "https://www.santander.cl/transa/cruce.asp". En caso afirmativo continúa haciendo sendas peticiones sobre
"https://www.santander.cl/transa/segmentos/Menu/views/sections/vsTS.asp?sectionCode=TC1" y "https://www.santander.cl/transa/mensajeria2/pivote_superclave.asp" para obtener el nombre y apellido del cliente y número identificativo de la tarjeta de coordenadas respectivamente.

Toda esta información la mantiene guardada en la sesión de PHP para posteriormente usarla en el proceso de solicitud de la tarjeta de coordenadas, como pudimos ver en la tercera imagen. Todo con objeto de darle más credibilidad al phishing.

Pasamos al momento de generar la página en la que nos solicita la tarjeta de coordenadas.


Con la función GenerateCard() genera un array asociativo con todas las coordenadas y posteriormente, con el código remarcado, extrae coordenadas pseudo-aletorias del array y genera la tabla HTML para la petición de coordenadas.

Como hemos mostrado en la imagen anterior, las solicita de manera desordenada y además tiene 2 métodos de control para asegurarse, en cierto modo, que el usuario no se equivoca al introducirlas. El primero de los métodos es que el usuario debe introducir todas las coordenadas, no puede dejar ninguna en blanco. El segundo es que cada una de las coordenadas deben ser numéricas y tener al menos 2 dígitos. LONGCOORDS es una constante que está fijada a 2 en la cabecera del archivo (no se aprecia en la imagen).


Para terminar, guarda todo esto en un fichero de logs ("Logsdb.txt"), definido en la constante DB_FILE) en el servidor y además, lo manda por correo usando el servidor SMTP configurado en el servidor vulnerado.

La manera de guardar este archivo de logs también es bastante curiosa, pues si lo guardara en texto plano, cualquiera que conociera la ruta del archivo podría abrir el archivo "Logsdb.txt" y ya tendría el "trabajo hecho". Para ello, antes de guardar la información en el registro, la cifra.


Para comenzar la función de cifrado utiliza la variable $string que son los datos robados y la clave $key, que está definida en el código.

  • La línea 5 lo único que hace es extraer cada uno de los caracteres. 
  • La linea 6 extrae un carácter de la clave siguiendo este patrón: si el bucle sólo contuviera esta línea de código y la clave fuera "1234", nos generaría como resultado la cadena "412341234...", con la misma longitud que el texto de entrada. 
  • La línea 7 es la que en realidad hace el cifrado. Realiza una SUMA del valor en decimal de ambos caracteres (el del texto plano y el correspondiente de la clave) y después lo vuelve a transformar en carácter. 
  • La línea 8 concatena todos estos caracteres en la variable "$result". 
  • Para finalizar la función, devuelve el contenido de la variable "result", pero antes lo codifica en base64, ya que los caracteres generados pueden no quedar en el rango ASCII y generar problemas al almacenarlos en disco.

El cifrado es bastante débil, con un pequeño análisis estadístico se podría extraer el texto en claro. Basa la fortaleza del cifrado en lo que se conoce como "seguridad por oscuridad", es decir, se supone que nadie conoce el método excepto él.

Por comodidad, el atacante ha creado la página "index.php?STP=logers" en la que introduciendo un usuario y contraseña, que tiene previamente definidos en el código, se muestra todo el contenido de los logs en texto plano. Para ello utiliza el siguiente código, junto con una captura de los logs (obviamente no son datos reales):


En ella comprueba si el usuario ha introducido el usuario y la contraseña definidas en el código, si es así, muestra el contenido de todos los logs que tiene en el archivo, no sin antes descifrarlos con el método "Desofuscar()". Si no se introduce el usuario y la contraseña o son incorrectos, devuelve un formulario para hacer login.

Y para finalizar, vamos a explicar cómo funciona el método "Desofuscar()", aunque en realidad hace lo mismo que "Ofuscar()", pero a la inversa.



  • En la línea 4 decodifica el texto que tenía almacenado en base64. 
  • En la línea 6 extrae carácter a carácter de la cadena cifrada. 
  • En la línea 7 extrae el carácter correspondiente de la clave, exactamente igual que el método de cifrado. 
  • La línea 8 es la que se encarga realmente de descifrar. Cuando se cifraba se realizaba una suma de los valores en decimal de los caracteres, pues ahora se tiene que realizar la resta del valor almacenado menos la clave. Con ello obtenemos el carácter original. 
  • En la línea 10 concatena los resultados que tenía y cuando termina la cadena completa, la devuelve.

En resumen, el flujo de trabajo del phishing se resume en la siguiente imagen.


Conclusiones

Después de este pequeño estudio podemos sacar algunas conclusiones:
El objetivo final es claro, recoger la máxima información del cliente para posteriormente poder realizar transacciones ilegítimas. Las novedades que han llamado la atención a nuestro departamento antifraude son, en primer lugar, la validación contra la página web del banco, si bien es cierto que no es el primer caso, este banco lo hace un poco más fácil para el atacante, ya que no usa sistemas como el "CSRF token" (por ejemplo), lo cuál no elimina el riesgo, pero complicaría un poco el proceso.

Por otro lado, la forma de engañar al usuario utilizando su nombre real y el número exacto de su tarjeta de coordenadas. La mayoría de phishings que tratamos en nuestro departamento antifraude a diario no se molestan en extraer tanta información para simplemente mostrarla al usuario. En algunas ocasiones simplemente, en lugar del nombre, usan algo genérico como "Cargando...".

Y para terminar, la forma de guardar la información de los usuarios (cifrada) es de lo más interesante. No es un cifrado robusto, como ya hemos dicho anteriormente, pero también es cierto que pocos "phishers" se molestan en hacer esto, simplemente lo guardan en texto plano en algún archivo con nombre extraño.

El código también tiene referencias al sistema de autenticación por SMS, pero no hace uso de él. No sabemos si es por un mal desarrollo o simplemente porque el atacante todavía no han tenido usuarios reales para probarlo. Bien es cierto, que si finalmente se implanta este método (a través de SMS), la recogida de datos del phishing sería más compleja, lo que esperamos que alerte al usuario, porque se haría la transferencia fraudulenta en el mismo momento en que el usuario está introduciendo los datos en el phishing.
  

Antonio Sánchez

jueves, 4 de septiembre de 2014

APKProtect: desempaquetador de bytecode Dalvik dentro de una libreria nativa

En nuestro departamento de antifraude hemos analizado el APK de un troyano Android para envío de SMS, en él hemos encontrado una técnica de desempaquetado muy interesante. El malware usa una librería nativa integrada (libAPKProtect.so) en el APK para desempaquetar el bytecode Dalvik. Además de eso, implementa técnicas para hacer mas difícil el volcado de la memoria del proceso y extraer el bytecode Dalvik también implementa un control para terminar la ejecución en caso de que el APK haya sido reempaquetado. Entendemos que el lector está familiarizado con el entorno Android y con las herramientas para depurar las aplicaciones Android como ndk-gdb de la NDK de Android.



Al abrir el archivo "classes.dex" del APK que contiene el bytecode Dalvik, vemos que hay instrucciones que no fueron interpretadas porque los byte codes están cifrados. El cifrado dificulta la ingeniería inversa, evita que los sistemas automatizados inserten "hooks", etc.

Ejemplo de la funcion "onCreate" cifrada:












Recorriendo el archivo, vemos el constructor de la clase "APKMainAPP12D0C" que carga una librería llamada "APKProtect". El desempaquetador seguramente está dentro de la librería nativa. ¿Cómo? Sí, un desempaquetador de bytecode Dalvik está contenido en una librería nativa compilada para procesadores ARM. Necesitamos de depurar la librería nativa. Por eso, nos hemos inspirado en el blog "The Cobra Den" para configurar el entorno de depuración y la instalación de ndk-gdb de Google.


Cuando el malware carga la librería nativa con la función "loadLibrary", el sistema operativo llama a la funcion "On_JNILoad" de esa misma librería nativa. Depurando la librería, hemos puesto un breakpoint a esa función "On_JNILoad".

En esa función, la primera parte se encarga de  llamar a la función "ptrace" con el parámetro PTRACE_TRACEME. Evita el funcionamiento de todo tipo de depuradores como GDB al igual que tampoco funcionará cualquier tentativa de volcado de memoria.

En la segunda parte, el malware abre el archivo "/proc/self/maps" que contiene el mapeado de su memoria. Itera cada línea hasta encontrar la cadena "classes.dex", es decir la zona de memoria que contiene el mapa del archivo "classes.dex". Analizando esa misma línea, el malware conoce la localización en memoria del mapa del archivo "classes.dex".

Ejemplo del mapeado del codigo Dalvik en el proceso donde vemos que el archivo “classes.dex” esta mapeado entre la dirección 0x47c46000 y 0x47c53000.

47c46000-47c53000 r--p 00000000 1f:01 525 /data/dalvik-cache/data@app@google.service-1.apk@classes.dex

Luego el malware comprueba si es un DEX o un DEX optimizado (DEY) buscando la cadena "dex" o "dey". Si es un DEX, recoge 20 bytes y compara esos 20 bytes con bytes de su librería. Si no hay correspondencias, el malware lanza un hilo que termina la ejecución del proceso en un tiempo aleatorio. ¿Pero…? un momento, ¿20 bytes no es el tamaño de una firma SHA1? En la documentación de Google, vemos que cada archivo DEX tiene una firma SHA1 en su cabecera excepto los códigos optimizados (DEY). Sirve para asegurarse de la integridad del archivo, es decir que el archivo no fue modificado. Si el archivo fue modificado, vuelve a matar su proceso de la misma manera. Pensamos, que el malware comprueba eso para evitar todos los tipos de reempaquetado que consisten en desempaquetar, insertar hooks, y re-empaquetar.

Esta es la firma SHA1 del archivo "classes.dex" que hemos encontrado:
0x73 0xAD 0x50 0x7F 0x1C 0xC6 0x8A 0x4D 0x2E 0x8C 0xEE 0xF5 0xDA 0xF8 0xE7 0x7C 0x27 0x4D 0x97 0xE7

En la tercera parte, el malware recoge el bytecode Dalvik (dentro del map del archivo classes.dex), llama a la funcion mprotect para cambiar la proteccion de las paginas de memoria a escritura. Por cada parte del bytecode Dalvik cifrado, recoge cada byte (LDRB), aplica un Exclusive OR (EORS) con bytes de su libreria, y guarda el resultado (STRB) a la misma ubicacion. Cuando todas las partes estan decifradas, el malware llama de nuevo a la funcion “mprotect” para cambiar las paginas de memoria al modo lectura. Solo en modo lectura, porque es la maquina Dalvik quien va a leer los byte codes, ejecutarlos y no el procesador ARM :=).



Ahora que sabemos donde descifra el malware los bytecode, solo es necesario poner un breakpoint cuando ha terminado, es decir a la funcion "mprotect". Con el comando "dump memory /home/ubuntu/dump.bin 0x47c46000 0x47c53000" dentro de la consola de gdb, conseguimos un volcado de la memoria.  Solo falta abrir el volcado con un intérprete de bytecode Dalvik como IDA para ver el código descifrado.

La misma función "onCreate" pero descifrada: 

El desempaquetador APKProtect no fue diseñado por el autor de malware. En efecto, APKProtect es un producto que se encarga de proteger los APKs ante ingeniería inversa. Desafortunadamente, ayuda tanto a los buenos como a los malos.

Más información:

SHA256 de la muestra analizada  3aee81db24540fb6b3666a38683259fd32713187ec6e0b421da9b91bd216205f

NDK,

Debugging Apps with Native Code - part 1

Dalvik Executable Format

ApkProtect,



  
Laurent Delosières

jueves, 28 de agosto de 2014

Estudio del Algoritmo de Generación de Dominios de Murofet, una variante de Zeus

Cada vez es más frecuente que los troyanos empleen un gran número de dominios para tratar de ocultar sus acciones: desde puntos de descarga, puntos de envió de instrucciones, etc. Para el malware es importante dificultar a los investigadores la localización de los sitios desde donde operan, de forma que cuanto más diversificado esté, mejor. Para generar un gran número de dominios de forma controlada se emplean los algoritmos de generación de dominios o Domain Generation Algorithm (DGA).

Para el atacante es importante controlar los dominios que se generan cada día para poder registrarlos previamente. De ahí la importancia que tiene para los investigadores conocer y controlar de que forma funcionan estos algoritmos. El Domain Generation Algorithm (DGA) fue usado por las variantes de Zeus como Murofet. Otras familias han usado DGAs como Conficker, BankPatch, etc. En el caso del Murofet (descrito como variante de Zeus), el DGA se usa para contactar con un sistema infectado y enviar  un ejecutable destinado a robar datos bancarios.

En esta entrada, analizamos el algoritmo DGA usado por un troyano de la familia Murofet (SHA256 99370d5162c2d9e165892af3bde7c6de8c44ec5945ed0a1ddb6b827b876931d0).

Cuando el malware se lanza empieza a generar los dominios. Para ilustrar el funcionamiento, tomamos el ejemplo de 3 dominios generados "whvqwshpulytfvx.biz", "rtqispmsdmrqcn.info", y "rtqispmsdmrqcn.org". Cuando los dominios resuelven a una dirección IP, el malware hace una petición HTTP al dominio generado. Aquí, el malware recoge un ejecutable desde el último ordenador, lo comprueba y lo ejecuta. Estos dominios fueron generados el 27 de Agosto 2014, y aquí la fecha toma especial importancia.



El algoritmo DGA se compone de dos partes. La primera parte va a inicializar el algoritmo mientras que la segunda parte va a construir el dominio. La fecha sirve para inicializar el algoritmo. En otras palabras, el algoritmo genera dominios diferentes de un día para otro. Cada día, Murofet genera 800 dominios diferentes. La ventaja principal de usar este sistema es la de aumentar la dificultad de recoger las infraestructuras con un sistema automatizado, de esta forma el proceso de obtener el ejecutable puede tardar horas, días, o meses. La otra ventaja que vemos es que a priori no se puede ver que el ordenador de la victima esta infectado por un troyano bancario, ya que la parte responsable de robar los datos bancarios todavía no fue descargada.

El algoritmo DGA genera un nuevo dominio a cada iteracion llamando la funcion "get_domain". El dominio se genera en base a dos parámetros que son la semilla (seed) y el contador (counter).

def DGA ():
 seed = GetSystemTime()
        for counter in range(0x320):
             domain = get_domain(seed[0:7], counter)

La semilla ("seed") resulta de la llamada a la función API de Windows "GetSystemTime" para inicializarse. La API de Windows lo guarda en una estructura SYSTEMTIME mientras que nosotros para más comodidad, lo guardamos en una lista que  llamamos "seed". Los dos primeros índices corresponden al año (2014 =  0x07*0x100 + 0xDE), el tercero al mes, el quinto al día de la semana, el séptimo al día, y el resto a los segundos y milisegundos. En la API de Windows, enero corresponde a 1, febrero a 2, etc. El domingo corresponde a 0, lunes a 1, etc. El día depende del mes y varia entre 1 y 28, 30 o 31. Abajo es el ejemplo de un seed:

Seed = [0xDE, 0x07, wMonth, 0x00, wDayOfWeek, 0x00, wDay, 0x00, 0x0F, 0x00, 0x31,
0x00, 0x33, 0x00, 0xD1, 0x03]
En la función "get_domain", la primera parte define una lista "data_to_hash" que esta inicializada con la semilla y el contador y luego se hace un XOR con los  bytes 0xB1, 0xA4, 0xD7, etc. Finalmente, se realiza el "hash" de esta lista con el algoritmo MD5 y el "hash" resultante se usa para generar el dominio.

def get_domain(seed, counter):
    //Primera parte
    data_to_hash = [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]

    data_to_hash[0] = (seed[0] + 0x30) & 0xff
    data_to_hash[1] = seed[2] & 0xff
    data_to_hash[2] = seed[6] & 0xff
    data_to_hash[3] = 0x00
    data_to_hash[4] = counter & 0xfe
    data_to_hash[5] = (counter >> 8) & 0xff

    data_to_hash[0] = (data_to_hash[0] ^ 0xB1) & 0xff
    data_to_hash[1] = (data_to_hash[1] ^ 0xA4) & 0xff
    data_to_hash[2] = (data_to_hash[2] ^ 0xD7) & 0xff
    data_to_hash[3] = (data_to_hash[3] ^ 0xD6) & 0xff
    data_to_hash[4] = (data_to_hash[4] ^ 0xB1) & 0xff
    data_to_hash[5] = (data_to_hash[5] ^ 0xA4) & 0xff
    data_to_hash[6] = (data_to_hash[6] ^ 0xD7) & 0xff
    data_to_hash[7] = (data_to_hash[7] ^ 0xD6) & 0xff

    hash_md5 = hashlib.md5(array.array('B', data_to_hash).tostring()).hexdigest()
    bytes_array = array.array('B', hash_md5.decode("hex"))

En la segunda parte, el algoritmo itera todos los bytes del "hash" con los cuales construye el nombre del dominio. Pone el límite de 0x7a que corresponde a la ultima letra del alfabeto (z)  y añade un offset 0x 61 (letra a) para solo generar letras. La extensión del dominio esta basada sobre el valor del contador. El DGA distingue 5 casos: la extensión “.org”, “.com”, “.net”, “.info”, o “.biz”. Concatenando el nombre y la extensión, conseguimos el dominio.

//Segunda parte
    #Generate the name
    for byte in bytes_array:
        al = (byte & 0xF) + (byte>>4) + 0x61
        if al <= 0x7A:
            name = "%s%c" % (name, chr(al))

    #Generate the extension
    if (counter % 5 != 0):
        if (counter & 0x3 != 0):
            if (counter % 3 == 0):
                extension = ".org"
            else:
                if (counter & 0x1 == 0x1):
                    extension = ".com"
                else:
                    extension = ".net"
        else:
            extension = ".info"
    else:
        extension = ".biz"

    domain = "%s%s" % (name, extension)
    return domain
Ejecutando el script Python, sobre el año 2014 entero, hemos notado que un dominio (epmmxkoszqyown.org) aloja un servidor nginx/1.6.0 activo, pero no pudimos recoger ningún ejecutable. Se trata de la maquina de una victima, es decir sin instrumentación del algoritmo, que podría haberse infectado el día 14 de Agosto 2014.

Más información:

Domain Name Generator for Murofet,

ZeuS Gets More Sophisticated Using P2P Techniques,

W32/Murofet-A,

martes, 26 de agosto de 2014

Disponible el libro Hackstory.es

Hace más de un par de años, Mercè Molist (@mercemolist), su autora, inició una campaña de crowdfunding para financiar un proyecto que ya venía gestándose tiempo atrás: un lugar donde escribir la historia del underground hacker español, Hackstory.

Aquellos que nunca han pasado por allí están perdiendo una gran oportunidad para conocer o rememorar como nació y creció la escena española. Una edad dorada comparable a la de los videojuegos de los 80 e imprescindible para entender que combustible empujaba la curiosidad de algunos para saltarse los límites técnicos u otros límites de naturaleza diferente.

El libro hace un recorrido por toda la historia "antigua". Desde los comienzos en los 80, pasando por los primeros grupos, el idealismo que los unió en una gran comunidad o historias que rememoramos con un suspiro cuando nos hablan de fibra óptica y nos viene a la mente aquellos modems prehistóricos que emitían ruidos infernales y la tarifa plana que tanto se hizo rogar.

Puedes leer la obra desde la wiki del proyecto o descargar el epub. No lo dejes pasar, es nuestra historia.

miércoles, 23 de abril de 2014

Depurar malware Android con Eclipse

Sí, habéis entendido correctamente: depurar malware para Android con el IDE Eclipse. Normalmente Eclipse se usa para desarrollar aplicaciones como Android, no para depurar aplicaciones compiladas. Con el depurador/desensamblador IDA se puede ver el código Dalvik de una aplicación Android, pero no se puede depurar una aplicación a ese nivel. También existen herramientas para transformar las aplicaciones Android en jar, con dex2jar por ejemplo y luego decompilarlos. Sin embargo, la decompilación no se hace correctamente, especialmente con los bucles. Como a veces el atacante ofusca su código, la ingeniería inversa es más difícil. Entonces, necesitamos de una herramienta para depurar la aplicación ofuscada.

En un foro ruso, hemos descubierto una manera de depurar las aplicaciones Android desensamblando el código Dalvik. Usaba la herramienta apktool para desensamblar las aplicaciones Android y  el IDE Netbeans 6.8 para depurar. Esta técnica fue mejorada y adaptada para la nueva versión de Eclipse. En esta entrada del blog, me gustaría compartir ésta técnica, que consideramos merece la pena.

Como ejemplo, usaremos una aplicación que hemos desarrollado llamada SilentSMS y que tiene el nombre de paquete "com.example.silentsms". Admitimos que el usuario tiene la configuración para desarrollar aplicaciones Android con Eclipse, es decir el emulador Android, DDMS para depurar, etc.

El primer paso consiste en desensamblar el APK en código Dalvik para modificar la aplicación y hacerla depurable. Para ello, usamos la herramienta Apktool 2.0 beta. Después del desensamblado, localizamos el archivo "AndroidManifest.xml" que nos permite saber cual es la primera parte del programa interpretada y también nos permite hacer la aplicación depurable. Para ello, añadimos la bandera android:debuggable a "true" a la etiqueta "application". También, nos permite saber que la primera parte del código interpretado se encuentra en la clase "com/example/silentsms/MainActivity".










Sin embargo, haciendo la aplicación depurable no permite a la aplicación esperar a un depurador. Es decir, al ejecutar la aplicación, no podríamos depurar al comienzo. Para forzar a que la aplicación espere al depurador, añadimos la instrucción "invoke-static {}, Landroid/os/Debug;->waitForDebugger()V". Es importante no olvidar el "a = 0;//" delante de ésta instrucción.









¿Por qué añadir un código delante? La razón es que cuando vamos a importar el proyecto desensamblado en Eclipse, no podríamos depurarlo por culpa de los errores de sintaxis generados por Eclipse. Es decir, Eclipse no sabe analizar sintácticamente el código Dalvik. Para evitar esto, comentamos el código Dalvik y ponemos una instrucción. En nuestro caso, incluimos la variable "a" puesta a 0 en cada línea. En otras palabras, Eclipse verá solo las instrucciones "a = 0", mientras que nosotros veremos el código Dalvik correspondiente a esta línea. De manera más macroscópica, la clase se asemeja a:

package com.example.silentsms; class MainActivity {void a() { int a;
a = 0; //  
.
.
.
}}

En otras palabras, el código Java es correcto y no genera ningún error.

El segundo paso consiste en ensamblar de nuevo la aplicación y firmarla. Para firmar la aplicación, necesitamos una llave. Usaremos la llave del Android SDK que se encuentra en la carpeta ".android" de la carpeta home del usuario en Linux. Para ensamblar de nuevo la aplicación usamos:

java -jar apktool _2.0.0b7.jar b -d out -o debug.apk

Mientras que para firmar la aplicación empleamos:

jarsigner -verbose -digestalg SHA1 -sigalg MD5withRSA -keystore 

debug.keystore debug.apk androiddebugkey

El tercer paso consiste en importar el proyecto desensamblado y depurarlo en Eclipse. Para eso, creamos un nuevo proyecto Java e importamos el proyecto desensamblado. Después ejecutamos la aplicación Android en el emulador. Cambiando la vista en DDMS, podemos ver la lista de procesos ejecutándose así como las listas de procesos esperando un depurador indicado por el icono rojo al lado. Para añadir esos procesos, podemos conectar un depurador en remoto sobre los puertos [8609-8700]. 


Para que Eclipse se conecte a nuestro proceso, necesitamos configurar el depurador. Para eso, accedemos a "Run->Debug Configurations", y luego pulsamos "Remote Java Application". Configuramos el puerto para que el depurador se conecte al proceso correcto. En nuestro caso, elegimos el puerto 8609.


Antes de pulsar el botón "Debug", necesitamos poner un "breakpoint" justo donde queremos para el depurador. En nuestro caso, queremos pararlo justo cuando llama a la función "onCreate".



Depurando el proceso,  Eclipse nos propone cambiar de perspectiva.
















Finalmente, podemos depurar la aplicación (step over, step into, etc.) como si fuera una aplicación Android desarrollada por nosotros. 




















Hemos visto como usar Eclipse para depurar aplicaciones Android a nivel de código Dalvik, lo que incluso nos permite hacer ingeniería inversa de las aplicaciones ofuscadas. Es decir: desensamblar una aplicación Android, hacerla depurable, reensamblarla, importarla en Eclipse y depurarla. Sin embargo, no podemos depurar las bibliotecas compiladas en código nativo (por procesorses ARM, Intel por ejemplo). Para eso, necesitamos usar otro depurador como IDA.

Más información:

Отладка приложений для Android без исходного кода на Java

Programering, Marzo 2014
Aptkool 2.0, 2014

Eclipse, 2014


Laurent Delosières



miércoles, 12 de marzo de 2014

Anuncios en moviles para hacer creer que estamos infectados

Dentro de las familias de malware, conocemos los "scamware" que te avisan que tu sistema esta infectado con malware y te proponen un antivirus gratuito que es en realidad un malware. Pero también se usan los anuncios en páginas legítimas con el objeto de instalarnos una aplicación maliciosa. Vamos a analizar un caso reciente que hemos encontrado para Android .

La semana pasada mirando un periódico francés muy famoso, se mostró un "popup" en mi pantalla recomendándome instalar un "cleaner" para limpiar mi móvil. La primera vez el popup estaba en inglés, tras pulsar el botón "Cancelar" se mostró el mismo popup pero esta vez en español (curiosamente no en francés).


Quizás pensaban que no entendía el ingles y que con el español tendrían más éxito. Pero no quería instalar este software. Al pulsar "Cancelar", automáticamente me redireccionó a otra pagina con una ventana que quería aparentar un mensaje de aviso de Android OS como la siguiente:


Parece que esperaba tener más éxito imitando la interfaz grafica de Android OS, para hacer creer al usuario que realmente está infectado. Al pulsar sobre cualquier parte del mensaje, redireccionaba a GooglePlay.
  

Vamos a analizar como hemos llegado a Google Play desde el periódico francés. Cuando el navegador de Android carga la pagina del periódico descarga un anuncio con la petición HTTP siguiente:

GET /ttj?
id=1028520&size=1000x300&promo_sizes=970x250,1000x90,970x90,728x90&promo_alignment=center&referrer=pagina.fr&cb=7741390030&pt1=http://ww14.smartadserver.com/call/pubj/252/82917/225/S/9665235358/origine%3Dasq? HTTP/1.1
Host: ib.adnxs.com
Referer: http://www.pagina.fr/

Vemos que recoge el anuncio del sitio web "ib.adnxs.com" para recibir el anuncio de otro servidor "http://ww14.smartadserver.com/call/pubj/252/82917/225/S/9665235358/origine%3Dasq?". El servidor responde con un código JavaScript ofuscado:

document.write('<a id=\"banner\" target=\"_blank\" href=\"http://fra1.ib.adnxs.com/click?Z2ZmZmZm2j8TYcPTK-XYPwAAAAAAAPA_E2HD0yvl2D9mZmZmZmbaP0LmHtvClCBBSI_mp8QbEgiuixlTAAAAAKixDwBIBgAAmQcAAAIAAAA9L8UA-8YCAAAAAQBVU0QARVVSANgCWgDVjlUFyYwAAgUCAQUAAIoAayUAdgAAAAA./cnd=%21aQaHPQin5MQBEL3elAYY-40LIAA./referrer=pagina.fr/clickenc=http%3A%2F%2Fsofcotrk.com%2Fmt%2Fv2b4x234d4t233s244u2u2%2F%26subid1%3Dfra1CMiemr_K-IaJCBACGMLM-9itmKWQQSINNjIuMTUuMjMwLjE2NCgBMK6X5pgF\"><img src=\"http://cdn.adnxs.com/p/b7/ed/ba/90/b7edba903816d39881e9db7acabab42b.jpeg\" border=\"0\" /></a>\r\n<scr'+'ipt type=\"text/javascript\">eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!\'\'.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return\'\\\\w+\'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp(\'\\\\b\'+e(c)+\'\\\\b\',\'g\'),k[c])}}return p}(\'5 2=[\"g\",\"f\",\"h\",\"i\"],7=j.e.c(),4=9;8(5 d=0;d<2.a;d++){3(7.b(2[d])!=-1){4=l}}3(4){3(y(\"6 u t 8 o n m 6 p!\")){q.s.r=v.x(\"k\").w}}\',35,35,\'||mobiles|if|ismobile|var|Clean|uagent|for|false|length|indexOf|toLowerCase||userAgent|ipod|iphone|ipad|android|navigator|banner|true|with|now|FREE|Master|window|location|top|Android|your|document|href|getElementById|confirm\'.split(\'|\'),0,{}));</scr'+'ipt>\r\n    ');document.write('<scr'+'ipt type="text/javascript" src="http://pixel.alephd.com/post_asq?LgDc=&L4DK=285147&rZzC=&aNVl=1&ZzpC=&Dsph=&Hsyz=440&0ZGY=12922685&Xyes=&iK5S=&b6lE=0.1385&-tgi=-1&wziz=1&vKcZ=4692914376340858434&2jSB=0.4125&-Zu8=8613&e9iH=581557833328660296&b5ho=class_1&GAa8=1028520&DH5k=62.15.230.164&8rSZ=&-Z06=0.4125"></scr'+'ipt>');document.write('<scr' + 'ipt src="http://cdn.adnxs.com/ANX_async_usersync.js"></scr'+'ipt>');

Con http://jsunpack.com/, hemos descifrado la función siguiente. Dentro de la página del anuncio, hay un código JavaScript que busca la presencia de la cadena "android" en el "user-agent". El "user-agent" forma parte de las peticiones http y permite identificar el navegador del usuario (incluyendo versión y sistema operativo) para que el servidor muestre las paginas mas adecuadas para él. El anuncio se sirve de esto mismo para comprobar si el usuario visita la pagina con un dispositivo Android. En tal caso, se muestra automáticamente un "popup" con el mensaje en ingles "Clean your Android for FREE now with Clean Master!". Si el usuario está de acuerdo, se le redirecciona a Google Play.

Mostramos el código desofuscado:

href = “http://fra1.ib.adnxs.com/click?...”
var mobiles=["iphone","ipod","ipad","android"],
uagent=navigator.userAgent.toLowerCase(),
ismobile=false;
for(var d=0;d<mobiles.length;d++){
   if(uagent.indexOf(mobiles[d])!=-1)
   {
      ismobile=true
   }
}

if(ismobile)
{
   if(confirm("Clean your Android for FREE now with Clean Master!"))
   {
      window.top.location=document.getElementById("banner").href
   }
}

En caso contrario, la página web hace una nueva petición HTTP al servidor de anuncios:

GET /ttj?
id=1028475&size=300x1050&promo_sizes=300x1000,300x900,300x600,300x250&promo_alignment=center&referrer=pagina.fr&cb=7296786274&pt1=http://ww14.smartadserver.com/call/pubj/252/82917/163/S/9665235358/origine%3Dasq? HTTP/1.1
Host: ib.adnxs.com
Referer: http://www.pagina.fr/
Accept: */*
X-Requested-With: com.android.browser
User-Agent: Mozilla/5.0 (Linux; U; Android 4.2; en-us; sdk Build/JB_MR1) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

Se abre la misma ventana esta vez con el mensaje en español. Al pulsar sobre "Cancelar",  la página web hace otra petición HTTP al servidor de anuncios:

GET /ttj?
id=1029172&size=300x600&promo_sizes=300x250&promo_alignment=center&referrer=pagina.fr&cb=6542638935&pt1=http://ww14.smartadserver.com/call/pubj/252/82917/828/S/9665235358/origine%3Dasq? HTTP/1.1
Host: ib.adnxs.com
Referer: http://www.----------.fr/
Accept: */*
X-Requested-With: com.android.browser
User-Agent: Mozilla/5.0 (Linux; U; Android 4.2; en-us; sdk Build/JB_MR1) AppleWebKit/534.30

El servidor responde con el siguiente contenido:

document.write('<iframe frameborder="0" width="300" height="250" marginheight="0" marginwidth="0" target="_blank" scrolling="no" src="http://pureads.com-trckr.co/c2/300_46555/"> </iframe>');
document.write('<scr'+'ipt type="text/javascript" src="http://pixel.alephd.com/post_asq?LgDc=&L4DK=285145&rZzC=&aNVl=1&ZzpC=&Dsph=&Hsyz=440&0ZGY=11836032&Xyes=&iK5S=&b6lE=0.1385&-tgi=-1&wziz=1.236&vKcZ=4241021722314788001&2jSB=0.181413&-Zu8=8613&e9iH=581557833328660296&b5ho=class_1&GAa8=1029172&DH5k=62.15.230.164&8rSZ=&-Z06=0.181413"></scr'+'ipt>');document.write('<scr' + 'ipt src="http://cdn.adnxs.com/ANX_async_usersync.js"></scr'+'ipt>');

Abriendo un iframe, el navegador carga la pagina alojada en "http://pureads.com-trckr.co/c2/300_46555/". Tenemos otro código en JavaScript que comprueba de nuevo si el usuario usa un dispositivo móvil. En cuyo caso, le redirecciona a la pagina "http://k5lme.trackvoluum.com/2d9c041a-48b3-44d8-b601-0c8cabafd377". Que a su vez, redirecciona a "http://mobilesecurity.com-trckr.co/warning/...". Mostramos el código del iframe:

<a id="banner" target="_blank" href="http://k5lme.trackvoluum.com/2d9c041a-48b3-44d8-b601-0c8cabafd377"><img src="https://pureads.com-trckr.co/im/300s.jpg" border="0" /></a>

<script type="text/javascript">
var mobiles = ['android'];
var uagent = navigator.userAgent.toLowerCase();
var ismobile = false;
for (var d=0;d<mobiles.length;d++) {
  if (uagent.indexOf(mobiles[d]) != -1) {
    ismobile = true;
  }
}
if (ismobile) {
     window.top.location = document.getElementById('banner').href;
}

Han creado un dominio que se llama "http://mobilesecurity.com-trckr.co" para aparentar venir de una entidad de seguridad. Al pulsar sobre la imagen, redirecciona finalmente a Google Play. Es muy importante hacer notar que la pagina "http://k5lme.trackvoluum.com/2d9c041a-48b3-44d8-b601-0c8cabafd377" (el servidor de publicidad) devuelve páginas diferentes (distintos anuncios) según el momento del día.

Hemos visto un anuncio que se parece a un ataque. El problema es que los sitios web (o las aplicaciones) contratan servicios de terceros para la publicidad, pero no tienen ningún control sobre los métodos usados para ofrecer los anuncios. Lo que puede afectar a la imagen directa del sitio web, como en este caso el periódico francés. Pensamos que los atacantes pueden aprovecharse de esos terceros servicios para infectar los dispositivos móviles. Ademas con los anuncios que cambia cada x minutos, seria muy difícil de encontrar la fuente de infección.

Desde luego no es la primera vez que encontramos como una página legítima intenta infectarnos a través del servidor de anuncios. Ya en el 2005 nos encontramos como "Dilbert intentaba infectarnos" En aquella ocasión, al visitar la (muy recomendable) tira cómica diaria de Dilbert aparecía una ventana emergente donde se informaba de errores de registro o del sistema de archivos. Obviamente era falsa, pues (en aquella ocasión) aparecía el mismo aviso independientemente del sistema operativo con que se visitara.


Laurent Delosières
ldelosieres@hispasec.com