
Um pesquisador de segurança ofensivo + uma IA vs. um n-day fresco: construindo o primeiro PoC público para CVE-2026-53435 em uma sexta-feira à noite. Log bruto de 8h20m incluso.
Primeira prova de conceito pública para CVE-2026-53435, construída quando apenas o aviso existia e nenhum PoC havia sido publicado em lugar nenhum. Reconstruída a partir de um aviso vago de uma linha ao longo de ~8,5 horas em uma sexta à noite — não escrevendo o exploit manualmente, mas orientando uma IA pelos becos sem saída, pivôs e verificação até que uma cadeia funcional surgisse.

O PoC acessando
/etc/passwdno controlador — leitura arbitrária de arquivos confirmada.
| CVE | CVE-2026-53435 (Jenkins SECURITY-3707) |
| Classe | Desserialização insegura (bypass do ClassFilter via config.xml) |
| Impacto (este PoC) | Leitura arbitrária de arquivos autenticada no controlador |
| Afetado | Jenkins weekly ≤ 2.567, LTS ≤ 2.555.2 |
| Corrigido | Jenkins weekly 2.568, LTS 2.555.3 |
| Aviso | https://www.jenkins.io/security/advisory/2026-06-10/ (publicado em 2026-06-10) |
| Status no momento da escrita | Nenhum PoC público existia (repositórios PoC no GitHub: 0) |
Este é um experimento para mostrar o processo, não apenas o artefato.
Por anos, a produção de um pesquisador ofensivo era um único exploit finalizado — as 8 horas de caminhos errados que o produziram desapareciam. Este repositório mantém os caminhos errados. A transcrição completa e levemente editada de um humano + IA trabalhando um n-day fresco desde o aviso até um PoC funcional está incluída como apêndice.
O ponto que ele defende é deliberadamente não "a IA fez isso." As 8 horas são a evidência de que não fez. A IA nunca acertou a cadeia de uma vez. O que carregou o trabalho foi o conhecimento de domínio — saber que o aviso estava escondendo um sink de desserialização, saber procurar em tipos core do Jenkins em vez de plugins, saber que um DescribableList pré-patch não aplicaria seu tipo de elemento, e saber como verificar uma afirmação em vez de confiar nela. O valor do pesquisador não desapareceu; ele mudou de digitar o exploit para orientar, podar e validar.
Essa mudança é a coisa que vale a pena documentar.
O Jenkins protege a desserialização com um ClassFilter personalizado que permite apenas tipos definidos no core ou plugins do Jenkins. O CVE-2026-53435 é que isso não é suficiente: um invasor que possa fazer POST de um config.xml pode fazer com que o Jenkins desserialize um tipo arbitrário de core/plugin em um contexto que nunca o esperava, e então alcançar esse objeto via HTTP através do roteamento Stapler.
Este PoC planta um hudson.Plugin$DummyImpl (um tipo do core do Jenkins, então passa na lista de permissões de localização) na lista <properties> de um ListView — um DescribableList<ViewProperty> que, pré-patch, não aplica seu tipo de elemento. O objeto plantado carrega baseResourceURL=file:/. Rotear uma requisição HTTP para ele então serve arquivos diretamente do sistema de arquivos do controlador.
Uma cadeia ysoserial genérica não funciona aqui — o gadget deve ser um tipo residente no Jenkins/plugin para sobreviver ao filtro. Essa restrição é todo o jogo.
Use apenas contra sistemas que você possui ou está explicitamente autorizado a testar.
O exploit foi executado contra dois contêineres de configuração idêntica — vulnerável 2.555.2 e corrigido 2.555.3 — para confirmar que ele exerce este CVE e não alguma primitiva de leitura de arquivos não relacionada:
A build corrigida aceita a requisição, mas a correção rejeita o tipo plantado no momento da desserialização, então a lista de propriedades retorna vazia e não há nada para rotear. A falha atinge exatamente o mecanismo que o aviso descreve — o que faz deste um PoC genuíno para o CVE-2026-53435.
python3 exploit_cve_2026_53435_v2.py <base_url> <user> <pass> <remote_file> [view_name]
# example (against your own lab):
python3 exploit_cve_2026_53435_v2.py http://127.0.0.1:8080 <user> <pass> /etc/passwd
base_url — http://host:port do alvo (HTTP simples; não prefixe com https a menos que a instância realmente termine TLS).config.xml de view (View/Configure), além de Overall/Read.docker compose -f lab/docker-compose.yml up -d
# vulnerable: http://127.0.0.1:8080 (Jenkins LTS 2.555.2)
# patched: http://127.0.0.1:8081 (Jenkins LTS 2.555.3, negative control)
O laboratório provisiona um banco de dados de usuários apenas local via Groovy init para que o caminho autenticado de baixo privilégio possa ser exercido sem um IdP externo.
2026-06-12 (Fri) 15:00 → 23:23 ≈ 8h 20m
15:00–15:12 Lab built (docker: vuln 2.555.2 / patched 2.555.3) + first recon
15:12–21:00 Deserialization sink & gadget analysis
(Commons-Collections bypass attempts failed → pivot
from properties to actions and back)
21:00–22:30 ClassFilter analysis + gadget re-hunt, restricted to core types
22:30–23:00 Working PoC (v1 → v2) + differential verification (vuln vs patched)
A maioria dessas horas foi análise, não digitação. O exploit propriamente dito se juntou nos últimos ~30 minutos — uma vez que o sink e gadget corretos foram conhecidos.
Construído com Claude Code (Anthropic), conduzido interativamente pelo autor:
A transcrição completa e levemente editada está em transcript/ — mantida crua, incluindo os becos sem saída. (Editada apenas para suavizar algumas linhas de tom e mascarar detalhes da rede local; nada técnico foi removido.)
Apenas para testes de segurança autorizados, pesquisa e educação. O autor e contribuidores não aceitam responsabilidade por uso indevido. A vulnerabilidade alvo foi corrigida — atualize para Jenkins LTS 2.555.3 / weekly 2.568 ou posterior.
| 2.555.2 (vulnerável) | 2.555.3 (corrigido) |
|---|
createView | HTTP 200 | HTTP 200 |
<properties> armazenado | <hudson.Plugin_-DummyImpl/> — sobrevive | <properties/> — removido |
Disparo → /etc/passwd | root:x:0:0:... — lido | vazio — bloqueado |