
PoC para CVE-2026-73847 - CSRF no emlog AI Assistant para execução de SQL e tomada de conta do administrador (CVSS 6.8)
PoC para a falta de proteção contra CSRF no endpoint execute_tool do Assistente de IA do emlog pro, que permite a um atacante aproveitar a sessão autenticada de um administrador para executar SQL arbitrário no banco de dados do site — incluindo a assunção total da conta de administrador.
| CVE | CVE-2026-73847 |
| CNA | GitHub |
| Aviso | GHSA-v6wr-4x55-7qp5 |
| CVSS 3.1 | 6.8 Médio — AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N |
| CWE | CWE-352 (CSRF), CWE-1275 (SameSite impróprio), CWE-798 (String de confirmação de gravação fixa no código) |
| Afetados | emlog pro até 2.6.23 |
| Crédito | Dostxodjayev Abdullox (@squeeze440) — correção pendente no registro do CVE, veja abaixo |
O painel administrativo do emlog pro traz um assistente de IA que pode executar SQL em nome do administrador via POST /admin/ai.php?action=execute_tool. Diversos problemas se acumulam neste único endpoint:
admin/ chamam LoginAuth::checkToken() primeiro (ex.: admin/media.php:140). admin/ai.php nunca chama.admin/ai.php:152, User::isAdmin()) — sem verificação de Origin/Referer.include/service/ai.php:594: if (trim($confirm_code) !== 'confirm'). Qualquer requisição forjada simplesmente envia confirm_code=confirm.include/service/ai.php:578,589) — uma requisição autenticada simples pode executar SELECT em qualquer tabela.blog é protegida contra gravação (include/service/ai.php:591) — user e todas as outras tabelas são totalmente graváveis.password (include/service/ai.php:822-828) — SELECT password AS pwd_hash FROM user retorna o hash bruto.SameSite (include/lib/loginauth.php:99), deixando a janela de carência padrão "Lax+POST" do Chrome (aproximadamente os dois primeiros minutos após o login) como única barreira entre isso e uma entrega cross-site confiável.Encadeados: uma única requisição forjada do navegador de um administrador lê todas as tabelas (incluindo hashes de senha) e grava em todas as tabelas, exceto blog, incluindo uma sobrescrita direta de user.password.
poc_raw_impact.sh)Isola a primitiva de bypass de SQL/auth da questão de entrega via CSRF. Execute contra uma instância local que você controla:
./poc_raw_impact.sh http://TARGET admin '<adminpass>'
Ele faz login como admin, extrai os hashes de senha via bypass por alias de coluna, sobrescreve a senha do admin diretamente na tabela user e então faz login novamente com a senha escolhida pelo atacante a partir de um novo cookie jar — provando a assunção total da conta quando uma única requisição autenticada chega ao endpoint.

poc_csrf.html)Resolve a questão do SameSite com um navegador real, em vez de supor. Sirva o poc_csrf.html de qualquer origem distinta do alvo (um IP diferente já basta — o Chrome trata IPs literais distintos como sites separados) e faça um administrador autenticado abri-lo dentro de aproximadamente dois minutos após o login:
python3 -m http.server 8888
# then point poc_csrf.html's form action at your target and get it opened
O formulário é enviado automaticamente ao carregar, fazendo POST de uma chamada query_database forjada com confirm_code=confirm entre sites.

Verificado ao vivo: o POST forjado entre sites carregou o cookie de autenticação real do administrador (sec-fetch-site: cross-site, cookie anexado), retornou 200 {"code":0,"msg":"ok",...}, e a linha injetada foi confirmada como presente por meio de uma leitura autenticada subsequente. Repetir a mesma requisição cerca de 48 minutos depois contra o mesmo cookie jar, agora envelhecido, falhou — nenhum cookie foi anexado e o servidor retornou um redirecionamento não autenticado, confirmando que a janela de aproximadamente dois minutos do Lax+POST é a restrição real (refletida em AC:H).
emlog_options (credenciais SMTP, chaves de API, etc.).blog, incluindo user — sobrescrita de papel/senha/email, demonstrada de ponta a ponta como assunção de conta.role=admin; writer/editor são bloqueados por User::checkRolePermission(). Não é escalada de privilégio a partir de um papel inferior — transforma um único clique em link malicioso por um admin autenticado em comprometimento total e silencioso do site.Adicione LoginAuth::checkToken() ao execute_tool, defina SameSite=Strict no cookie de autenticação e substitua a string estática confirm_code por um token real de uso único por sessão. Detalhes completos de remediação no aviso.
Nota sobre o crédito: o GitHub como CNA publicou o registro do CVE sem uma entrada credits, apesar de o próprio GHSA creditar e aceitar o relator. Uma solicitação de correção foi enviada para [email protected] em 2026-08-16; este README será atualizado se o registro for corrigido.
Publicado depois que o aviso se tornou público e um CVE foi atribuído, para uso defensivo/educacional. Não execute isto contra uma instância do emlog que você não possua ou para a qual não esteja explicitamente autorizado a testar.