Introdução Teórica e Contextualização
A vulnerabilidade de Local File Inclusion (LFI) é uma falha de segurança de alta severidade que ocorre quando uma aplicação web permite que dados não sanitizados ou não validados, fornecidos pelo usuário, sejam passados diretamente a funções internas do servidor responsáveis por carregar, incluir ou ler arquivos no sistema de arquivos local.
Enquadramento no OWASP Top 10
No padrão internacional de riscos de segurança da OWASP (Open Web Application Security Project), o LFI classifica-se primariamente sob duas categorias estratégicas:
- A03A03:2021 — Injection
Ocorre devido ao tratamento inadequado de entradas de dados não confiáveis que alteram a lógica de interpretação de comandos ou caminhos do servidor.
- A05A05:2021 — Security Misconfiguration
Muitas vezes intensificado por configurações inseguras no servidor web ou runtime da linguagem, como permissões excessivas do processo web ou diretivas ativas como
allow_url_include.
Diferença fundamental: Path Traversal vs. LFI
Embora os termos sejam frequentemente usados como sinônimos na prática de testes de penetração, existe uma distinção técnica crucial entre ambos:
| Característica | Path Traversal / Directory Traversal | Local File Inclusion (LFI) |
|---|---|---|
| Objetivo primário | Navegar na estrutura de diretórios para ler ou extrair bytes de um arquivo. | Solicitar que o motor do servidor carregue, interprete ou execute o arquivo. |
| Mecanismo interno | Funções de leitura estática, como file_get_contents, readfile, fs.readFile e send_file. | Funções do runtime que podem executar código, como include e require no PHP. |
| Resultado | Vazamento de dados ou leitura de arquivo texto. | Leitura de arquivo ou execução de código caso o arquivo contenha instruções interpretáveis. |
LFI vs. RFI (Remote File Inclusion)
LFI
O arquivo acessado deve existir no sistema de arquivos local do servidor, mesmo que seja temporário, log ou upload.
RFI
O servidor é induzido a buscar conteúdo externo via HTTP/FTP. No PHP, depende de configurações inseguras, como allow_url_include.
Anatomia do Código Vulnerável
A raiz de toda vulnerabilidade LFI é a confiança cega da aplicação nas entradas do usuário. Veja como o padrão vulnerável aparece em diferentes ecossistemas web.
A. PHP — a linguagem clássica
Devido ao modelo de renderização dinâmica do PHP, funções nativas de inclusão são muito utilizadas para criar sistemas de templates ou carregar componentes modulares.
<?php
$page = isset($_GET['page']) ? $_GET['page'] : 'pages/home.php';
// Vulnerável: entrada do usuário chega diretamente à inclusão.
include($page);
?>
Inclusão/execução: include(), require(), include_once() e require_once(). Leitura: file_get_contents(), readfile(), file(), fopen() e copy().
B. Python — Flask e Django
Embora Python não execute código arbitrário ao ler arquivos como o PHP faz, o uso incorreto de funções de manipulação de arquivo pode resultar em vazamento de código e credenciais via Path Traversal/LFI.
from flask import Flask, request, send_file
import os
app = Flask(__name__)
@app.route('/download')
def download_file():
filename = request.args.get('file')
file_path = os.path.join('/var/www/static/', filename)
return send_file(file_path)
Em um laboratório autorizado, uma entrada como ?file=../../../../etc/passwd demonstra como a resolução do caminho pode sair da pasta prevista.
C. Node.js — Express
No ecossistema Node.js, erros ao servir arquivos estáticos ou manipular o sistema de arquivos (fs) geram vulnerabilidades graves de leitura arbitrária.
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
app.get('/view', (req, res) => {
const fileName = req.query.file;
const filePath = path.join(__dirname, 'public', fileName);
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) return res.status(404).send('Arquivo não encontrado');
res.send(data);
});
});
D. Java — Servlets e JSP
Aplicações Java corporativas baseadas em Servlets ou JSP podem ser afetadas ao utilizar o RequestDispatcher com entrada não confiável.
String page = request.getParameter("page");
RequestDispatcher dispatcher = request.getRequestDispatcher(
"/WEB-INF/views/" + page
);
dispatcher.forward(request, response);
Se o parâmetro page aceitar segmentos de subida de diretório, o acesso pode escapar do local originalmente esperado.
Mapeamento de Arquivos Sensíveis
Ao confirmar a presença de uma falha de LFI, testes autorizados buscam compreender o impacto sem coletar mais dados do que o necessário. O acesso depende das permissões do processo web.
Chaves privadas, hashes, variáveis de ambiente e históricos podem conter credenciais reais. Em laboratório, use dados fictícios e registre apenas a evidência mínima.
Targets críticos em sistemas Linux
| Caminho | Descrição e importância |
|---|---|
/etc/passwd | Lista usuários, diretórios home e shells padrão; útil para demonstrar enumeração de contas. |
/etc/shadow | Contém hashes de senhas e normalmente exige privilégios de root. |
/proc/self/environ | Variáveis de ambiente do processo web; podem conter segredos e credenciais. |
/proc/self/cmdline | Comando utilizado para iniciar o processo web atual. |
/proc/self/fd/N | Descritores de arquivos abertos pelo processo, incluindo temporários. |
~/.ssh/id_rsa / id_ed25519 | Chaves privadas SSH; exposição representa comprometimento crítico. |
~/.bash_history / .zsh_history | Histórico de comandos executados no terminal. |
/var/log/apache2/access.log | Log de acessos do Apache e superfície associada a log poisoning. |
/var/log/nginx/access.log | Log de acessos do Nginx. |
/var/log/auth.log / sshd.log | Logs de autenticação SSH. |
Técnicas de Traversal e Bypasses de Filtros
Filtros frágeis procuram uma sequência específica, enquanto o sistema de arquivos trabalha com o caminho já decodificado e normalizado. A diferença entre essas duas representações cria oportunidades de bypass.
O problema da ordem de processamento
Uma validação feita antes da última transformação pode analisar um valor diferente daquele que chegará à função de arquivo. Por isso, bloquear apenas a cadeia ../ não estabelece uma fronteira de segurança.
Variações relevantes em laboratório
| Categoria | Exemplo didático | Por que filtros simples falham |
|---|---|---|
| URL encoding | %2e%2e%2f | O filtro pode executar antes da decodificação feita pelo framework. |
| Codificação dupla | %252e%252e%252f | Camadas diferentes podem decodificar a entrada mais de uma vez. |
| Separador alternativo | ..\ | Em Windows, a barra invertida também separa diretórios. |
| Caminho absoluto | /etc/passwd ou C:\Windows\win.ini | Não depende de segmentos de subida para sair da pasta prevista. |
| Prefixos redundantes | ....// | Uma única substituição textual pode produzir novamente ../. |
| Esquemas | php://filter/... | Não é um caminho tradicional e pode ser interpretado pelo runtime. |
O sufixo %00 contornava extensões forçadas em versões antigas do PHP e de bibliotecas C. Runtimes modernos normalmente rejeitam esse byte, mas sistemas legados ainda devem ser avaliados.
Como testar sem extrapolar o escopo
- 01Defina uma linha de base
Compare tamanho, código HTTP, título e tempo de resposta de uma página válida com uma página inexistente.
- 02Use arquivos-cobaia
Crie no laboratório um arquivo sem dados reais, como
/opt/lab/marcador.txt, e limite o objetivo à leitura desse marcador. - 03Varie uma dimensão por vez
Teste codificação, profundidade e separadores separadamente para saber qual transformação causou o resultado.
- 04Registre e interrompa
Ao demonstrar a falha, preserve a requisição mínima, pare a enumeração e avance para a remediação.
PHP Wrappers e Pseudo-Protocolos
Wrappers são interfaces nativas que permitem ao PHP tratar diferentes fontes de dados como fluxos. Quando uma entrada não confiável alcança include(), require() ou funções de leitura, esses esquemas podem ampliar o impacto da falha.
php://filter: leitura transformada
O wrapper php://filter aplica filtros a um recurso antes de entregá-lo à aplicação. Em laboratório, a transformação Base64 demonstra a leitura do código-fonte de um arquivo PHP sem que ele seja interpretado.
GET /index.php?page=php://filter/convert.base64-encode/resource=index.php HTTP/1.1
Host: 127.0.0.1
Connection: close
Uma resposta com texto Base64 que decodifica para o código da aplicação comprova exposição de fonte. Não é necessário procurar credenciais ou arquivos adicionais.
Principais wrappers e condições
| Wrapper | Uso legítimo | Risco quando a entrada é controlada | Controle principal |
|---|---|---|---|
php://filter | Transformar dados em fluxo. | Exposição de código-fonte e arquivos legíveis. | Não permitir que o usuário componha o recurso. |
php://input | Ler o corpo bruto da requisição. | Inclusão de conteúdo enviado no corpo em configurações vulneráveis. | Allowlist de rotas e runtime atualizado. |
data:// | Representar dados inline. | Conteúdo interpretável quando opções perigosas estão habilitadas. | Manter allow_url_include=Off. |
zip:// / phar:// | Acessar recursos empacotados. | Processamento inesperado de arquivos controlados. | Separar uploads, validar formato e evitar inclusão dinâmica. |
file:// | Acessar arquivos locais por URI. | Leitura absoluta fora da pasta esperada. | Mapear identificadores para caminhos internos fixos. |
Configuração defensiva do PHP
allow_url_fopen = Off
allow_url_include = Off
display_errors = Off
log_errors = On
open_basedir = /var/www/app:/var/lib/php/sessions:/tmp
open_basedir e diretivas de URL reduzem impacto, mas a defesa principal continua sendo impedir que dados do usuário sejam usados como nomes de arquivos ou recursos.
Escalada de LFI para Execução de Código
LFI não implica automaticamente execução remota de código. A escalada ocorre apenas quando o atacante consegue fazer conteúdo interpretável existir em um arquivo local que a aplicação vulnerável posteriormente inclui.
Pré-condições da cadeia
Fontes de conteúdo controlável
Log poisoning
Cabeçalhos ou caminhos registrados pelo servidor podem chegar ao arquivo de log. A escalada exige que esse log seja legível e incluído pelo PHP.
Arquivos de sessão
Dados persistidos em sessões podem ser controláveis. O formato, o nome e o diretório variam conforme a configuração.
Arquivos enviados
Um upload aceito como imagem ou documento pode conter conteúdo inesperado. A exploração depende de conhecer o caminho e incluí-lo.
Ambiente e temporários
Variáveis, descritores e arquivos temporários podem expor conteúdo, mas permissões e ciclo de vida limitam a cadeia.
Prova de conceito segura
Em uma VM descartável, use somente um marcador inofensivo para demonstrar interpretação. O objetivo é observar LAB_OK, não executar comandos do sistema.
<?php
// Arquivo criado exclusivamente dentro do laboratório.
echo 'LAB_OK';
?>
Se o marcador for interpretado, a cadeia está comprovada. Não use webshells, reverse shells, persistência ou comandos de pós-exploração em uma atividade didática.
Como quebrar a cadeia
- ARemova a inclusão controlável
Troque caminhos fornecidos pelo usuário por chaves de uma allowlist interna.
- BIsole conteúdo gravável
Logs, sessões e uploads não devem ficar dentro de diretórios interpretáveis pelo servidor web.
- CReduza permissões
O usuário do serviço deve ler apenas os arquivos estritamente necessários à aplicação.
- DDesative interpretação em uploads
Sirva arquivos enviados por um domínio estático, armazenamento de objetos ou diretório sem execução.
Automação, Fuzzing e Reconhecimento
Automação ajuda a encontrar variações, mas também multiplica tráfego e impacto. Em aula, restrinja o alvo a 127.0.0.1 ou a uma VM isolada, use uma lista curta e pare assim que a hipótese for confirmada.
Fluxo de trabalho reproduzível
- 1Documente o escopo
Registre host, porta, endpoint, janela de teste e arquivos-cobaia autorizados.
- 2Capture respostas-base
Faça uma requisição válida e outra inválida para identificar tamanho, status e texto característicos.
- 3Construa uma wordlist mínima
Inclua somente o marcador do laboratório, profundidades necessárias e representações que respondam à hipótese.
- 4Limite taxa e concorrência
Evite indisponibilidade acidental e facilite a correlação com os logs do servidor.
- 5Valide manualmente
Um resultado diferente é apenas um indício; reproduza a requisição isoladamente antes de concluir.
Linha de base com cURL
# Página válida
curl -i 'http://127.0.0.1:8080/index.php?page=pages/home.php'
# Entrada inválida para comparação
curl -i 'http://127.0.0.1:8080/index.php?page=nao-existe.php'
Fuzzing com lista curta
pages/home.php
../marcador.txt
../../marcador.txt
%2e%2e%2fmarcador.txt
php://filter/convert.base64-encode/resource=index.php
ffuf \
-u 'http://127.0.0.1:8080/index.php?page=FUZZ' \
-w lfi-lab.txt \
-rate 5 \
-t 1 \
-mc all
Aplicações vulneráveis frequentemente retornam 200 OK até em erros. Compare corpo, tamanho, hashes, mensagens e comportamento com as respostas-base.
Evidências mínimas para o relatório
| Campo | O que registrar |
|---|---|
| Localização | Endpoint, parâmetro e método HTTP afetados. |
| Reprodução | Uma requisição mínima e uma resposta com o marcador do laboratório. |
| Impacto | Tipo de dado acessível e permissões observadas, sem coletar dados reais desnecessários. |
| Causa raiz | Fluxo da entrada até a função de arquivo vulnerável. |
| Correção | Patch proposto, teste negativo e evidência de que a rota legítima continua funcionando. |
Estudo de Caso Prático
O portal fictício FatecSeg Corp usa um parâmetro para escolher o conteúdo da página. O exercício conduz da identificação à correção, sempre em uma VM isolada e com um arquivo-cobaia criado pelo professor.
Demonstrar que o parâmetro page alcança uma operação de inclusão, ler apenas o marcador autorizado e substituir a implementação por um mapa fechado de rotas.
1. Reconhecer o fluxo normal
Abra as páginas pelo menu e observe valores como pages/home.php e pages/company.php. A aplicação revela que o nome do arquivo é transportado na URL.
GET /fatecseg/index.php?page=pages/company.php HTTP/1.1
Host: 127.0.0.1
2. Localizar a causa raiz
<?php
$page = isset($_GET['page']) ? $_GET['page'] : 'pages/home.php';
include($page);
?>
O valor externo chega a include() sem passar por uma decisão interna. A aplicação delega ao visitante a escolha do recurso que o runtime tentará abrir.
3. Confirmar com o marcador autorizado
O professor prepara /opt/fatecseg/contrato-interno.txt com conteúdo fictício. Os alunos ajustam apenas a profundidade do caminho até obter o marcador, registram a evidência e interrompem a exploração.
Não acesse /etc/shadow, chaves SSH, históricos, variáveis com segredos ou dados de outros usuários. O marcador já é evidência suficiente.
4. Aplicar a correção
<?php
$routes = array(
'home' => __DIR__ . '/pages/home.php',
'company' => __DIR__ . '/pages/company.php',
'services' => __DIR__ . '/pages/services.php',
'news' => __DIR__ . '/pages/news.php',
'contact' => __DIR__ . '/pages/contact.php'
);
$route = isset($_GET['page']) ? $_GET['page'] : 'home';
if (!isset($routes[$route])) {
http_response_code(404);
exit('Página não encontrada');
}
include $routes[$route];
?>
5. Executar testes de regressão
| Caso | Entrada | Resultado esperado |
|---|---|---|
| Rota válida | page=company | Página da empresa com HTTP 200. |
| Rota inexistente | page=nao-existe | HTTP 404 sem detalhes internos. |
| Traversal | page=../../marcador.txt | HTTP 404; nenhum conteúdo do arquivo. |
| Wrapper | page=php://filter/... | HTTP 404; esquema não é avaliado. |
| Separador Windows | page=..\marcador.txt | HTTP 404; entrada ausente do mapa. |
Defesa em Profundidade e Código Seguro
A correção mais forte elimina a relação direta entre a entrada do usuário e o sistema de arquivos. Controles adicionais reduzem o impacto caso outra falha apareça, mas não substituem essa mudança de arquitetura.
Prioridade 1: identificadores, não caminhos
O cliente envia uma chave curta, como company. Somente o servidor conhece o caminho correspondente. Isso impede traversal, caminhos absolutos e wrappers por construção.
Caminho vindo da URL
?page=pages/company.php expõe estrutura interna e permite que a entrada influencie a resolução.
Identificador de negócio
?page=company é convertido por um mapa imutável mantido no código da aplicação.
Quando o caminho dinâmico for inevitável
Normalize o destino, resolva o caminho real e verifique a contenção dentro de uma pasta base. A existência e o tipo do arquivo também devem ser confirmados.
<?php
$base = realpath(__DIR__ . '/documents');
$name = isset($_GET['file']) ? $_GET['file'] : '';
$candidate = realpath($base . DIRECTORY_SEPARATOR . $name);
$insideBase = $candidate !== false
&& strpos($candidate, $base . DIRECTORY_SEPARATOR) === 0
&& is_file($candidate);
if (!$insideBase) {
http_response_code(404);
exit('Arquivo não encontrado');
}
readfile($candidate);
?>
Compare caminhos canônicos com o separador ao final da base. Uma comparação ingênua com strpos($candidate, $base) === 0 também aceitaria uma pasta vizinha chamada, por exemplo, documents-backup.
Camadas complementares
- 01Menor privilégio
Execute o serviço com usuário dedicado, sem shell interativo e sem acesso a chaves, backups ou diretórios administrativos.
- 02Separação de uploads
Armazene arquivos enviados fora do DocumentRoot, gere nomes internos e sirva-os como conteúdo estático sem interpretação.
- 03Erros discretos
Não exponha caminhos absolutos, stack traces ou avisos de inclusão ao cliente. Envie detalhes apenas para logs protegidos.
- 04Runtime endurecido
Mantenha dependências atualizadas, desative inclusão remota e restrinja o acesso do PHP quando compatível com a aplicação.
- 05Monitoramento
Detecte sequências de traversal, esquemas inesperados e picos de erros, correlacionando-os por origem e endpoint.
- 06Testes contínuos
Inclua casos negativos para traversal, encoding, caminhos absolutos, separadores alternativos e wrappers na suíte de segurança.
Checklist de revisão
A aplicação deve decidir quais recursos podem ser acessados. A entrada do usuário pode selecionar uma opção conhecida, mas nunca construir livremente o caminho que o servidor abrirá.
Simulador e Validador de Payloads LFI
Compare, no navegador, como quatro estratégias fictícias tratam uma entrada. Nenhum arquivo ou servidor real é acessado.
- Entrada recebida
- —
- Caminho processado
- —
Escolha uma estratégia e execute a simulação.
Remover sequências específicas é insuficiente. Prefira identificadores mapeados em allowlist e, quando caminhos forem indispensáveis, normalize-os e confirme que permanecem dentro de uma pasta base confiável.