
CVE-2026-13233 (Drupal OpenAI Provider, SA-CONTRIB-2026-053): response-URL SSRF / 로컬 파일 읽기. 신뢰할 수 없는 업스트림, 프롬프트가 아님. 안전한 재현기 + 탐지. 1.1.1/1.2.2에서 수정됨.
신뢰할 수 없는 업스트림: 응답이 지시한 URL을 가져온(fetch) 프로바이더.
Drupal OpenAI Provider(ai_provider_openai) · SSRF(CWE-918) · CVE-2026-13233 · SA-CONTRIB-2026-053
상태: 조율된 공개 완료(2026-07-10). Drupal 보안 팀에 비공개로 보고되었고, 수정 후 CVE-2026-13233 / 권고 SA-CONTRIB-2026-053으로 공개되었습니다(Moderately critical 등급). 작성자는 **발견자 겸 수정 개발자(finder & remediation developer)**로 기재되었습니다.
AI '연결 지점(connection point)'에서 발생한 전형적인 권한 부여 / 입력 검증 결함에 대한 연구입니다:
AI 이미지 생성 프로바이더가 스킴 허용 목록(scheme allowlist)이 없는 범용 함수를 사용하여
업스트림 API 응답에 포함된 URL을 가져왔습니다. 구성된 업스트림이 신뢰할 수 있는 기본값이 아닌
배포 환경(자체 엔드포인트 사용(bring-your-own-endpoint) / 프록시 / 자체 호스팅 게이트웨이 / 손상된
릴레이)에서는 응답이 서버를 file:// 또는 내부 주소로 향하게 할 수 있었습니다.
싱크(sink)에 도달하는 신뢰할 수 없는 입력은 업스트림 응답 URL이지 사용자의 프롬프트가 아닙니다.
file:// 로컬 파일 읽기가 포함되었으며,
작성자의 내부 분석에서는 CWE-73 / CWE-441에도 매핑되었습니다.3.1 / 조건부 5.3
(Medium); 이전의 6.5 / 7.4는 철회되었으며, 중간 등급 평가가 이를 뒷받침했습니다.ai_provider_openai) < 1.1.1
및 1.2.0–1.2.1(1.2.1 감사 완료)모든 검증은 직접 소유한 네트워크 격리 샌드박스에서 **모의 업스트림(mock upstream)**을 대상으로 수행되었습니다. 실제 OpenAI API는 호출되지 않았습니다. 누출 오라클(leak oracle)은 합성 데이터뿐입니다.
확인됨(동적으로 시연됨):
settings.php 읽기는 sha256 바이트 일치(35,182B)로 확인 — 콘텐츠는 출력되지 않았으며,
따라서 DB 자격 증명이 범위에 포함됩니다.미검증(명시적으로 주장하지 않음):
영향 받지 않음: TLS를 통한 기본 api.openai.com. host 설정은 관리자 / 설정 동기화(config-sync) /
배포가 제어하므로 — 런타임의 비관리자 공격자는 이를 변경할 수 없습니다.
응답 URL이 모든 등록된 PHP 스트림 래퍼(file, php, http, data, phar, …)를 허용하는
범용 fetch에 전달되었습니다. response_format은 기본값이 url이므로 취약한 분기가 기본 경로입니다.
file://는 PHP의 기본 파일시스템 래퍼이며 allow_url_fopen의 적용을 받지 않습니다
(이전의 'allow_url_fopen 필요' 주장은 틀렸으며 철회되었습니다).
호출 지점뿐만 아니라 프리미티브(primitive) 자체를 수정하십시오:
http/https인지 검증하고, 리디렉션을 비활성화하십시오(30x 응답이
file://로 튈 수 있음).file://…, http://169.254.169.254/…, 302→file://
리디렉션이 모두 거부되어야 합니다.전체 방향: docs/fix-direction.md.
.
├── README.md # this file (English, canonical)
├── README_ja.md # Japanese mirror
├── SECURITY.md # reporting policy + responsible-disclosure disclaimer
├── .gitignore # blocks secrets / primary evidence from being committed
├── docs/
│ └── fix-direction.md # root-cause fix + variant hunt + minimal-diff sketch (EN)
├── reproducer/ # SAFE reproducer only (sandbox, mock upstream, benign oracle)
│ ├── README.md # how to stand up the network-isolated sandbox
│ ├── verdict.md # what "success" means: raw-byte match + 200-vs-403 + exit codes
│ ├── mock-openai-server.py # benign mock; default happy-path PNG, ssrf_demo → file:///etc/hostname only
│ └── docker-compose.yml # TEMPLATE: isolated Drupal sandbox + mock (you add the vulnerable target)
├── detections/
│ ├── README.md
│ ├── sigma/web-egress-to-internal-after-imagegen.yml
│ ├── sigma/php-sensitive-file-open.yml
│ ├── sentinel/imagegen-egress-correlation.kql
│ └── splunk/imagegen-content-type-mismatch.spl
└── timeline.md # disclosure timeline (single source of truth)
문서는 현재 일본어(
.ja.md)이며, 영어 정식 번역은 TODO입니다. 이 저장소에서 제외된 것: 무장(arming) 페이로드 및 즉시 사용 가능한 파일 읽기 PoC. 공개된reproducer/는 단일 무해 오라클(/etc/hostname)을 사용해 모의 서버를 대상으로 결함 메커니즘을 시연합니다 — 임의 경로 읽기,settings.php탈취, IMDS는 없습니다. 실사용 사이트를 공격하는 도구가 아닙니다. 1.1.1 / 1.2.2로 업데이트하십시오.
목표: 실사용 대상 익스플로잇이나 비밀 노출 없이 연구자가 결함 *유형(class)*을 확인할 수 있게 합니다. 모든 과정이 오프라인으로 실행됩니다.
docker compose(언어 프리미티브 검사는
--network none). 자격 증명은 마운트되지 않습니다. 모의 서버는 실제 API와 통신하지 않습니다.host' 역할을 합니다. 합성 대상 URL을 반환하도록 **대역 외(out-of-band)**로
무장합니다(프롬프트 경유가 아님) — 이는 수정된 공격자 모델(업스트림이 URL을 선택하고 사용자는
트리거만 함)을 반영합니다.file:///etc/hostname 및 앱 컨테이너 내부에만 기록된 실행별 고유 마커 → 앱이
업스트림이 선택한 경로를 읽었음을 입증(마커는 모의 서버에 없음).settings.php는 sha256 바이트 일치로 검증되며 콘텐츠는 출력되지 않음 → 노출 없이
'DB 자격 증명이 범위 내'임을 입증.0 악용 가능 /
1 사전 조건 불충족 / 2 재현 안 됨 / 3 정리 실패 / 99 오류).
를 참조하십시오.reproducer/README.md를 참조하십시오.
timeline.md를 참조하십시오. 요약:
실무자용 탐지는 detections/에 있으며, 근거는
docs/detection-engineering.md에 있습니다. 간단히 요약하면:
169.254.169.254 / 루프백 / RFC1918로의 비정상
이그레스(egress)(프록시 / EDR)와 이미지 생성을 트리거한 주체(앱 감사)를 상호 연관시키십시오.
이그레스만으로 알림을 발생시키지 마십시오.file:// 읽기는 네트워크에 닿지 않으므로 — 호스트 감사(auditd/EDR)를 통해 웹 프로세스의 민감
파일 열기를 탐지하되, 베이스라인을 설정하여 이미지 생성 요청 내에서 패턴을 벗어난 읽기만
발화(fire)하도록 하십시오.image/*로 선언된 바이트를 소급하여 탐지하십시오.{request_id, actor, target_url, scheme, resolved_ip, content_type} 형식의 fetch 감사
레코드를 추가하십시오. 표준 로그만으로는 이를 포착할 수 없습니다.file://는 기본 파일시스템 래퍼이며 allow_url_fopen의 적용을 받지 않음)reproducer/는 샌드박스 전용이며 합성 오라클을 사용합니다. 실사용 대상 익스플로잇이
아니며 비밀 정보를 포함하지 않습니다. 소유하지 않았거나 테스트 권한이 없는 시스템에 대해
실행하지 마십시오.모든 연구는 작성자가 직접 소유한 네트워크 격리 샌드박스에서 모의 업스트림을 대상으로 수행되었으며 —
실제 벤더 API에 접촉한 적이 없고, 제3자 시스템을 테스트한 적도 없습니다. 이 발견은 먼저 Drupal
보안 팀에 비공개로 보고되었으며, 수정 및 권고(SA-CONTRIB-2026-053, CVE-2026-13233)와 함께
공개되었습니다. 벤더의 등급은 Moderately critical입니다. 작성자의 CVSS 수치는 잠정적인 자체
평가일 뿐 공식 점수가 아니며, 의도적으로 보수적으로 유지되었습니다(CVSS Critical/High 없음) —
이는 벤더의 중간대역 등급과 일치합니다. 이 자료는 방어 및 교육 목적으로 제공됩니다.
SECURITY.md를 참조하십시오.
reproducer/verdict.md| 날짜 | 사건 |
|---|
| 2026-06-10 | 정적 검토로 싱크 위치 확인; 고정 경로 file:// 읽기 동적으로 확인 |
| 2026-06-11 | 임의 경로 읽기 + settings.php sha256 일치 + 내부 HTTP SSRF 확인 |
| 2026-06-11 | 프로덕션 소비자 도달 가능성(자동화 도구 내부 경로; FWA 정상-HTTP 유출) |
| 2026-06-11 | 독립적인 동료 검토가 과도한 주장을 수정(공격자 모델, CVSS, allow_url_fopen); 제출 보류(HOLD) |
| 2026-06/07 | 보류 항목 해소; Drupal 보안 팀에 비공개 보고; 기본 클래스 쌍둥이 결함을 drupal/ai에 플래그 지정 |
| 2026-07-10 | 수정 릴리스 1.1.1 / 1.2.2; 권고 SA-CONTRIB-2026-053 및 CVE-2026-13233 공개; 작성자를 발견자 겸 수정 개발자로 기재 |