Skip to main content
Glama

MCP Pentest

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 } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/adriyansyah-mf/mcp-pentest'

If you have feedback or need assistance with the MCP directory API, please join our Discord server