
CVE-2026-86950을 악용하는 PDF 파일을 탐지하는 Python 도구
이 프로젝트와 CVE-2026-86950에 대한 자세한 내용은 https://decalage.info/CVE-2026-86950/ 를 참조하세요.
면책 조항: 이 readme의 대부분과 탐지 코드는 AI 코딩 에이전트의 도움을 받아 작성되었습니다. 이 도구는 수천 개의 정상적인 PDF 파일과 여기에 공개된 사용 가능한 개념 증명(proof-of-concept) 파일에서 성공적으로 테스트되었습니다. 그러나 이 도구는 새롭고 탐지 알고리즘이 상당히 복잡하므로 주의해서 사용하시기 바랍니다.
detect_cve_2026_86950.py는 CVE-2026-86950("The Great Glyph Grift")을 익스플로잇하도록 조작된 내장 폰트가 포함된 PDF 파일을 정적으로 스캔합니다. 이 취약점은 Apple CoreGraphics의 앤티앨리어싱 글리프 래스터라이저에서 발생하는 범위를 벗어난 쓰기(out-of-bounds write)이며, iOS / macOS 26.7.1에서 수정되었습니다.
이 도구는 폰트를 렌더링하거나 익스플로잇 코드를 실행하지 않습니다. 대신 PDF와 내장 폰트 프로그램을 파싱하여 공격의 구조적 서명을 찾습니다.
CoreGraphics는 부동 소수점 장치 좌표를 대략 다음과 같은 방식으로 16.12 고정 소수점 정수로 변환합니다.
int32_t aa_double_to_fixed(double px) { return (int32_t)(0.5 + 4096.0 * px); }
4096 * px가 int32 범위를 초과하면 캐스트는 정의되지 않은 동작이 되며, 두 호출 지점(aa_moveto vs. aa_lineto)이 서로 다르게 동작합니다 — 하나는 포화되고, 하나는 잘립니다. 이러한 불일치는 aa_add_edges의 글리프 경계 상자 계산을 손상시켜, aa_cache_render의 커버리지 버퍼가 너무 작게 할당되고 래스터라이저가 범위를 벗어나 기록하게 됩니다. 26.7.1 패치는 ±0x3FFFF001로의 명시적 클램핑을 추가합니다.
공개된 개념 증명은 이러한 과대 좌표를 강제하는 악성 TrueType 폰트를 구성합니다:
/Tf 1024)에서 4096 × device_coord가 int32를 오버플로합니다./FontFile2) 및 CFF / OpenType(/FontFile3)./Tf, Tm)와 결합하고 4096 × device_coord가 실제로 INT32_MAX를 초과하는지 — 즉 고정 소수점 오버플로에 도달 가능한지 — 확인합니다.판정: exploit (오버플로 도달 가능), suspicious (과대 기하이지만 관찰된 크기에서 오버플로하지 않음), clean.
# with uv (recommended)
uv venv
uv pip install -r requirements.txt
# or any environment with pikepdf + fonttools
python3 detect_cve_2026_86950.py suspicious.pdf
python3 detect_cve_2026_86950.py -v *.pdf # per-font details
python3 detect_cve_2026_86950.py --json f.pdf # machine-readable
종료 코드: 0 정상, 1 익스플로잇/의심 발견, 2 오류.
aa_* 경로 래스터라이저에서의 고정 소수점 변환으로, 이 래스터라이저는 TrueType glyf와 CFF/Type2 외곽선을 모두 렌더링합니다. 공개된 PoC는 TrueType 복합 글리프를 사용하여 좌표를 증폭시키며, CFF 폰트는 상대 연산자를 누적하는 등의 방식으로 과대한 좌표에 직접 도달할 수 있으므로 /FontFile2와 /FontFile3 모두 분석됩니다. 복합 체인 휴리스틱은 TrueType에 특화되어 있으며, CFF의 경우 신호는 과대 좌표/경계 상자 거짓말 검사입니다./Tf, Tm) 스캔은 최선의 노력 방식이며, PDF 그래픽 상태/CTM을 완전히 평가하지는 않습니다. 크기를 찾지 못한 경우 오버플로 검사에는 보수적인 기본값 1024가 가정됩니다. 폰트 고유의 과대 기하 신호는 이에 의존하지 않습니다.