Pular para o conteúdo
Categoria: Pentest9 min de leitura

Explorando Vulnerabilidades de Upload de Arquivos sem Quebrar a Lei

Por Lucas Andrade ·

Como bypassar validacoes de upload em lab proprio, mapear classes de falhas e endurecer webservers contra RCE via arquivo malicioso.

Explorando Vulnerabilidades de Upload de Arquivos sem Quebrar a Lei

Um endpoint de upload mal configurado ainda e uma das formas mais baratas de transformar um formulario humilde em execucao remota de codigo. Num lab montado com DVWA, OWASP Juice Shop e um reverse proxy Nginx 1.25, saimos de 'upload de avatar' para uma shell www-data em menos de 40 minutos. Antes de qualquer payload, o contrato importa: o alvo e a nossa propria VM, isolada numa rede 10.10.0.0/24 sem rota para a internet, e o objetivo e escrever regras endurecidas, nao colecionar escalpos. Este passo a passo cobre toda a superficie de ataque de upload de arquivos, cada classe de bug com vetor, oraculo e o fix que a fecha de vez.

O contrato: apenas lab autorizado

Cada tecnica aqui e demonstrada contra infraestrutura que possuimos e estamos explicitamente autorizados a quebrar. Replica-las contra um SaaS de producao 'so para conferir' nao e bug bounty, e crime sob a maioria das leis de abuso informatico, e a diferenca entre pesquisador e reu costuma ser um escopo escrito e um snapshot de VM. Monte o lab como descreve pentest web do zero: Docker numa bridge isolada, sem rota ao host, Burp Suite como proxy interceptador. Tire snapshot antes de cada rodada para provar exatamente o que voce tocou e reverter limpo.

Bypass do lado cliente e de blacklist de extensao

A primeira classe de bug e a validacao de extensao do lado cliente. O PHP legado costuma trazer uma blacklist (.php, .phtml, .php5) sem verificacao real de Content-Type. Renomear shell.php para shell.pHp.jpg, interceptar com Burp e forcar o Content-Type para image/jpeg vence cerca de 80% dos filtros amadores vistos em CTFs corporativos. Misturar maiusculas vence blacklists ingenuas em minusculas; pontos e espacos finais (shell.php.) vencem as orientadas a Windows; e handlers alternativos como .phtml, .phar ou .pht muitas vezes executam onde .php esta bloqueado. O oraculo e simples: peca o caminho enviado e veja se o servidor devolve sua string marcador ou o codigo-fonte cru. Se executa, a blacklist era o unico controle e falhou.

Confusao de Content-Type e MIME

A segunda camada e confiar no tipo MIME declarado pelo cliente. Um backend que checa apenas o header Content-Type do multipart e trivialmente contornado, porque esse header e controlado pelo atacante; mude para image/png e um payload PHP passa. Backends um pouco melhores chamam getimagesize() ou farejam os primeiros bytes, o que eleva a barra mas nao a supera. O oraculo correto no teste e separar tipo declarado de tipo real: envie uma extensao de imagem genuina com conteudo executavel, depois um header de imagem falso na frente de codigo, e observe qual combinacao o servidor armazena e serve como executavel. Qualquer caminho onde o tipo declarado sozinho decide a aceitacao e um achado que vale reportar.

Parsing de caminho: dupla extensao e null bytes

Uma superficie classica e o parsing de caminho do servidor. O antigo double-parsing do Nginx (CVE-2013-4547 e descendentes) deixava um arquivo chamado shell.jpg seguido de um null byte e .php enganar stacks que combinam Nginx e PHP-FPM com um pathinfo desleixado. No lab reconstrui isso com nginx:1.14-alpine e php:7.2-fpm apenas como referencia historica; com Nginx 1.25 e cgi.fix_pathinfo=0 o mesmo payload morre. A licao: a seguranca de upload nao e decidida pela aplicacao sozinha; o servidor web, o handler FastCGI e o ajuste fix_pathinfo decidem juntos se um diretorio de imagens pode executar codigo. Este exercicio combina com a mentalidade de parsing de entrada de SQL injection na pratica.

Magic bytes, polyglots e o pipeline de imagem

Quando o backend valida magic bytes, o jogo muda. Polyglots GIF/PHP vencem getimagesize() mas falham contra finfo mais Imagick com reprocessamento, entao o truque e atacar o pipeline de transformacao em vez do validador. ImageMagick com policies frouxas ainda parseia MVG e SVG, e a familia ImageTragick ressurge em forks de LMS e CMS. Num cliente fintech encontrei um upload de recibo chamando convert sem limites de memoria nem de delegates, o que transformou um SVG num SSRF e depois numa leitura de arquivo. Se o pipeline busca recursos remotos, o upload vira uma primitiva de request forgery, por isso essa classe se liga direto a SSRF e cloud metadata. Reprocesse cada imagem, desative coders perigosos e nunca deixe convert seguir uma URL.

Onde o arquivo cai: armazenamento, traversal, servir

Uma superficie subestimada e onde o arquivo cai. Armazenar em /var/www/uploads servido direto pelo Apache e o classico, mas o mesmo problema se esconde em buckets S3 com Content-Disposition errada e em CDNs que reprocessam HTML. O path traversal no nome do arquivo (sequencias ponto-ponto-barra mirando /tmp/cron.d/ ou um webroot) ainda funciona contra middleware Node que confia no originalname do Multer. O oraculo: envie um marcador benigno com nome de traversal e verifique onde ele se materializa em disco; se escapa do diretorio previsto, o atacante escolhe o local de escrita. Para um plano de teste mais formal por endpoint, sobretudo em APIs, siga a checklist de pentest REST e GraphQL.

Fuzzing metodico do endpoint

Os bypass manuais acham o primeiro bug; o fuzzing metodico acha o resto. Aponte o Burp Intruder ou um pequeno script para o campo de upload e itere tres dimensoes ao mesmo tempo: a extensao (uma wordlist de .php, .phtml, .phar, .pht, .php7, .inc, mistura de maiusculas, ponto e espaco finais), o Content-Type declarado e o prefixo de magic bytes. Para cada combinacao, o oraculo e uma sonda de dois passos: o servidor aceita o arquivo, e pedi-lo de volta executa o marcador. Nao esqueca a race condition de upload, em que uma webshell fica brevemente alcancavel entre a escrita e o passo de antivirus ou de move; um loop apertado de enviar-e-pedir pode ganhar essa janela num alvo real e precisa ser testado explicitamente para justificar o fix (validar antes de o arquivo ser alcancavel).

Encadear um upload ate o comprometimento total

Um unico marcador executado e um achado, mas o relatorio deve mostrar impacto. No lab, o polyglot GIF/PHP deu uma webshell minima de comandos; dali uma reverse shell padrao (um callback bash TCP para a VM atacante na rede isolada) elevou o apoio a uma sessao interativa www-data em segundos. A escalada realista dali nao e um exploit de Hollywood, e persistencia chata: um diretorio gravavel servido pelo servidor web, um caminho de cron world-writable alcancavel pelo traversal anterior, ou uma credencial de banco vazada num arquivo de config que a shell agora consegue ler. Documente a cadeia ate o impacto provado e pare; o objetivo e justificar a nota de severidade, nao saquear uma maquina, e o snapshot deixa voce reexecutar toda a cadeia para o cliente sem deixar dano.

Regras defensivas que de fato funcionam

Do lado defensivo, as regras que ganham lugar num relatorio sao chatas e eficazes. Gere um nome de arquivo aleatorio (UUIDv7) e descarte por completo o nome do cliente; armazene fora do document root; sirva por um handler que forca Content-Disposition: attachment e Content-Type: application/octet-stream; valide o MIME com libmagic do lado servidor, nao o header declarado; reprocesse imagens e remova metadados; e bloqueie dupla extensao no proprio servidor web, por exemplo negando toda requisicao cujo caminho contenha .php, .phtml ou .phar antes de outro ponto. Some uma passagem de antivirus e um teto de tamanho, e ponha tudo atras dos gates shift-left de AppSec shift-left para que uma regressao caia no CI, nao em producao.

Checklist

Antes de liberar uma funcionalidade de upload: nome de arquivo gerado pelo servidor e que nunca reflita entrada do usuario; extensao em allowlist, nao blacklist; MIME verificado por conteudo, nao por header; armazenamento fora do webroot e servido com disposicao attachment; imagens reprocessadas e metadados removidos; o servidor web nao executa nada no caminho de upload; path traversal no nome neutralizado; existem limites de tamanho e de taxa; e um antivirus ou scanner de conteudo roda de forma assincrona. Cada 'nao' dessa lista e um achado, e cada achado recebe uma linha concreta de remediacao, nao um vago 'sanitize a entrada'.

FAQ

Bloquear .php basta para estar seguro? Nao; uma blacklist e o controle mais fraco possivel, vencido por mistura de maiusculas, handlers alternativos (.phtml, .phar, .pht), pontos finais e peculiaridades do parsing de caminho do servidor. O controle duradouro e uma allowlist de extensoes combinada com validacao MIME por conteudo e um servidor web que jamais executa codigo no diretorio de upload. Posso permitir uploads SVG com seguranca? So com muita cautela: SVG e XML e pode carregar script e referencias externas, entao sirva-o com uma Content-Security-Policy restritiva e disposicao attachment, ou rasterize para PNG no recebimento e descarte o original. Tratar SVG como imagem inofensiva e como ImageTragick e o XSS armazenado continuam caindo.

Os uploads devem viver no mesmo host que serve a app? Prefira um dominio de armazenamento separado sem execucao de codigo e um handler de servico trancado, para que ate uma escrita bem-sucedida nao vire RCE no host da aplicacao. E como testo sem arriscar dano? Tire snapshot da VM, trabalhe apenas dentro do escopo autorizado, use payloads marcadores benignos que provem o bug (uma string unica, o proprio id da VM) em vez de exfiltrar dados reais, e reverta apos cada rodada; provar a escrita basta, ler /etc/passwd na maquina de outrem e desnecessario e ilegal.

Conclusao

Upload de arquivos segue perigoso porque fica na costura de tres sistemas, a aplicacao, o servidor web e a camada de armazenamento, e uma brecha em qualquer um basta. Ataque-o como este lab faz, classe por classe, com um oraculo claro para cada, e as regras defensivas se escrevem sozinhas: nomes gerados pelo servidor, extensoes em allowlist, checagens MIME por conteudo, imagens reprocessadas, sem execucao no caminho de upload e traversal neutralizado. Mantenha cada experimento dentro do escopo autorizado com um snapshot que o prove, entregue as regras endurecidas pelo CI, e o humilde formulario de avatar deixa de ser um caminho de 40 minutos ate uma shell.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly