vulnscan.tsā¢16.1 kB
import { exec } from 'child_process';
import { promisify } from 'util';
import axios from 'axios';
import { ScanResult } from './recon.js';
const execAsync = promisify(exec);
export interface VulnerabilityResult {
id: string;
name: string;
severity: 'info' | 'low' | 'medium' | 'high' | 'critical';
description: string;
solution?: string;
references?: string[];
cvss_score?: number;
cve?: string;
affected_url?: string;
}
export class VulnScanTools {
async nucleiScan(target: string, templates?: string[], severity?: string): Promise<ScanResult> {
try {
let command = `nuclei -target ${target} -json`;
if (templates && templates.length > 0) {
command += ` -t ${templates.join(',')}`;
}
if (severity) {
command += ` -severity ${severity}`;
}
// Add rate limiting and timeout
command += ' -rate-limit 10 -timeout 10';
console.error(`Executing: ${command}`);
const { stdout, stderr } = await execAsync(command, {
timeout: 600000, // 10 min timeout
maxBuffer: 1024 * 1024 * 10 // 10MB buffer
});
const vulnerabilities: VulnerabilityResult[] = [];
// Parse JSON output line by line
const lines = stdout.split('\n').filter(line => line.trim());
for (const line of lines) {
try {
const result = JSON.parse(line);
vulnerabilities.push({
id: result.info?.name || result.templateID || 'Unknown',
name: result.info?.name || 'Unknown Vulnerability',
severity: result.info?.severity || 'info',
description: result.info?.description || 'No description available',
solution: result.info?.remediation,
references: result.info?.reference || [],
cvss_score: result.info?.['cvss-score'],
cve: result.info?.classification?.['cve-id'],
affected_url: result.matched_at || target
});
} catch (e) {
// Skip invalid JSON lines
}
}
return {
target,
timestamp: new Date().toISOString(),
tool: 'nuclei',
results: {
vulnerabilities,
total_found: vulnerabilities.length,
severity_breakdown: this.categorizeBySeverity(vulnerabilities),
raw_output: stdout
},
status: 'success'
};
} catch (error) {
return {
target,
timestamp: new Date().toISOString(),
tool: 'nuclei',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
async niktoScan(url: string, port?: number): Promise<ScanResult> {
try {
let command = `nikto -h ${url}`;
if (port) {
command += ` -p ${port}`;
}
// Output format
command += ' -Format txt';
console.error(`Executing: ${command}`);
const { stdout, stderr } = await execAsync(command, {
timeout: 600000 // 10 min timeout
});
const vulnerabilities = this.parseNiktoOutput(stdout, url);
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'nikto',
results: {
vulnerabilities,
total_found: vulnerabilities.length,
raw_output: stdout
},
status: 'success'
};
} catch (error) {
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'nikto',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
async sqlmapScan(url: string, data?: string, cookie?: string): Promise<ScanResult> {
try {
let command = `sqlmap -u "${url}" --batch --risk=1 --level=1`;
if (data) {
command += ` --data="${data}"`;
}
if (cookie) {
command += ` --cookie="${cookie}"`;
}
// Add safety flags
command += ' --answers="extending=N,follow=N,other=N" --timeout=10 --retries=1';
console.error(`Executing: ${command}`);
const { stdout, stderr } = await execAsync(command, {
timeout: 600000 // 10 min timeout
});
const sqlInjectionResults = this.parseSqlmapOutput(stdout, url);
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'sqlmap',
results: {
sql_injection_points: sqlInjectionResults,
total_found: sqlInjectionResults.length,
raw_output: stdout
},
status: 'success'
};
} catch (error) {
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'sqlmap',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
async customWebVulnScan(url: string): Promise<ScanResult> {
try {
const vulnerabilities: VulnerabilityResult[] = [];
// Check for common web vulnerabilities manually
await Promise.all([
this.checkXSSReflection(url, vulnerabilities),
this.checkDirectoryTraversal(url, vulnerabilities),
this.checkCommandInjection(url, vulnerabilities),
this.checkInsecureHeaders(url, vulnerabilities),
this.checkInformationDisclosure(url, vulnerabilities),
this.checkCSRF(url, vulnerabilities)
]);
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'custom_web_vuln_scan',
results: {
vulnerabilities,
total_found: vulnerabilities.length,
severity_breakdown: this.categorizeBySeverity(vulnerabilities)
},
status: 'success'
};
} catch (error) {
return {
target: url,
timestamp: new Date().toISOString(),
tool: 'custom_web_vuln_scan',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
// Helper methods
private categorizeBySeverity(vulnerabilities: VulnerabilityResult[]): Record<string, number> {
const breakdown = { info: 0, low: 0, medium: 0, high: 0, critical: 0 };
vulnerabilities.forEach(vuln => {
breakdown[vuln.severity]++;
});
return breakdown;
}
private parseNiktoOutput(output: string, target: string): VulnerabilityResult[] {
const vulnerabilities: VulnerabilityResult[] = [];
const lines = output.split('\n');
for (const line of lines) {
if (line.includes('+ ') && !line.includes('Nikto v') && !line.includes('Target Host')) {
let severity: 'info' | 'low' | 'medium' | 'high' | 'critical' = 'info';
// Determine severity based on content
if (line.toLowerCase().includes('xss') ||
line.toLowerCase().includes('sql injection') ||
line.toLowerCase().includes('command injection')) {
severity = 'high';
} else if (line.toLowerCase().includes('directory') ||
line.toLowerCase().includes('admin') ||
line.toLowerCase().includes('backup')) {
severity = 'medium';
} else if (line.toLowerCase().includes('version') ||
line.toLowerCase().includes('information')) {
severity = 'low';
}
vulnerabilities.push({
id: `nikto-${vulnerabilities.length + 1}`,
name: 'Nikto Finding',
severity,
description: line.trim(),
affected_url: target
});
}
}
return vulnerabilities;
}
private parseSqlmapOutput(output: string, target: string): VulnerabilityResult[] {
const vulnerabilities: VulnerabilityResult[] = [];
if (output.toLowerCase().includes('injectable') ||
output.toLowerCase().includes('sql injection')) {
const lines = output.split('\n');
let currentPayload = '';
let currentParameter = '';
for (const line of lines) {
if (line.includes('Parameter:')) {
currentParameter = line.split('Parameter:')[1]?.trim() || '';
}
if (line.includes('Type:') || line.includes('Payload:')) {
currentPayload = line.trim();
}
if (line.toLowerCase().includes('injectable')) {
vulnerabilities.push({
id: `sqlmap-${vulnerabilities.length + 1}`,
name: 'SQL Injection',
severity: 'high',
description: `SQL injection vulnerability found in parameter: ${currentParameter}. ${currentPayload}`,
solution: 'Use parameterized queries and input validation',
affected_url: target,
cve: 'CWE-89'
});
}
}
}
return vulnerabilities;
}
private async checkXSSReflection(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const payload = '<script>alert("XSS")</script>';
const testUrl = `${url}?test=${encodeURIComponent(payload)}`;
const response = await axios.get(testUrl, { timeout: 5000 });
if (response.data.includes(payload)) {
vulnerabilities.push({
id: 'xss-reflection',
name: 'Reflected Cross-Site Scripting (XSS)',
severity: 'high',
description: 'The application reflects user input without proper sanitization',
solution: 'Implement proper input validation and output encoding',
affected_url: testUrl,
cve: 'CWE-79'
});
}
} catch (error) {
// Test failed, continue
}
}
private async checkDirectoryTraversal(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const payloads = ['../../../etc/passwd', '..\\..\\..\\windows\\system32\\drivers\\etc\\hosts'];
for (const payload of payloads) {
const testUrl = `${url}?file=${encodeURIComponent(payload)}`;
try {
const response = await axios.get(testUrl, { timeout: 5000 });
if (response.data.includes('root:') || response.data.includes('127.0.0.1')) {
vulnerabilities.push({
id: 'directory-traversal',
name: 'Directory Traversal',
severity: 'high',
description: 'The application is vulnerable to directory traversal attacks',
solution: 'Implement proper input validation and file access controls',
affected_url: testUrl,
cve: 'CWE-22'
});
break;
}
} catch (error) {
// Continue to next payload
}
}
} catch (error) {
// Test failed, continue
}
}
private async checkCommandInjection(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const payloads = ['; cat /etc/passwd', '| type C:\\Windows\\System32\\drivers\\etc\\hosts'];
for (const payload of payloads) {
const testUrl = `${url}?cmd=${encodeURIComponent(payload)}`;
try {
const response = await axios.get(testUrl, { timeout: 5000 });
if (response.data.includes('root:') || response.data.includes('127.0.0.1')) {
vulnerabilities.push({
id: 'command-injection',
name: 'Command Injection',
severity: 'critical',
description: 'The application is vulnerable to command injection attacks',
solution: 'Implement proper input validation and avoid system calls with user input',
affected_url: testUrl,
cve: 'CWE-78'
});
break;
}
} catch (error) {
// Continue to next payload
}
}
} catch (error) {
// Test failed, continue
}
}
private async checkInsecureHeaders(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const response = await axios.get(url, { timeout: 5000 });
const headers = response.headers;
// Check for missing security headers
const securityHeaders = [
'x-frame-options',
'x-content-type-options',
'x-xss-protection',
'strict-transport-security',
'content-security-policy'
];
const missingHeaders = securityHeaders.filter(header => !headers[header]);
if (missingHeaders.length > 0) {
vulnerabilities.push({
id: 'missing-security-headers',
name: 'Missing Security Headers',
severity: 'medium',
description: `Missing security headers: ${missingHeaders.join(', ')}`,
solution: 'Implement proper security headers to prevent common attacks',
affected_url: url
});
}
// Check for information disclosure in headers
if (headers.server) {
vulnerabilities.push({
id: 'server-info-disclosure',
name: 'Server Information Disclosure',
severity: 'low',
description: `Server information disclosed: ${headers.server}`,
solution: 'Hide server version information',
affected_url: url
});
}
} catch (error) {
// Test failed, continue
}
}
private async checkInformationDisclosure(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const testPaths = [
'/robots.txt',
'/.git/config',
'/.env',
'/phpinfo.php',
'/info.php',
'/test.php',
'/backup.sql',
'/database.sql'
];
for (const path of testPaths) {
try {
const testUrl = url.replace(/\/$/, '') + path;
const response = await axios.get(testUrl, { timeout: 5000 });
if (response.status === 200) {
let severity: 'info' | 'low' | 'medium' | 'high' = 'low';
if (path.includes('.git') || path.includes('.env') || path.includes('.sql')) {
severity = 'high';
} else if (path.includes('phpinfo') || path.includes('info.php')) {
severity = 'medium';
}
vulnerabilities.push({
id: `info-disclosure-${path.replace(/[^a-zA-Z0-9]/g, '-')}`,
name: 'Information Disclosure',
severity,
description: `Sensitive file accessible: ${path}`,
solution: 'Remove or protect sensitive files',
affected_url: testUrl
});
}
} catch (error) {
// File not accessible, continue
}
}
} catch (error) {
// Test failed, continue
}
}
private async checkCSRF(url: string, vulnerabilities: VulnerabilityResult[]): Promise<void> {
try {
const response = await axios.get(url, { timeout: 5000 });
const html = response.data;
// Check for forms without CSRF tokens
const formRegex = /<form[^>]*>/gi;
const forms = html.match(formRegex) || [];
let hasCSRFProtection = false;
for (const form of forms) {
if (form.toLowerCase().includes('csrf') ||
form.toLowerCase().includes('token') ||
html.includes('csrf_token') ||
html.includes('authenticity_token')) {
hasCSRFProtection = true;
break;
}
}
if (forms.length > 0 && !hasCSRFProtection) {
vulnerabilities.push({
id: 'csrf-missing',
name: 'Missing CSRF Protection',
severity: 'medium',
description: 'Forms found without CSRF protection',
solution: 'Implement CSRF tokens for all state-changing operations',
affected_url: url,
cve: 'CWE-352'
});
}
} catch (error) {
// Test failed, continue
}
}
}