
PoC для CVE-2026-73847 - emlog ИИ-ассистент: от CSRF до выполнения SQL и захвата администратора (CVSS 6.8)
PoC для отсутствующей CSRF-защиты в конечной точке execute_tool ИИ-ассистента emlog pro, которая позволяет атакующему использовать аутентифицированную сессию администратора для выполнения произвольного SQL по базе данных сайта — включая полный захват учётной записи администратора.
| CVE | CVE-2026-73847 |
| CNA | GitHub |
| Advisory | GHSA-v6wr-4x55-7qp5 |
| CVSS 3.1 | 6.8 Medium — AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N |
| CWE | CWE-352 (CSRF), CWE-1275 (Improper SameSite), CWE-798 (жёстко заданная строка подтверждения записи) |
| Affected | emlog pro до 2.6.23 включительно |
| Credit | Dostxodjayev Abdullox (@squeeze440) — ожидается исправление в записи CVE, см. ниже |
В админ-панели emlog pro есть ИИ-ассистент, который может выполнять SQL от имени администратора через POST /admin/ai.php?action=execute_tool. В этой единственной конечной точке сходится несколько проблем:
admin/ сначала вызывают LoginAuth::checkToken() (например, admin/media.php:140). admin/ai.php — никогда.admin/ai.php:152, User::isAdmin()) — никакой проверки Origin/Referer.include/service/ai.php:594: if (trim($confirm_code) !== 'confirm'). Любой поддельный запрос просто отправляет confirm_code=confirm.include/service/ai.php:578,589) — голый аутентифицированный запрос может выполнить SELECT из любой таблицы.blog (include/service/ai.php:591) — user и все остальные таблицы полностью доступны для записи.password (include/service/ai.php:822-828) — SELECT password AS pwd_hash FROM user возвращает исходный хеш.SameSite (include/lib/loginauth.php:99), поэтому между этой уязвимостью и надёжной межсайтовой доставкой остаётся лишь стандартное окно Chrome "Lax+POST" (примерно первые две минуты после входа).Совместно: один поддельный запрос из браузера администратора читает все таблицы (включая хеши паролей) и пишет во все таблицы, кроме blog, включая прямую перезапись user.password.
poc_raw_impact.sh)Изолирует примитив SQL/обхода аутентификации от вопроса доставки CSRF. Запускайте на локальном экземпляре, которым управляете:
./poc_raw_impact.sh http://TARGET admin '<adminpass>'
Скрипт входит как администратор, выгружает хеши паролей через обход через алиас столбца, напрямую перезаписывает пароль администратора через таблицу user, затем снова входит с выбранным атакующим паролем из свежего cookie-хранилища — доказывая полный захват учётной записи, как только один аутентифицированный запрос достигает конечной точки.

poc_csrf.html)Решает вопрос SameSite с помощью живого браузера, а не предположений. Разместите poc_csrf.html в любом origin, отличном от целевого (достаточно другого IP — Chrome считает разные буквальные IP отдельными сайтами), и добейтесь, чтобы вошедший администратор открыл его в течение примерно двух минут после входа:
python3 -m http.server 8888
# then point poc_csrf.html's form action at your target and get it opened
Форма автоматически отправляется при загрузке, выполняя межсайтовый POST-запрос поддельного вызова query_database с confirm_code=confirm.

Проверено вживую: поддельный межсайтовый POST-запрос передал настоящий auth-cookie администратора (sec-fetch-site: cross-site, cookie прикреплён), вернул 200 {"code":0,"msg":"ok",...}, а наличие внедрённой строки было подтверждено последующим аутентифицированным чтением. Повторение того же запроса примерно через 48 минут с тем же, уже состарившимся cookie-хранилищем не удалось — cookie не был прикреплён, и сервер вернул редирект на аутентификацию, что подтверждает: примерно двухминутное окно Lax+POST — это реальное ограничение (отражено в AC:H).
emlog_options (учётные данные SMTP, ключи API и т.д.).blog, включая user — перезапись роли/пароля/email, продемонстрированная целиком как захват учётной записи.role=admin; writer/editor блокируются User::checkRolePermission(). Это не повышение привилегий с более низкой роли — один вредоносный клик по ссылке вошедшим администратором превращается в полную и незаметную компрометацию сайта.Добавьте вызов LoginAuth::checkToken() в execute_tool, установите SameSite=Strict для auth-cookie и замените статическую строку confirm_code на настоящий одноразовый токен, действующий для конкретной сессии. Полные детали устранения — в рекомендации.
Примечание об авторстве: GitHub как CNA опубликовал запись CVE без поля credits, несмотря на то что сам GHSA указывает автора и принял отчёт. Запрос на исправление был отправлен на [email protected] 16.08.2026; этот README будет обновлён, если запись исправят.
Опубликовано после того, как рекомендация стала публичной и был присвоен CVE, для защитных/образовательных целей. Не запускайте это против экземпляра emlog, которым вы не владеете или на тестирование которого у вас нет явного разрешения.