README.md
Rendering markdown...
#!/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 };