4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / test-vulnerability.js JS
#!/usr/bin/env node

/**
 * 用于演示 CVE-2025-66478 漏洞检测的测试脚本
 * 该脚本模拟测试 Next.js 应用程序是否存在漏洞
 * 并提供修复指导。
 */

const https = require('https');
const http = require('http');
const fs = require('fs');
const { RSCExploit } = require('./exploit');

class VulnerabilityTester {
    constructor() {
        this.vulnerableVersions = {
            '15.0': { min: 0, max: 4, fixed: '15.0.5' },
            '15.1': { min: 0, max: 8, fixed: '15.1.9' },
            '15.2': { min: 0, max: 5, fixed: '15.2.6' },
            '15.3': { min: 0, max: 5, fixed: '15.3.6' },
            '15.4': { min: 0, max: 7, fixed: '15.4.8' },
            '15.5': { min: 0, max: 6, fixed: '15.5.7' },
            '16.0': { min: 0, max: 6, fixed: '16.0.7' }
        };
        
        this.canaryVulnerable = '14.3.0-canary.77';
    }

    /**
     * 检查版本字符串是否存在漏洞
     */
    isVersionVulnerable(version) {
        if (!version) return false;
        
        // 检查 canary 版本
        if (version.includes('canary')) {
            return version >= this.canaryVulnerable;
        }
        
        // 检查常规版本
        const versionParts = version.match(/^(\d+)\.(\d+)(?:\.(\d+))?/);
        if (!versionParts) return false;
        
        const [, major, minor, patch] = versionParts;
        const versionKey = `${major}.${minor}`;
        
        if (this.vulnerableVersions[versionKey]) {
            const patchNum = parseInt(patch || '0');
            const range = this.vulnerableVersions[versionKey];
            return patchNum >= range.min && patchNum <= range.max;
        }
        
        return false;
    }

    /**
     * 获取易受攻击版本的修复版本
     */
    getFixedVersion(version) {
        const versionParts = version.match(/^(\d+)\.(\d+)/);
        if (!versionParts) return null;
        
        const [, major, minor] = versionParts;
        const versionKey = `${major}.${minor}`;
        
        return this.vulnerableVersions[versionKey]?.fixed || null;
    }

    /**
     * 尝试从响应头或内容中检测 Next.js 版本
     */
    async detectNextjsVersion(url) {
        return new Promise((resolve, reject) => {
            const protocol = url.startsWith('https://') ? https : http;
            const parsedUrl = new URL(url);
            
            const options = {
                hostname: parsedUrl.hostname,
                port: parsedUrl.port || (protocol === https ? 443 : 80),
                path: parsedUrl.pathname || '/',
                method: 'GET',
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
                }
            };
            
            const req = protocol.request(options, (res) => {
                let version = null;
                
                // 首先检查头信息
                if (res.headers['x-powered-by'] && res.headers['x-powered-by'].includes('Next.js')) {
                    const match = res.headers['x-powered-by'].match(/Next\.js\/([\d\.]+(?:-canary\.\d+)?)/);
                    if (match) {
                        version = match[1];
                    }
                }
                
                let body = '';
                res.on('data', (chunk) => {
                    body += chunk;
                });
                
                res.on('end', () => {
                    // 尝试在响应体中查找版本信息
                    if (!version) {
                        const bodyMatch = body.match(/Next\.js\/([\d\.]+(?:-canary\.\d+)?)/);
                        if (bodyMatch) {
                            version = bodyMatch[1];
                        }
                    }
                    
                    resolve({
                        detected: !!version,
                        version: version,
                        headers: res.headers
                    });
                });
            });
            
            req.on('error', (error) => {
                reject(error);
            });
            
            req.end();
        });
    }

    /**
     * 测试 RSC 端点和漏洞指标
     */
    async testRSCEndpoints(baseUrl) {
        const endpoints = [
            '/_rsc',
            '/api/rsc',
            '/app/api/rsc',
            '/server-actions',
            '/api/server-actions',
            '/_next/data/test/page.json'
        ];
        
        const results = [];
        
        for (const endpoint of endpoints) {
            const url = new URL(endpoint, baseUrl).href;
            const exploit = new RSCExploit(url);
            
            try {
                const response = await exploit.sendExploit('echo "test"');
                
                // 检查 RSC 特定指标
                // 仅将 200/500 状态码且具有 RSC 特征的视为真正的 RSC 端点
                const isUsefulStatusCode = 
                    response.statusCode === 200 || 
                    response.statusCode === 500;
                    
                const hasRSCIndicators = 
                    isUsefulStatusCode && (
                        response.headers['content-type']?.includes('application/json') ||
                        response.data.includes('react-server-components') ||
                        response.data.includes('RSC') ||
                        response.headers['x-nextjs-data']
                    );
                
                results.push({
                    endpoint,
                    url,
                    statusCode: response.statusCode,
                    hasRSCIndicators,
                    // 405、403、404 均视为不可访问或无意义的响应
                    isAccessible: isUsefulStatusCode
                });
            } catch (error) {
                results.push({
                    endpoint,
                    url,
                    error: error.message,
                    hasRSCIndicators: false,
                    isAccessible: false
                });
            }
            
            // 在请求之间添加延迟
            await new Promise(resolve => setTimeout(resolve, 500));
        }
        
        return results;
    }

    /**
     * 生成综合漏洞报告
     */
    async generateReport(targetUrl) {
        console.log(`\n🔍 为 ${targetUrl} 生成漏洞报告`);
        console.log('-'.repeat(60));
        
        const report = {
            target: targetUrl,
            timestamp: new Date().toISOString(),
            nextjs: {
                detected: false,
                version: null,
                isVulnerable: false,
                fixedVersion: null
            },
            rsc: {
                detected: false,
                endpoints: []
            },
            vulnerability: {
                exists: false,
                severity: '严重 (CVSS 10.0)',
                description: 'React Server Components 反序列化中的 RCE 漏洞'
            },
            recommendations: []
        };
        
        try {
            // 检测 Next.js 版本
            console.log('\n📦 检查 Next.js...');
            const versionInfo = await this.detectNextjsVersion(targetUrl);
            
            if (versionInfo.detected) {
                report.nextjs.detected = true;
                report.nextjs.version = versionInfo.version;
                report.nextjs.isVulnerable = this.isVersionVulnerable(versionInfo.version);
                report.nextjs.fixedVersion = this.getFixedVersion(versionInfo.version);
                
                console.log(`✅ 检测到 Next.js 版本: ${versionInfo.version}`);
                if (report.nextjs.isVulnerable) {
                    console.log(`🚨 版本存在漏洞!修复版本: ${report.nextjs.fixedVersion}`);
                } else {
                    console.log('✅ 版本似乎已修复');
                }
            } else {
                console.log('❓ 无法检测 Next.js 版本');
                console.log('   (某些网站可能隐藏版本信息)');
            }
            
            // 测试 RSC 端点
            console.log('\n🔧 测试 React Server Components 端点...');
            const rscResults = await this.testRSCEndpoints(targetUrl);
            report.rsc.endpoints = rscResults;
            
            const accessibleEndpoints = rscResults.filter(r => r.isAccessible);
            const rscEndpoints = rscResults.filter(r => r.hasRSCIndicators);
            
            if (rscEndpoints.length > 0) {
                report.rsc.detected = true;
                console.log(`🚨 发现 ${rscEndpoints.length} 个潜在的 RSC 端点:`);
                rscEndpoints.forEach(endpoint => {
                    console.log(`   - ${endpoint.endpoint} (状态: ${endpoint.statusCode})`);
                });
            } else if (accessibleEndpoints.length > 0) {
                console.log(`ℹ️  发现 ${accessibleEndpoints.length} 个可访问的端点,但没有明确的 RSC 指标`);
            } else {
                console.log('❓ 未检测到可访问的 RSC 端点');
            }
            
            // 确定整体漏洞状态 - 更严格的判断:
            // 1. 必须是存在漏洞的 Next.js 版本
            // 2. 或者存在真正的 RSC 端点(返回 200/500 且具有 RSC 特征)
            // 3. 405 响应不被视为漏洞证据
            const hasVulnerableVersion = report.nextjs.isVulnerable;
            const hasRealRSCEndpoints = report.rsc.endpoints.some(e => e.hasRSCIndicators);
            
            report.vulnerability.exists = hasVulnerableVersion && hasRealRSCEndpoints;
            
            // 生成建议
            console.log('\n💡 安全建议:');
            console.log('-'.repeat(40));
            
            if (report.nextjs.isVulnerable) {
                const recommendation = `紧急: 立即将 Next.js 从版本 ${report.nextjs.version} 升级到 ${report.nextjs.fixedVersion}!`;
                report.recommendations.push(recommendation);
                console.log(`🚨 ${recommendation}`);
            }
            
            if (report.rsc.detected) {
                const recommendation = '为所有 RSC 端点实施适当的输入验证和清理';
                report.recommendations.push(recommendation);
                console.log(`🔒 ${recommendation}`);
            }
            
            // 通用建议
            const generalRecommendations = [
                '启用内容安全策略 (CSP) 头',
                '实施速率限制以防止暴力攻击',
                '使用 Web 应用防火墙 (WAF) 作为额外的保护层',
                '使用安全工具定期扫描漏洞',
                '遵循最小权限原则设置服务器权限'
            ];
            
            generalRecommendations.forEach(rec => {
                report.recommendations.push(rec);
                console.log(`ℹ️ ${rec}`);
            });
            
            console.log('\n📋 最终评估:');
            console.log('-'.repeat(40));
            
            if (report.vulnerability.exists) {
                console.log('🚨 检测到漏洞 - 立即采取行动!');
            } else {
                console.log('✅ 未检测到明显的漏洞迹象');
                console.log('   然而,这并不保证系统是安全的');
            }
            
            // 保存报告到文件
            const reportFilename = `vulnerability-report-${Date.now()}.json`;
            fs.writeFileSync(reportFilename, JSON.stringify(report, null, 2));
            console.log(`\n📄 详细报告已保存到: ${reportFilename}`);
            
        } catch (error) {
            console.error('\n❌ 生成报告时出错:', error.message);
            report.error = error.message;
        }
        
        return report;
    }
}

/**
 * 主函数
 */
async function main() {
    console.log('='.repeat(60));
    console.log('CVE-2025-66478 漏洞测试器');
    console.log('='.repeat(60));
    console.log('此工具帮助识别与 Next.js RSC 反序列化问题');
    console.log('(CVE-2025-66478) 相关的潜在漏洞。');
    console.log('='.repeat(60));

    if (process.argv.length < 3) {
        console.log('\n用法: node test-vulnerability.js <目标URL>');
        console.log('示例: node test-vulnerability.js https://example.com');
        process.exit(1);
    }

    const targetUrl = process.argv[2];
    const tester = new VulnerabilityTester();

    try {
        await tester.generateReport(targetUrl);
    } catch (error) {
        console.error('❌ 测试失败:', error.message);
    }

    console.log('\n🔒 记住: 最好的保护是保持依赖项更新!');
}

if (require.main === module) {
    main().catch(console.error);
}

module.exports = { VulnerabilityTester };