Empleando técnicas Rookit para ocultar VirtualBox
'Hand of the Thief' es un troyano "comercial" que se ha estado moviendo por foros rusos. Este troyano está dirigido a sistemas Linux y su función principal es el robo de credenciales bancarias, aunque no se limita a este tipo de webs. Dicha función permite al troyano captar las credenciales a través de form grabbing. Curiosamente, a pesar de ser un troyano orientado a sistemas Linux, posee un sistema de manipulación de registros DNS en memoria. Es decir, el troyano no toca los archivos para modificar la resolución de nombres local.
El troyano ha sido probado con éxito en diversas distribuciones: Fedora, Arch Linux, Gentoo, Debian entre otras.
Una de las técnicas tradicionales para evitar su análisis por parte de las compañías antivirus es la detección de máquinas virtuales, esto es, cuando el troyano se está ejecutando en un entorno virtualizado su comportamiento pasa a ser inocuo o directamente finaliza su ejecución de manera prematura. 'Hand of the Thief' detecta máquinas virtuales VirtualBox, VMware, Power VM, IBM/S390, QEMU y Xen.
No vamos a describir aquí el comportamiento de este troyano, ya que esto daría para otro post y además existe un excelente análisis aquí. Lo que vamos a ver son técnicas concretas que podemos usar para esconder el entorno de virtualización, en concreto VirtualBox. Primero introduciremos los términos que vamos a usar y posteriormente veremos como el troyano intenta detectar la virtualización y como podemos evadir dicha funcionalidad.
Definiciones
Las técnicas rootkit permiten ocultar la presencia de un malware, explotar vulnerabilidades y escalar privilegios en un sistema (cuanto más altos, mayor control sobre el sistema). Un rootkit puede cargarse tanto en espacio de usuario como en el espacio del kernel (donde siempre tendrá más control).
Una llamada al sistema o system call, es una función que se efectúa en el espacio de usuario para transferir momentaneamente el control al kernel a través de una interrupción. Podemos ver una lista de las llamadas al sistema realizadas por un proceso a través del programa strace. El funcionamiento de una llamada al sistema es sencillo: Un proceso en espacio de usuario necesita ejecutar una llamada al sistema, mete en la pila su índice, parámetros y ejecuta una interrupción a través del código ensamblador INT 0x80. En ese momento el kernel recoge dicha petición, busca en el índice la función correspondiente, la ejecuta con los parámetros provistos por el usuario y retorna el resultado y el control de ejecución al proceso del usuario.
Un driver es un módulo del kernel. Un programa inyectado en el espacio del kernel teniendo acceso tanto a dicho espacio como al espacio del usuario. Dispone de una variedad de instrucciones más amplia que un proceso de usuario debido a los privilegios que dispone. Un módulo se carga en el kernel con el comando insmod o modprobe y se desinstala con rmmod.
Evitando la detección
Para detectar la máquina virtual VirtualBox, el troyano prueba la presencia de la cadena “VBOX” dentro del archivo “/proc/scsi/scsi” que lista los discos SCSI usados (disco duro, disco de DVD, etc.). Si lo encuentra, el troyano termina su ejecución. Como dijo P. Kalni, podemos evitar esta prueba quitando el modo lectura de este archivo para que el troyano no pueda leerlo. Sin embargo, este método es limitado y no puede generalizarse a otro troyano ya que éste podría probar si el archivo esta en modo lectura. Nuestra solución implica el uso de un rootkit para modificar el contenido del archivo leído. Es decir, que cuando el troyano lea el archivo, el rootkit va a buscar la cadena VBOX y la reemplazará por otra cadena. De esta manera, modificamos el contenido leído al vuelo y no el archivo. En este ejemplo, modificamos la cadena VBOX por “____”.
Cuando un programa lee un archivo, llama a la system call “read” de la biblioteca libc que ejecuta la función del kernel “sys_read” a través de la interrupción 0x80. En otras palabras, para modificar el contenido al vuelo de un archivo leído se necesita hookear la función del espacio de usuario “read” o del espacio del kernel “sys_read”. Hemos elegido hookear la función del kernel porque el programa podría escapar del hook de la función “read” llamando a la función “sys_read” directamente. Además, hookear la función “read” supone que el malware use la biblioteca libc para leer un archivo.
Hookear la función "sys_read" significa cambiar el modo lectura de la tabla de llamadas al sistema para poder escribir y cambiar el index en dicha tabla de la función “sys_read” para redireccionarla a nuestra función “new_sys_read”. Hemos creado un driver para hookear la función “sys_read” y crear la nueva función “sys_new_read”. Cuando la función “new_sys_read” sea llamada, ella va a (1) llamar a la función original “sys_read” para recoger una parte del archivo o la totalidad del archivo en un búfer, (2) reemplazará cada cadena VBOX por “____” del búfer y (4) devolverá el búfer modificado.
Sin embargo, necesitamos conocer la dirección de la función “sys_read” para llamarla desde "new_sys_read", conocer la dirección de la tabla de llamadas al sistema para modificar la entrada “sys_read”, y también la dirección de la función “lookup_address” que nos permite obtener la página correspondiente a la tabla de llamadas al sistema para cambiarla del modo lectura y escritura. Estas direcciones variarán de un sistema a otro. Se pueden encontrar en el archivo /boot/System.map.
Una parte del código fuente:
//Nueva función sys_read
asmlinkage long
new_sys_read(unsigned int fd, char __user *buf, size_t count)
{
//Llama a la funcion sys_read original
nread = (*ptr_original_sys_read)(fd, buf, count);
//Quita el límite para leer la memoria del user space
fs = get_fs();
set_fs(get_ds());
//Oculta la cadena VBOX
ptr = buf;
while ( (ptr = strstr(ptr, "VBOX")) != NULL)
{
strncpy(ptr, "____", 4);
}
//Pone de nuevo el limite de memoria
set_fs(fs);
return nread;
}
//Pone en lectura y escritura la página correspondiente a la dirección “addr”
void
_set_addr_rw(unsigned long addr)
{
unsigned int level;
pte_t *pte = (*ptr_lookup_address)(addr, &level);
pte->pte |= 0x002;
}
//Primera función ejecutada cuando el driver esté cargado
int
init_module(void)
{
unsigned long long ret;
//Consigue el index de la tabla de llamadas al sistema correspondiente a la función original
sys_read
NR_sys_read = get_index_syscall((unsigned long *)ptr_original_sys_read);
//Cambia en lectura y escritura la tabla syscall
set_addr_rw((unsigned long)(sys_call_table + 1*NR_sys_read));
//Hookea la función sys_read
*(sys_call_table + 1*NR_sys_read) = new_sys_read;
return 0;
}
El código compilaría un módulo del kernel que podemos instalar con insmod.
Antes de hookear la función “sys_read” del kernel:
Salida del comando cat /proc/scsi/scsi:
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: VBOX HARDDISK model Rev: 1.0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: VBOX Model: CD-ROM Rev: 1.0
Type: CD-ROM ANSI SCSI revision: 05
Después de hookear la función “sys_read” del kernel
Salida del comando cat /proc/scsi/scsi:
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: ____ HARDDISK model Rev: 1.0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: ____ Model: CD-ROM Rev: 1.0
Type: CD-ROM ANSI SCSI revision: 05
Esto es solo una de las múltiples técnicas que podemos emplear para evadir este tipo de detecciones. Por supuesto, ni es la única técnica que emplean los troyanos para evadir la ejecución sobre una plataforma virtualizada, ni es la única contramedida de las que disponemos.
Fuente : Hispasec
El troyano ha sido probado con éxito en diversas distribuciones: Fedora, Arch Linux, Gentoo, Debian entre otras.
Una de las técnicas tradicionales para evitar su análisis por parte de las compañías antivirus es la detección de máquinas virtuales, esto es, cuando el troyano se está ejecutando en un entorno virtualizado su comportamiento pasa a ser inocuo o directamente finaliza su ejecución de manera prematura. 'Hand of the Thief' detecta máquinas virtuales VirtualBox, VMware, Power VM, IBM/S390, QEMU y Xen.
No vamos a describir aquí el comportamiento de este troyano, ya que esto daría para otro post y además existe un excelente análisis aquí. Lo que vamos a ver son técnicas concretas que podemos usar para esconder el entorno de virtualización, en concreto VirtualBox. Primero introduciremos los términos que vamos a usar y posteriormente veremos como el troyano intenta detectar la virtualización y como podemos evadir dicha funcionalidad.
Definiciones
Las técnicas rootkit permiten ocultar la presencia de un malware, explotar vulnerabilidades y escalar privilegios en un sistema (cuanto más altos, mayor control sobre el sistema). Un rootkit puede cargarse tanto en espacio de usuario como en el espacio del kernel (donde siempre tendrá más control).
Una llamada al sistema o system call, es una función que se efectúa en el espacio de usuario para transferir momentaneamente el control al kernel a través de una interrupción. Podemos ver una lista de las llamadas al sistema realizadas por un proceso a través del programa strace. El funcionamiento de una llamada al sistema es sencillo: Un proceso en espacio de usuario necesita ejecutar una llamada al sistema, mete en la pila su índice, parámetros y ejecuta una interrupción a través del código ensamblador INT 0x80. En ese momento el kernel recoge dicha petición, busca en el índice la función correspondiente, la ejecuta con los parámetros provistos por el usuario y retorna el resultado y el control de ejecución al proceso del usuario.
Un driver es un módulo del kernel. Un programa inyectado en el espacio del kernel teniendo acceso tanto a dicho espacio como al espacio del usuario. Dispone de una variedad de instrucciones más amplia que un proceso de usuario debido a los privilegios que dispone. Un módulo se carga en el kernel con el comando insmod o modprobe y se desinstala con rmmod.
Evitando la detección
Para detectar la máquina virtual VirtualBox, el troyano prueba la presencia de la cadena “VBOX” dentro del archivo “/proc/scsi/scsi” que lista los discos SCSI usados (disco duro, disco de DVD, etc.). Si lo encuentra, el troyano termina su ejecución. Como dijo P. Kalni, podemos evitar esta prueba quitando el modo lectura de este archivo para que el troyano no pueda leerlo. Sin embargo, este método es limitado y no puede generalizarse a otro troyano ya que éste podría probar si el archivo esta en modo lectura. Nuestra solución implica el uso de un rootkit para modificar el contenido del archivo leído. Es decir, que cuando el troyano lea el archivo, el rootkit va a buscar la cadena VBOX y la reemplazará por otra cadena. De esta manera, modificamos el contenido leído al vuelo y no el archivo. En este ejemplo, modificamos la cadena VBOX por “____”.
Cuando un programa lee un archivo, llama a la system call “read” de la biblioteca libc que ejecuta la función del kernel “sys_read” a través de la interrupción 0x80. En otras palabras, para modificar el contenido al vuelo de un archivo leído se necesita hookear la función del espacio de usuario “read” o del espacio del kernel “sys_read”. Hemos elegido hookear la función del kernel porque el programa podría escapar del hook de la función “read” llamando a la función “sys_read” directamente. Además, hookear la función “read” supone que el malware use la biblioteca libc para leer un archivo.
Hookear la función "sys_read" significa cambiar el modo lectura de la tabla de llamadas al sistema para poder escribir y cambiar el index en dicha tabla de la función “sys_read” para redireccionarla a nuestra función “new_sys_read”. Hemos creado un driver para hookear la función “sys_read” y crear la nueva función “sys_new_read”. Cuando la función “new_sys_read” sea llamada, ella va a (1) llamar a la función original “sys_read” para recoger una parte del archivo o la totalidad del archivo en un búfer, (2) reemplazará cada cadena VBOX por “____” del búfer y (4) devolverá el búfer modificado.
Sin embargo, necesitamos conocer la dirección de la función “sys_read” para llamarla desde "new_sys_read", conocer la dirección de la tabla de llamadas al sistema para modificar la entrada “sys_read”, y también la dirección de la función “lookup_address” que nos permite obtener la página correspondiente a la tabla de llamadas al sistema para cambiarla del modo lectura y escritura. Estas direcciones variarán de un sistema a otro. Se pueden encontrar en el archivo /boot/System.map.
Una parte del código fuente:
//Nueva función sys_read
asmlinkage long
new_sys_read(unsigned int fd, char __user *buf, size_t count)
{
//Llama a la funcion sys_read original
nread = (*ptr_original_sys_read)(fd, buf, count);
//Quita el límite para leer la memoria del user space
fs = get_fs();
set_fs(get_ds());
//Oculta la cadena VBOX
ptr = buf;
while ( (ptr = strstr(ptr, "VBOX")) != NULL)
{
strncpy(ptr, "____", 4);
}
//Pone de nuevo el limite de memoria
set_fs(fs);
return nread;
}
//Pone en lectura y escritura la página correspondiente a la dirección “addr”
void
_set_addr_rw(unsigned long addr)
{
unsigned int level;
pte_t *pte = (*ptr_lookup_address)(addr, &level);
pte->pte |= 0x002;
}
//Primera función ejecutada cuando el driver esté cargado
int
init_module(void)
{
unsigned long long ret;
//Consigue el index de la tabla de llamadas al sistema correspondiente a la función original
sys_read
NR_sys_read = get_index_syscall((unsigned long *)ptr_original_sys_read);
//Cambia en lectura y escritura la tabla syscall
set_addr_rw((unsigned long)(sys_call_table + 1*NR_sys_read));
//Hookea la función sys_read
*(sys_call_table + 1*NR_sys_read) = new_sys_read;
return 0;
}
El código compilaría un módulo del kernel que podemos instalar con insmod.
Antes de hookear la función “sys_read” del kernel:
Salida del comando cat /proc/scsi/scsi:
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: VBOX HARDDISK model Rev: 1.0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: VBOX Model: CD-ROM Rev: 1.0
Type: CD-ROM ANSI SCSI revision: 05
Después de hookear la función “sys_read” del kernel
Salida del comando cat /proc/scsi/scsi:
Attached devices:
Host: scsi0 Channel: 00 Id: 00 Lun: 00
Vendor: ATA Model: ____ HARDDISK model Rev: 1.0
Type: Direct-Access ANSI SCSI revision: 05
Host: scsi1 Channel: 00 Id: 00 Lun: 00
Vendor: ____ Model: CD-ROM Rev: 1.0
Type: CD-ROM ANSI SCSI revision: 05
Esto es solo una de las múltiples técnicas que podemos emplear para evadir este tipo de detecciones. Por supuesto, ni es la única técnica que emplean los troyanos para evadir la ejecución sobre una plataforma virtualizada, ni es la única contramedida de las que disponemos.
Fuente : Hispasec
Comentarios
Publicar un comentario