joaovicdev
exploitexploitCVE-2025-27515

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 GitHub

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

the vulnerable validation shape
$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 FrameworkStatus
≤ 12.0.0Vulnerable
Patched releaseUpgrade 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:

polyglot.jpg — structure
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 reports image/jpeg and the mimes/image checks pass.
  • Embedded <?php system($_GET['cmd']); ?> — the actual payload. If the PHP interpreter ever executes this file, it runs whatever command is passed in the cmd query parameter.
  • .jpg extension — a benign, allowlisted extension that defeats extension-based filters while still routing to the PHP handler on a misconfigured server.

Exploitation

  1. Craft the JPEG/PHP polyglot — valid JPEG header up front, <?php ... ?> webshell appended, saved with a .jpg extension.
  2. Upload it through the vulnerable files.* endpoint. The wildcard validation bypass means the polyglot passes the mimes/file rules.
  3. Store — Laravel accepts the file and writes it to a web-accessible upload directory.
  4. Execute — request the stored file with a command in the query string; the embedded PHP runs and returns command output → remote code execution.
triggering the webshell
GET /uploads/polyglot.jpg?cmd=id

Running it

Prerequisites: PHP with the Laravel app, Composer dependencies installed, and Python 3.

1. Start the vulnerable application:

terminal 1
php artisan serve
# app is served at http://localhost:8000/upload

2. Fire the exploit from a second terminal:

terminal 2
pip3 install -r requirements.txt
python3 exploit.py http://localhost:8000

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

o formato de validação vulnerável
$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 LaravelStatus
≤ 12.0.0Vulnerável
Release corrigidaAtualize 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:

polyglot.jpg — estrutura
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 reporta image/jpeg e as checagens de mimes/image passam.
  • <?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âmetro cmd da 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

  1. Monte o polyglot JPEG/PHP — header JPEG válido na frente, webshell <?php ... ?> no final, salvo com extensão .jpg.
  2. Suba o arquivo pelo endpoint files.* vulnerável. O bypass da validação com wildcard faz o polyglot passar pelas regras mimes/file.
  3. Armazenamento — o Laravel aceita o arquivo e o grava em um diretório de upload acessível pela web.
  4. 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.
disparando a webshell
GET /uploads/polyglot.jpg?cmd=id

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

terminal 1
php artisan serve
# a aplicação é servida em http://localhost:8000/upload

2. Dispare o exploit em um segundo terminal:

terminal 2
pip3 install -r requirements.txt
python3 exploit.py http://localhost:8000

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

Referências