
PoC for CVE-2026-73847 - emlog AI 어시스턴트 CSRF를 통한 SQL 실행 및 관리자 계정 탈취 (CVSS 6.8)
emlog pro의 AI 어시스턴트 execute_tool 엔드포인트의 CSRF 보호 누락에 대한 PoC입니다. 공격자는 관리자의 인증된 세션에 편승하여 사이트 데이터베이스에 대해 임의의 SQL을 실행할 수 있으며, 완전한 관리자 계정 탈취까지 가능합니다.
| CVE | CVE-2026-73847 |
| CNA | GitHub |
| 권고 | 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 (Hardcoded write-confirmation string) |
| 영향받는 버전 | emlog pro 2.6.23까지 |
| 발견자 | Dostxodjayev Abdullox (@squeeze440) — CVE 레코드 정정 대기 중, 아래 참조 |
emlog pro의 관리자 패널에는 POST /admin/ai.php?action=execute_tool을 통해 관리자를 대신해 SQL을 실행할 수 있는 AI 어시스턴트가 포함되어 있습니다. 이 단일 엔드포인트에 여러 문제가 겹쳐 있습니다:
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" 유예 창(로그인 후 약 2분)만이 이 공격과 안정적인 크로스사이트 전달 사이를 가로막고 있습니다.모두 연결되면: 관리자 브라우저에서 발생한 위조 요청 하나로 모든 테이블(비밀번호 해시 포함)을 읽고, blog를 제외한 모든 테이블에 쓸 수 있으며, user.password를 직접 덮어쓰는 것도 가능합니다.
poc_raw_impact.sh)SQL/인증 우회 프리미티브를 CSRF 전달 문제와 분리하여 검증합니다. 직접 제어하는 로컬 인스턴스에서 실행하세요:
./poc_raw_impact.sh http://TARGET admin '<adminpass>'
이 스크립트는 admin으로 로그인하고, 컬럼 별칭 우회를 통해 비밀번호 해시를 덤프한 다음, user 테이블을 통해 관리자 비밀번호를 직접 덮어쓰고, 새로운 쿠키 저장소에서 공격자가 선택한 비밀번호로 다시 로그인합니다. 즉, 인증된 요청 하나만 엔드포인트에 도달하면 완전한 계정 탈취가 가능함을 증명합니다.

poc_csrf.html)SameSite 문제를 추측이 아닌 실제 브라우저로 검증합니다. 대상과 다른 오리진(다른 IP면 충분합니다 — Chrome은 서로 다른 리터럴 IP를 별개의 사이트로 취급합니다)에서 poc_csrf.html을 서빙하고, 로그인한 관리자가 로그인 후 약 2분 이내에 해당 페이지를 열도록 유도하세요:
python3 -m http.server 8888
# then point poc_csrf.html's form action at your target and get it opened
폼은 로드 시 자동으로 제출되며, 위조된 query_database 호출에 confirm_code=confirm을 담아 크로스사이트로 POST합니다.

실제 환경에서 검증됨: 위조된 크로스사이트 POST는 관리자의 실제 인증 쿠키를 실어 보냈으며(sec-fetch-site: cross-site, 쿠키 첨부됨), 200 {"code":0,"msg":"ok",...}를 반환했고, 주입된 행은 후속 인증 읽기를 통해 존재가 확인되었습니다. 약 48분 후 동일한(이제 오래된) 쿠키 저장소로 동일한 요청을 반복하면 실패했습니다 — 쿠키가 첨부되지 않았고 서버가 비인증 리다이렉트를 반환하여, 약 2분의 Lax+POST 창이 실제 제약 조건임을 확인했습니다(AC:H에 반영됨).
emlog_options의 모든 비밀(SMTP 자격 증명, API 키 등)을 포함한 모든 테이블/컬럼.blog를 제외한 모든 테이블(user 포함)에 대한 전체 데이터베이스 쓰기 — 역할/비밀번호/이메일 덮어쓰기가 가능하며, 계정 탈취로 종단 간 실증되었습니다.role=admin 계정으로 제한됩니다. writer/editor는 User::checkRolePermission()에 의해 차단됩니다. 하위 역할에서의 권한 상승은 아닙니다 — 로그인한 관리자가 악성 링크를 한 번 클릭하면 완전하고 은밀한 사이트 장악으로 이어집니다.execute_tool에 LoginAuth::checkToken()을 추가하고, 인증 쿠키에 SameSite=Strict를 설정하며, 정적 confirm_code 문자열을 세션별 실제 일회용 토큰으로 교체하세요. 전체 수정 상세 내용은 보안 권고에 있습니다.
크레딧 관련 참고: CNA 역할의 GitHub은 GHSA 자체가 신고자를 크레딧에 기재하고 수용했음에도 불구하고, credits 항목 없이 CVE 레코드를 게시했습니다. 2026-08-16에 [email protected]으로 정정 요청을 보냈으며, 레코드가 정정되면 이 README를 업데이트할 예정입니다.
이 도구는 보안 권고가 공개되고 CVE가 배정된 이후에 방어적/교육적 용도로 게시되었습니다. 소유하지 않았거나 명시적으로 테스트 권한을 부여받지 않은 emlog 인스턴스에 대해 실행하지 마십시오.