24 febrero 2009

Hackeos Memorables: Jfs en el concurso de PcWeek.

PCWEEK mantenía un wargame allá en los 90 donde el ganador se llevaba un premio de 1.000$, hoy vamos a contar como el español "Jfs" consiguió ganar este concurso de importancia Internacional y que obstáculos tuvo que saltar. Es posiblemente uno de los hackeos memorables más bonitos y didácticos que recuerdo.

El reto consistía en localizar un fichero secreto en un servidor en Internet.

Siguiendo una metodología aplicable hoy en día, lo primero que hizo fue obtener información, asegurarse que únicamente era el puerto web el abierto a Internet, que otros servicios corrían y que aplicaciones estaban instaladas.

Mediante un telnet, en las propia cabecera "Server" revelaba ser un Apache instalado en una RedHat. Tras probar los cgis típicos con vulnerabilidades de la época, no encontró ninguno de ellos, pero si aparecieron rastros de otros directorios interesantes. En concreto uno llamado "photoads".
HTTP/1.1 400 Bad Request
Date: Fri, 24 Sep 1999 23:42:15 GMT
Server: Apache/1.3.6 (Unix) (Red Hat/Linux)
Tras investigar un poco, obtuvo una copia prestada de este software comercial, escrito en perl y distribuido con código fuente. Lo que permitía buscar archivos de configuración y vulnerabilidades no conocidas.

La aplicación tenía algunos cgis instalados que revelaban información importante, como "env.cgi", mostrando el valor del "DocumentRoot" y el usuario con el que se ejecutaba el servidor web.

Después de tratar de explotar SSI, descarto la opción y comenzó la auditoría sobre el código fuente de la aplicación. Lo primero, buscar donde se abrían ficheros:

emming:~/photoads/cgi-bin# grep 'open.*(.*)' *cgi | more
advisory.cgi: open (DATA, "$BaseDir/$DataFile");
edit.cgi: open (DATA, ">$BaseDir/$DataFile");
edit.cgi: open(MAIL, "|$mailprog -t") || die "Can't open $mailprog!\n";
photo.cgi: open(ULFD,">$write_file") || die show_upload_failed("$write_file $!");
photo.cgi: open ( FILE, $filename );
Ya estaba más cerca, la variable "write_file" se formaba en base a otras dos:

$write_file = $Upload_Dir.$filename;


"Upload_Dir" no puede ser sobrescrita por encontrarse en el fichero de configuración, en cambio. "filename" proviene del nombre del fichero que es filtrado mediante una expresión regular ninja.

$filename =~ s/.+\\([^\\]+)$|.+\/([^\/]+)$/\1/;

Nuestro héroe consigue saltar esta expresión añadiendo un carácter null () al nombre tal y como cuenta "rfp" en Phrack, además de escapar el primer punto:

/jfs/\../../../../../../../export/www/htdocs/index.html.gif

Para su desgracia y nuestro entretenimiento, existían algunas trabas más. El fichero subido era analizado comprobando sus dimensiones. Lo que obligaba a que los bytes 6, 7, 8 y 9 del fichero subido fueran 0, (nul), evitando ser cazado en esta verificación.
if ( substr ( $filename, -4, 4 ) eq ".gif" ) {
open ( FILE, $filename );
my $head;
my $gHeadFmt = "A6vvb8CC";
my $pictDescFmt = "vvvvb8";
read FILE, $head, 13;
(my $GIF8xa, $width, $height, my $resFlags, my $bgColor, my $w2h) = unpack $gHeadFmt, $head;
close FILE;
$PhotoWidth = $width;
$PhotoHeight = $height;
$PhotoSize = $size;
return;
}

Para rematar, una vez subido el fichero, este es movido a un directorio no controlable y que para colmo, su nombre se compone solo de números.

chmod 0755, $Upload_Dir.$filename;
$newname = $AdNum;
rename("$write_file", "$Upload_Dir/$newname");

Show_Upload_Success($write_file);


Queda descartado el viejo truco "../../..", habrá que jugar con la función "rename()", que tal y como se muestra, no está controlando excepciones. Por lo tanto, si existiese un error al renombrar, la aplicación continuaría sin que nada ocurra...

...Llegados a este punto, me gustaría terminar aquí y dejar la solución que se le ocurrió para otro día, generando la misma intriga que una novela de Agatha Christie, pero me siento generoso...

Para generar el error y evitar que su fichero fuera renombrado y movido, utilizó las propiedades del kernel de Linux, el cual tiene una restricción de 1024 para el tamaño del PATH (nombre más directorio), y que una vez superados, generará la excepción.

Con todo lo visto hasta ahora ya se conocen los requisitos para crear un fichero con permisos de "nobody" y con cualquier contenido (exceptuando la cabecera de la imagen para saltar la verificación).

Una shell script no funcionaría ya que no se pueden asignar los bytes del 6 al 9 en NUL (comprobación de tamaño), así que la opción pasa por generar un binario ELF de linux, "encodearlo" para pasarlo mediante un GET (la aplicación no leía mediante el método POST) y ajustar su tamaño para poder ser enviado dentro del URI máximo que acepta Apache (8190 bytes).

Recordamos que el objetivo del concurso era encontrar un fichero secreto, por lo que generó un pequeño programa que hiciera un "find /" en el sistema, que mostrase todos los archivos.

lemming:~/pcweek/hack/POST# cat fin.c
#include <stdio.h>
main()
{
printf("Content-type: text/html\n\n\r");
fflush(stdout);
execlp("/usr/bin/find","find","/",0);
}

El primer intento no funcionó, ya que el tamaño de este ejecutable era demasiado grande.

Tras reducirlo mediante la eliminación de partes del binario "a mano", su tamaño se redujo lo suficiente como para que funcionase y poder ser ejecutado.

El resultado fue negativo y el fichero no apareció, estos comandos se ejecutaban como usuario nobody y el fichero tenía otros permisos. Estaba muy cerca, ya que conocía el método para poder subir y ejecutar comandos.

No había otra solución, necesitaba root. En aquel entonces existía un fallo en crontab que permitía escalada de privilegios, así que utilizando esta vulnerabilidad se creó una suitroot con la que dispondría de estos permisos para tener control total sobre el sistema.

Una vez encontrado el fichero y modificada la página principal. Finalizó el juego. Jfs se coronaba ganador.

Podéis consultar la historia completa con más detalles contados por el propio Jfs en la vieja página de Hispahack: http://hispahack.ccc.de/oldweb/introes.htm

3 comments :

Asfasfos dijo...

"Tras investigar un poco, obtuvo una copia prestada de este software comercial"

No había emule no ni p2p, no? xDDD. La verdad que si que se curró un huevo el hackeo, digno de reconocimiento.

Anónimo dijo...

Nopz, creo que tenía algún amiguete que trabajaba en el desarrollo de ese producto. Salieron ganando ambos, el por llevarse lo que quería, y los desarrolladores de ese software por que les hubieran encontrado el "buhero" :-)

P.D La entrevista que le hicieron creo que salía en el numero de agosto del 98 de la revista iWORLD

Saludos

Anónimo dijo...

El amigo jfs, se aprovechó y se lucró preguntando a todos los "amigos" de !Hispahack, como solucionar varias etapas del concurso.Es decir, el concurso lo ganaron entre todos los miembros de !H, pero el se llevó la gloria. No tenía mucha moral el muchacho...

Pero claro, la historia reconoce más a los trepas y crápulas...