
PoC pour CVE-2026-73847 - emlog AI Assistant : CSRF → exécution SQL → prise de contrôle de l'administrateur (CVSS 6.8)
PoC pour l'absence de protection CSRF sur le point de terminaison execute_tool de l'Assistant IA d'emlog pro, qui permet à un attaquant d'utiliser la session authentifiée d'un administrateur pour exécuter du SQL arbitraire contre la base de données du site — y compris une prise de contrôle complète du compte administrateur.
| CVE | CVE-2026-73847 |
| CNA | GitHub |
| Avis | GHSA-v6wr-4x55-7qp5 |
| CVSS 3.1 | 6.8 Moyen — AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N |
| CWE | CWE-352 (CSRF), CWE-1275 (SameSite incorrect), CWE-798 (Chaîne de confirmation d'écriture codée en dur) |
| Versions affectées | emlog pro jusqu'à 2.6.23 |
| Crédit | Dostxodjayev Abdullox (@squeeze440) — correction en attente dans l'enregistrement CVE, voir ci-dessous |
Le panneau d'administration d'emlog pro inclut un assistant IA capable d'exécuter du SQL au nom de l'administrateur via POST /admin/ai.php?action=execute_tool. Plusieurs problèmes s'accumulent sur ce point de terminaison :
admin/ appellent LoginAuth::checkToken() en premier (ex. admin/media.php:140). admin/ai.php ne le fait jamais.admin/ai.php:152, User::isAdmin()) — aucun contrôle Origin/Referer.include/service/ai.php:594 : if (trim($confirm_code) !== 'confirm'). Toute requête falsifiée envoie simplement confirm_code=confirm.include/service/ai.php:578,589) — une simple requête authentifiée peut SELECT n'importe quelle table.Enchaînés, ces problèmes permettent à une seule requête falsifiée envoyée depuis le navigateur d'un administrateur de lire toutes les tables (y compris les hashs de mots de passe) et d'écrire dans toutes les tables sauf blog, y compris un écrasement direct de user.password.
poc_raw_impact.sh)Isole la primitive de contournement SQL/auth de la question de la livraison CSRF. À exécuter contre une instance locale que vous contrôlez :
./poc_raw_impact.sh http://TARGET admin '<adminpass>'
Ceci se connecte en tant qu'admin, extrait les hashs de mots de passe via le contournement par alias de colonne, écrase directement le mot de passe admin via la table user, puis se reconnecte avec le mot de passe choisi par l'attaquant à partir d'un nouveau cookie jar — prouvant une prise de contrôle totale du compte dès qu'une requête authentifiée atteint le point de terminaison.

poc_csrf.html)Règle la question SameSite avec un vrai navigateur plutôt que de la supposer. Servez poc_csrf.html depuis une origine distincte de la cible (une IP différente suffit — Chrome considère des IP littérales distinctes comme des sites séparés) et faites en sorte qu'un admin connecté l'ouvre dans les deux minutes environ après la connexion :
python3 -m http.server 8888
# then point poc_csrf.html's form action at your target and get it opened
Le formulaire s'envoie automatiquement au chargement, en envoyant en POST un appel falsifié query_database avec confirm_code=confirm en inter-sites.

Vérifié en conditions réelles : le POST inter-sites falsifié transportait le vrai cookie d'authentification de l'admin (sec-fetch-site: cross-site, cookie attaché), a renvoyé 200 {"code":0,"msg":"ok",...}, et la ligne injectée a été confirmée présente via une lecture authentifiée de suivi. La répétition de la requête identique ~48 minutes plus tard avec le même cookie jar désormais vieilli a échoué — aucun cookie n'a été attaché et le serveur a renvoyé une redirection non authentifiée, confirmant que la fenêtre d'environ deux minutes Lax+POST est la vraie contrainte (reflétée dans AC:H).
emlog_options (identifiants SMTP, clés API, etc.).blog, y compris user — écrasement rôle/mot de passe/e-mail, démontré de bout en bout comme prise de contrôle de compte.role=admin ; les writer/editor sont bloqués par User::checkRolePermission(). Il ne s'agit pas d'une élévation de privilèges depuis un rôle inférieur — cela transforme un clic sur un lien malveillant par un admin connecté en compromission totale et silencieuse du site.Ajoutez LoginAuth::checkToken() à execute_tool, définissez SameSite=Strict sur le cookie d'authentification, et remplacez la chaîne statique confirm_code par un véritable jeton à usage unique par session. Détail complet de la remédiation dans l'avis.
Note sur le crédit : GitHub en tant que CNA a publié l'enregistrement CVE sans entrée credits, alors que le GHSA lui-même crédite et accepte le rapporteur. Une demande de correction a été envoyée à [email protected] le 2026-08-16 ; ce README sera mis à jour si l'enregistrement est corrigé.
Publié après que l'avis est devenu public et qu'un CVE a été attribué, à des fins défensives/éducatives. N'exécutez pas ceci contre une instance emlog que vous ne possédez pas ou pour laquelle vous n'êtes pas explicitement autorisé à tester.
bloginclude/service/ai.php:591userpassword (include/service/ai.php:822-828) — SELECT password AS pwd_hash FROM user renvoie le hash brut.SameSite (include/lib/loginauth.php:99), laissant la fenêtre de grâce « Lax+POST » par défaut de Chrome (environ les deux premières minutes après la connexion) comme seule barrière entre cette attaque et une livraison inter-sites fiable.