martes, 23 de junio de 2015

Automatización segura de sistemas con Ansible Vault

Ansible es una herramienta de automatización y administración de configuraciones remotas desarrollada en Python. Es un sistema “clientless” que hace uso de SSH para realizar las conexiones a las máquinas administradas. Las acciones a realizar en los sistemas se organizan por acciones o “task” que a su vez se agrupan en “playbooks”, escritos usando una sintaxis amigable basada en YAML. A diferencia de otros sistemas que se conectan al servidor central para buscar actualizaciones en su configuración (“pull-based”), con Ansible el administrador elige cuando se realizan cambios en los servidores. Hace unos meses empezamos a utilizar Ansible como estándar para manejar despliegues y configuraciones en nuestro departamento de sistemas.

Utilizando Ansible como herramienta se planteaba un problema al lanzar playbooks contra varios servidores que requerían usar sudo para elevar privilegios. Por defecto, Ansible considera un solo usuario y contraseña en común para todo el conjunto de servidores objetivo de un playbook. Esto significa que todos los servidores deben tener un mismo usuario con permisos para ejecutar sudo y que además tuviera la misma contraseña en todos los servidores.

Frente a esto, existen alternativas como el uso de la directiva “NOPASSWD” de configuración de sudo o importar en cada momento la contraseña de sudo desde un fichero en texto plano mediante un motor de templates llamado jinja. Ambas evitan la necesidad de una contraseña común, pero implican otros problemas.

A partir de su versión 1.5, Ansible añade una herramienta muy útil que abre nuevas posibilidades a la hora de gestionar datos sensibles, Ansible Vault. Esta nueva característica permite almacenar en ficheros cifrados variables sensibles necesarias para la ejecución de un playbook. Valiéndose de Vault, el motor de templates jinja y claves compartidas SSH es posible mitigar de manera considerable los problemas de seguridad relacionados con administración de contraseñas.

Nuestra aproximación consiste en utilizar un fichero cifrado usando Vault para cada servidor, creando una especie de gestor de contraseñas al que Ansible accederá cuando requiera hacer sudo.

Crear los ficheros cifrados

Vault no es más que una herramienta que cifra ficheros. Estos pueden contener, entre otras variables privadas, la contraseña de sudo de un servidor. Para crear nuestro “gestor de contraseñas” necesitaremos un fichero por cada uno de los servidores en nuestro inventario.

Bucle en bash para crear los ficheros cifrados con Vault

La variable que definimos en cada uno de los ficheros es “ansible_sudo_pass”, el equivalente a la opción “--ask-sudo-pass” del comando “ansible-playbook”.
Declaración de la variable “ansible_sudo_pass” dentro del vault

Existen varios modos de generar los ficheros cifrados: usando las opciones “create” y “edit” o usando las opciones “encrypt” y “decrypt”, para las que el fichero ya debe existir. En el primer caso, el comando vim debe existir en el $PATH o, en su defecto, una variable $EDITOR apuntando al editor de texto deseado. La opción “rekey” se utiliza para cambiar la contraseña de cifrado de Vault si esto fuera necesario.

Una vez creados y cifrados los ficheros, falta añadirlos a nuestro playbook. Para ello basta con especificar el directorio que contiene los ficheros cifrados. De esta manera, indicamos al playbook que en cada iteración debe importar el fichero de contraseña cuyo nombre corresponde al del servidor objetivo especificado, usando la variable jinja inventory_hostname.

Sintaxis para importar contraseñas en un playbook

Finalmente, para lanzar el playbook hace falta añadir la opción “--ask-vault-pass”, la cual bloqueará el prompt a la espera de que se introduzca la contraseña con la que se cifraron los ficheros que se precisan y permitirá a Ansible leer su contenido.




Bartolomé Muñoz

martes, 24 de febrero de 2015

El malware para Android, cada vez mas sofisticado

En una entrada anterior del blog hablamos de una técnica que consistía a usar una librería nativa como libAPKProtect para descifrar código Dalvik. Durante el análisis de un nuevo APK hemos encontrado un malware que usa un concepto similar al mostrado por libAPKProtect, pero que usa técnicas mas sofisticadas para ofuscar el código y hacer mas difícil la ingeniería inversa. La relevancia de las técnicas empleadas hace interesante su análisis.

En primer lugar, detectamos que el malware carga una librería denominada libprotectClass.so”. Esa librería como sugiere su nombre descifra una parte de la aplicación ofuscada. El malware carga esa librería desde la función “attachBaseContext” que en primera instancia determina la arquitectura del sistema (Intel o ARM), y en segunda instancia carga la librería nativa correspondiente (“libprotectClass.so” en nuestro caso) ejecutando el comando System.load (“libprotectClass.so”).


Analizando la librería nativa del malware, nos damos cuenta que no tiene un formato ELF valido. En efecto, la librería tiene más secciones “header” que secciones definidas. También tiene una sección más grande que el tamaño del malware, y por último tiene un tamaño de sección “header” invalido.

Cada sección es mapeada en la memoria del proceso y tiene una semántica definida. La sección “.text” contiene el código del programa, “.data” contiene las cadenas, etc. Resulta que, por ejemplo, no se puede cargar la librería con readelf (utilidad para mostrar información sobre archivos ELF). Sin embargo, con Android funciona perfectamente, es decir que no es tan riguroso como readelf.


Tras arreglar el tamaño de cada sección de la cabecera a 40 bytes, bajar la cantidad de secciones a 23, y ajustar la sección que excede el tamaño del archivo, se ha podido arreglar el problema. Abajo se muestra la lista de secciones presentes en el malware. 


Cuando una librería nativa se carga por Android desde la llamada System.load de la maquina virtual Dalvik (DVM) se llama a la funcion “JNI_OnLoad” de esa librería. Sin embargo, esa función parece estar cifrada:
JNI_OnLoad cifrada

Mirando a más bajo nivel, en la función “dvmLoadNativeCode” de la libreria “libdvm.so” de Android, vemos que esta función llama a “dlopen” para cargar la librería “libprotectClass.so” en el proceso. La función “dlopen” se encarga de cargar la librería en memoria y llama a los constructores definidos en la librería nativa. Es decir, a la función “init” de la librería nativa (si esta definida) y a las funciones donde las referencias están definidas en la sección “.init_array” del malware. Esas funciones permiten inicializar la librería. El malware usa esta funcionalidad para descifrar la función “JNI_OnLoad”:).

Después de haber inicializado la librería, Android recoge la dirección de la función “JNI_OnLoad” llamando a la función “dlsym”, y por fin llama a “JNI_OnLoad”. Entonces, sabemos que la función está descifrada cuando Android ha recogido la dirección de la función “JNI_OnLoad”, y así podemos poner un breakpoint:).

JNI_OnLoad descifrada

Comprobaciones anti ingeniería inversa

Al principio de la ejecución de la librería nativa, el malware hace cinco comprobaciones para detectar la presencia de un depurador, evitar la lectura de memoria, etc.

El malware realiza las siguientes comprobaciones:

1) Para evitar que un programa externo lea la memoria del malware, se monitorizan todos los accesos a los archivos “/proc/pid/mem” y “/proc/pid/pagemap” gracias a las funciones “inotify_init” y “inotify_add_watch”. Si detecta una lectura de su memoria por un programa externo, el malware termina su ejecución. En efecto, una manera muy rápida para extraer todos los archivos descomprimidos/descifrados es leer la memoria del malware después de descifrarlo y buscar, por ejemplo, con una herramienta como “volatility” los nuevos DEX creados (conteniendo el código Dalvik).

2) “Hookea” las funciones de Android “__android_log_write” y “__android_log_buf_write” de la librería “liblog.so”. Estas funciones, usadas para escribir en el log de Android, ayudan a realizar la ingeniería inversa porque revelan los archivos abiertos, los DEX cargados, etc. El sistema de explotación Android usa ese log tanto a nivel kernel como de usuario.

3) Igualmente tambien “hookea” las funciones usadas por el depurador de código Dalvik como “dvmDbgActive”. Con esto evita que la aplicación pueda estar depurada al nivel de la maquina virtual Dalvik (DVM).

4) Recorre todos los procesos activos y busca la cadena “android_server” que corresponde al depurador de IDA ejecutándose en el dispositivo. Por eso, abre la carpeta “/proc” que contiene todos los PID de los procesos activos. Forma la cadena “/proc/pid/cmdline” y abre el archivo correspondiente para recoger el nombre del proceso y lo compara con “android_server”.

5) Comprueba si el nombre del proceso padre es “strace”, “ltrace”, “gdb” o “android_server”. “Strace” y “ltrace” permiten de listar las llamadas al sistema y así ver los archivos leídos/escritos, memoria leída/escrita, etc. En cuanto a “gdb” y “android_server”, son programas que permiten depurar un proceso.

Cifrado interno

Merece la pena comentar que todas las cadenas usadas por el malware están cifradas. Cada cadena es codificada en base64 antes de pasar por un XOR.


Tras las comprobaciones el malware carga una nueva librería nativa, localiza la función “JNI_OnLoad” y procede a descifrarla.


En la nueva función “JNI_OnLoad”, el malware hace llamadas a métodos Dalvik, definidos en la maquina virtual (DVM). Usa el JNI, que se puede ver como un puente entre la maquina virtual y la librería nativa, para comunicar con la DVM desde la librería nativa. Por ejemplo, se puede usar ésta técnica para “hookear” los métodos Dalvik. Se muestra un ejemplo de uso de la API de Android para recoger la clase “com/qihoo/util/StubApplication”.

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
        JNIEnv *env;
        jvm->GetEnv((void **)&env, JNI_VERSION_1_6);
        jvm->AttachCurrentThread(&env, NULL);
        gJClass = env->FindClass("com/qihoo/util/StubApplication");

        return JNI_VERSION_1_6;
}
En esta función, el malware recoge el hash de la firma del APK llamando a métodos Dalvik. Pensamos que el malware comprueba si el APK fue modificado. En efecto, cada APK necesita estar firmado antes de ser instalado en el móvil. Si queremos modificar el APK, necesitamos firmarlo otra vez, con un certificado diferente.

Comprueba la versión del SDK actual del sistema (“ro.build.version.sdk”) y del Android runtime (Dalvik o ART). Pensamos que hace esas comprobaciones para asegurarse que los métodos Dalvik necesarios para la ejecución del malware existen en la versión del sistema.

Finalmente, reserva memoria en el proceso mapeando el dev “/dev/zero”, copia el DEX cifrado en esta zona de memoria, y lo descifra. Abajo se puede ver el principio del nuevo DEX descifrado:)


Ahora, podemos hacer un volcado del nuevo DEX y abrirlo con un desensamblador de código dalvik. Se muestra una parte de una función desensamblada del nuevo DEX.


Por fin, el malware registra todas las clases del nuevo DEX como “Lcom/astep/pay/theActivity”, “Lcom/astep/pay/rd/Rd1Activity”, “Lcom/common/as/activity/DlActivity”, etc, y cambia la variable strEntryApplication de la clase “com/qihoo/util/StubApplication” a “com.ly.tcmy.application.PeibanApplication” para que la nueva aplicación sea cargada por el malware.


Este malware usa algunos de los métodos de ofuscaciones y anti-debugging mas sofisticados de los que hemos visto, como por ejemplo los incluidos en APKProtect. Estas ofuscaciones están presentes tanto al nivel del DVM como de la librería nativa. Ademas, el malware llama a funciones métodos Dalvik desde la librería nativa y así manipula objetos de la DVM que hace la depuración mas difícil. Por encima, el malware se aprovecha del poco rigor de Android para manipular la librera nativa.

Más información:

SHA256 de la muestra analizada:
1987a8b5858762247b77b62dd7ce8494c5442a19e0c883a5359332e09342b614

Introduction to Dynamic Dalvik Instrumentation,




Laurent Delosières