#!/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 };