Skip to main content
Glama

MCP Pentest

recon.ts•23.2 kB
import { exec } from 'child_process'; import { promisify } from 'util'; import axios from 'axios'; import * as cheerio from 'cheerio'; import { URL } from 'url'; const execAsync = promisify(exec); export interface ScanResult { target: string; timestamp: string; tool: string; results: any; status: 'success' | 'error'; error?: string; } export interface PortScanResult { port: number; protocol: string; state: string; service: string; version?: string; } export interface TechDetectionResult { technology: string; version?: string; confidence: number; category: string; } export interface ServiceRecommendation { service: string; port: number; protocol: string; version?: string; recommendedTools: string[]; testingApproach: string[]; riskLevel: 'low' | 'medium' | 'high' | 'critical'; } export class ReconTools { async serviceAnalysis(ports: PortScanResult[]): Promise<{serviceRecommendations: ServiceRecommendation[]}> { const recommendations: ServiceRecommendation[] = []; for (const port of ports) { const service = port.service.toLowerCase(); const recommendation: ServiceRecommendation = { service: port.service, port: port.port, protocol: port.protocol, version: port.version, recommendedTools: [], testingApproach: [], riskLevel: 'low' }; // SMB/NetBIOS services if (service.includes('smb') || service.includes('netbios') || port.port === 445 || port.port === 139) { recommendation.recommendedTools = ['crackmapexec', 'smbclient', 'enum4linux', 'smbmap', 'rpcclient']; recommendation.testingApproach = [ 'SMB null session enumeration', 'Share enumeration and access testing', 'User enumeration via RPC', 'Password spraying attacks', 'SMB vulnerability scanning (EternalBlue, etc.)' ]; recommendation.riskLevel = 'high'; } // SSH services else if (service.includes('ssh') || port.port === 22) { recommendation.recommendedTools = ['hydra', 'medusa', 'ncrack', 'ssh-audit', 'ssh-keyscan']; recommendation.testingApproach = [ 'SSH banner analysis and version detection', 'Authentication method enumeration', 'Weak credential testing', 'SSH key enumeration', 'Protocol vulnerability testing' ]; recommendation.riskLevel = 'medium'; } // FTP services else if (service.includes('ftp') || port.port === 21) { recommendation.recommendedTools = ['ftp', 'hydra', 'medusa', 'nmap-scripts']; recommendation.testingApproach = [ 'Anonymous FTP access testing', 'FTP bounce attack testing', 'Credential brute-forcing', 'Directory traversal testing', 'FTP vulnerability scanning' ]; recommendation.riskLevel = 'medium'; } // HTTP/HTTPS services else if (service.includes('http') || port.port === 80 || port.port === 443 || port.port === 8080) { recommendation.recommendedTools = ['dirb', 'gobuster', 'feroxbuster', 'nikto', 'burpsuite', 'owasp-zap']; recommendation.testingApproach = [ 'Directory and file enumeration', 'Parameter discovery and fuzzing', 'OWASP Top 10 vulnerability testing', 'Technology stack analysis', 'SSL/TLS configuration testing' ]; recommendation.riskLevel = 'medium'; } // Database services else if (service.includes('mysql') || service.includes('postgres') || service.includes('mssql') || port.port === 3306 || port.port === 5432 || port.port === 1433) { recommendation.recommendedTools = ['sqlmap', 'hydra', 'medusa', 'nmap-scripts']; recommendation.testingApproach = [ 'Default credential testing', 'SQL injection testing', 'Database enumeration', 'Privilege escalation testing', 'Configuration review' ]; recommendation.riskLevel = 'high'; } // RDP services else if (service.includes('rdp') || port.port === 3389) { recommendation.recommendedTools = ['rdesktop', 'xfreerdp', 'hydra', 'crowbar', 'bluekeep-scanner']; recommendation.testingApproach = [ 'RDP connection testing', 'Credential brute-forcing', 'BlueKeep vulnerability testing', 'NLA bypass testing', 'Session hijacking testing' ]; recommendation.riskLevel = 'high'; } // SNMP services else if (service.includes('snmp') || port.port === 161) { recommendation.recommendedTools = ['snmpwalk', 'snmpcheck', 'onesixtyone', 'snmp-check']; recommendation.testingApproach = [ 'SNMP community string enumeration', 'SNMP walk and enumeration', 'SNMP version detection', 'Information disclosure testing', 'Write access testing' ]; recommendation.riskLevel = 'medium'; } // LDAP services else if (service.includes('ldap') || port.port === 389 || port.port === 636) { recommendation.recommendedTools = ['ldapsearch', 'ldapenum', 'enum4linux']; recommendation.testingApproach = [ 'Anonymous LDAP bind testing', 'Directory enumeration', 'User and group enumeration', 'Password policy enumeration', 'LDAP injection testing' ]; recommendation.riskLevel = 'medium'; } recommendations.push(recommendation); } return { serviceRecommendations: recommendations }; } async nmapScan(target: string, scanType: string = 'quick'): Promise<ScanResult> { try { let nmapArgs = ''; switch (scanType) { case 'quick': nmapArgs = '-F -sV'; break; case 'full': nmapArgs = '-p- -sV -sC'; break; case 'stealth': nmapArgs = '-sS -T2 -f'; break; case 'aggressive': nmapArgs = '-A -T4'; break; default: nmapArgs = '-F -sV'; } const command = `nmap ${nmapArgs} -oX - ${target}`; console.error(`Executing: ${command}`); const { stdout, stderr } = await execAsync(command, { timeout: 300000 }); // 5 min timeout // Parse XML output const ports = this.parseNmapXML(stdout); return { target, timestamp: new Date().toISOString(), tool: 'nmap', results: { scan_type: scanType, open_ports: ports, raw_output: stdout }, status: 'success' }; } catch (error) { return { target, timestamp: new Date().toISOString(), tool: 'nmap', results: {}, status: 'error', error: error instanceof Error ? error.message : String(error) }; } } async subdomainEnum(domain: string, wordlist?: string, useSubfinder?: boolean, fuzzTool?: 'ffuf' | 'wfuzz'): Promise<ScanResult> { try { const subdomains = new Set<string>(); // Method 1: Certificate Transparency logs try { const ctLogs = await this.getCertTransparencySubdomains(domain); ctLogs.forEach(sub => subdomains.add(sub)); } catch (e) { console.error('CT logs failed:', e); } // Method 2: DNS brute force with common subdomains or supplied wordlist const commonSubs = [ 'www', 'mail', 'ftp', 'localhost', 'webmail', 'smtp', 'pop', 'ns1', 'webdisk', 'ns2', 'cpanel', 'whm', 'autodiscover', 'autoconfig', 'dev', 'staging', 'test', 'api', 'admin', 'blog', 'shop', 'forum', 'support', 'mobile', 'app', 'cdn' ]; const customWordlist = wordlist ? await this.loadWordlist(wordlist) : commonSubs; for (const subdomain of customWordlist) { try { const fullDomain = `${subdomain}.${domain}`; const dns = require('dns').promises; await dns.lookup(fullDomain); subdomains.add(fullDomain); } catch (e) { // Subdomain doesn't exist } } // Method 3: subfinder integration if (useSubfinder) { try { const cmd = `subfinder -silent -d ${domain}`; const { stdout } = await execAsync(cmd, { timeout: 60000 }); stdout.split('\n').map(s => s.trim()).filter(s => s).forEach(s => subdomains.add(s)); } catch (e) { console.error('subfinder failed:', e); } } // Method 4: Fuzzing with ffuf/wfuzz if (fuzzTool) { try { const fuzzSubs = await this.fuzzSubdomains(domain, fuzzTool, wordlist); fuzzSubs.forEach(sub => subdomains.add(sub)); } catch (e) { console.error(`${fuzzTool} subdomain fuzzing failed:`, e); } } return { target: domain, timestamp: new Date().toISOString(), tool: 'subdomain_enum', results: { subdomains: Array.from(subdomains), count: subdomains.size, methods_used: ['certificate_transparency', 'dns_bruteforce'] .concat(useSubfinder ? ['subfinder'] : []) .concat(fuzzTool ? [`${fuzzTool}_fuzzing`] : []) }, status: 'success' }; } catch (error) { return { target: domain, timestamp: new Date().toISOString(), tool: 'subdomain_enum', results: {}, status: 'error', error: error instanceof Error ? error.message : String(error) }; } } async techDetection(url: string): Promise<ScanResult> { try { const technologies: TechDetectionResult[] = []; // Make HTTP request to analyze headers and content const response = await axios.get(url, { timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } }); const headers = response.headers; const html = response.data; const $ = cheerio.load(html); // Detect technologies from headers this.detectFromHeaders(headers, technologies); // Detect technologies from HTML content this.detectFromHTML($, technologies); // Detect technologies from meta tags this.detectFromMetaTags($, technologies); // Detect technologies from script sources this.detectFromScripts($, technologies); return { target: url, timestamp: new Date().toISOString(), tool: 'tech_detection', results: { technologies, headers: headers, status_code: response.status, server_info: { server: headers.server || 'Unknown', powered_by: headers['x-powered-by'] || 'Unknown', generator: $('meta[name="generator"]').attr('content') || 'Unknown' } }, status: 'success' }; } catch (error) { return { target: url, timestamp: new Date().toISOString(), tool: 'tech_detection', results: {}, status: 'error', error: error instanceof Error ? error.message : String(error) }; } } async directoryBruteforce(url: string, wordlist?: string, extensions?: string[], useSecLists?: boolean): Promise<ScanResult> { try { const foundPaths: string[] = []; const baseUrl = new URL(url); // Default directory wordlist const defaultDirs = [ 'admin', 'administrator', 'login', 'dashboard', 'panel', 'config', 'backup', 'test', 'dev', 'staging', 'api', 'uploads', 'files', 'images', 'css', 'js', 'assets', 'static', 'public', 'private', 'secure', 'hidden', 'secret', 'wp-admin', 'wp-content', 'wp-includes', 'phpmyadmin', 'mysql', 'database' ]; let dirs: string[] = defaultDirs; if (wordlist) { dirs = await this.loadWordlist(wordlist); } else if (useSecLists) { const candidates = [ '/usr/share/seclists/Discovery/Web-Content/common.txt', '/usr/share/seclists/Discovery/Web-Content/raft-small-words.txt', '/usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt' ]; for (const path of candidates) { try { const loaded = await this.loadWordlist(path); if (loaded.length > 0) { dirs = loaded; break; } } catch (_) {} } } const exts = extensions || ['php', 'html', 'asp', 'aspx', 'jsp', 'txt', 'xml', 'json']; // Check directories for (const dir of dirs) { try { const testUrl = `${baseUrl.origin}/${dir}/`; const response = await axios.head(testUrl, { timeout: 5000 }); if (response.status === 200 || response.status === 403) { foundPaths.push(testUrl); } } catch (e) { // Path not found or error } } // Check files with extensions for (const dir of dirs) { for (const ext of exts) { try { const testUrl = `${baseUrl.origin}/${dir}.${ext}`; const response = await axios.head(testUrl, { timeout: 5000 }); if (response.status === 200) { foundPaths.push(testUrl); } } catch (e) { // File not found or error } } } return { target: url, timestamp: new Date().toISOString(), tool: 'directory_bruteforce', results: { found_paths: foundPaths, total_found: foundPaths.length, wordlist_size: dirs.length, extensions_tested: exts }, status: 'success' }; } catch (error) { return { target: url, timestamp: new Date().toISOString(), tool: 'directory_bruteforce', results: {}, status: 'error', error: error instanceof Error ? error.message : String(error) }; } } // Helper methods private parseNmapXML(xmlOutput: string): PortScanResult[] { const ports: PortScanResult[] = []; // Simple XML parsing for ports - in production, use proper XML parser const portRegex = /<port protocol="(tcp|udp)" portid="(\d+)">[\s\S]*?<state state="(open|closed|filtered)"[\s\S]*?<service name="([^"]*)"(?:\s+version="([^"]*)")?/g; let match; while ((match = portRegex.exec(xmlOutput)) !== null) { ports.push({ port: parseInt(match[2]), protocol: match[1], state: match[3], service: match[4], version: match[5] || undefined }); } return ports; } private async getCertTransparencySubdomains(domain: string): Promise<string[]> { try { const response = await axios.get(`https://crt.sh/?q=%.${domain}&output=json`, { timeout: 10000 }); const subdomains = new Set<string>(); for (const cert of response.data) { if (cert.name_value) { const names = cert.name_value.split('\n'); for (const name of names) { if (name.includes(domain) && !name.includes('*')) { subdomains.add(name.trim()); } } } } return Array.from(subdomains); } catch (error) { console.error('CT logs error:', error); return []; } } private async loadWordlist(wordlistPath: string): Promise<string[]> { try { const fs = require('fs').promises; const content = await fs.readFile(wordlistPath, 'utf8'); return content.split('\n').filter((line: string) => line.trim() !== ''); } catch (error) { console.error('Wordlist loading error:', error); return []; } } private detectFromHeaders(headers: any, technologies: TechDetectionResult[]): void { // Server detection if (headers.server) { const server = headers.server.toLowerCase(); if (server.includes('apache')) { technologies.push({ technology: 'Apache HTTP Server', version: this.extractVersion(server, 'apache'), confidence: 100, category: 'Web Server' }); } else if (server.includes('nginx')) { technologies.push({ technology: 'Nginx', version: this.extractVersion(server, 'nginx'), confidence: 100, category: 'Web Server' }); } else if (server.includes('iis')) { technologies.push({ technology: 'Microsoft IIS', version: this.extractVersion(server, 'iis'), confidence: 100, category: 'Web Server' }); } } // X-Powered-By detection if (headers['x-powered-by']) { const poweredBy = headers['x-powered-by'].toLowerCase(); if (poweredBy.includes('php')) { technologies.push({ technology: 'PHP', version: this.extractVersion(poweredBy, 'php'), confidence: 100, category: 'Programming Language' }); } else if (poweredBy.includes('asp.net')) { technologies.push({ technology: 'ASP.NET', version: this.extractVersion(poweredBy, 'asp.net'), confidence: 100, category: 'Web Framework' }); } } } private detectFromHTML($: cheerio.CheerioAPI, technologies: TechDetectionResult[]): void { const html = $.html(); // WordPress detection if (html.includes('wp-content') || html.includes('wp-includes')) { technologies.push({ technology: 'WordPress', confidence: 95, category: 'CMS' }); } // jQuery detection if (html.includes('jquery') || $('script[src*="jquery"]').length > 0) { technologies.push({ technology: 'jQuery', confidence: 90, category: 'JavaScript Library' }); } // Bootstrap detection if (html.includes('bootstrap') || $('link[href*="bootstrap"]').length > 0) { technologies.push({ technology: 'Bootstrap', confidence: 85, category: 'CSS Framework' }); } } private detectFromMetaTags($: cheerio.CheerioAPI, technologies: TechDetectionResult[]): void { const generator = $('meta[name="generator"]').attr('content'); if (generator) { const gen = generator.toLowerCase(); if (gen.includes('wordpress')) { technologies.push({ technology: 'WordPress', version: this.extractVersion(generator, 'wordpress'), confidence: 100, category: 'CMS' }); } else if (gen.includes('drupal')) { technologies.push({ technology: 'Drupal', version: this.extractVersion(generator, 'drupal'), confidence: 100, category: 'CMS' }); } } } private detectFromScripts($: cheerio.CheerioAPI, technologies: TechDetectionResult[]): void { $('script[src]').each((_, element) => { const src = $(element).attr('src'); if (src) { const srcLower = src.toLowerCase(); if (srcLower.includes('react')) { technologies.push({ technology: 'React', confidence: 90, category: 'JavaScript Framework' }); } else if (srcLower.includes('angular')) { technologies.push({ technology: 'Angular', confidence: 90, category: 'JavaScript Framework' }); } else if (srcLower.includes('vue')) { technologies.push({ technology: 'Vue.js', confidence: 90, category: 'JavaScript Framework' }); } } }); } private extractVersion(text: string, technology: string): string | undefined { const versionRegex = new RegExp(`${technology}[\/\\s]+(\\d+(?:\\.\\d+)*(?:\\.\\d+)*(?:[a-z]\\d*)?)`, 'i'); const match = text.match(versionRegex); return match ? match[1] : undefined; } private async fuzzSubdomains(domain: string, tool: 'ffuf' | 'wfuzz', wordlist?: string): Promise<string[]> { const subdomains: string[] = []; // Get wordlist for subdomain fuzzing let fuzzWordlist: string; if (wordlist) { fuzzWordlist = wordlist; } else { // Try SecLists subdomain wordlists const candidates = [ '/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt', '/usr/share/seclists/Discovery/DNS/fierce-hostlist.txt', '/usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt' ]; fuzzWordlist = candidates[0]; // Default to largest for (const path of candidates) { try { const fs = require('fs'); if (fs.existsSync(path)) { fuzzWordlist = path; break; } } catch (_) {} } } try { let command: string; if (tool === 'ffuf') { // ffuf command for subdomain fuzzing command = `ffuf -w ${fuzzWordlist} -u http://FUZZ.${domain} -H "Host: FUZZ.${domain}" -t 50 -fc 404,403 -fs 0 -timeout 10 -o /tmp/ffuf_subdomains.json -of json -s`; } else if (tool === 'wfuzz') { // wfuzz command for subdomain fuzzing command = `wfuzz -w ${fuzzWordlist} -H "Host: FUZZ.${domain}" --hc 404,403 --hl 0 -t 50 -s 0.1 http://FUZZ.${domain}`; } else { throw new Error(`Unsupported fuzzing tool: ${tool}`); } console.error(`Running subdomain fuzzing: ${command}`); const { stdout, stderr } = await execAsync(command, { timeout: 300000 }); // 5 min timeout if (tool === 'ffuf') { // Parse ffuf JSON output try { const fs = require('fs'); const jsonOutput = fs.readFileSync('/tmp/ffuf_subdomains.json', 'utf8'); const results = JSON.parse(jsonOutput); if (results.results) { for (const result of results.results) { if (result.input && result.input.FUZZ) { subdomains.push(`${result.input.FUZZ}.${domain}`); } } } // Cleanup temp file fs.unlinkSync('/tmp/ffuf_subdomains.json'); } catch (e) { console.error('Failed to parse ffuf output:', e); } } else if (tool === 'wfuzz') { // Parse wfuzz output const lines = stdout.split('\n'); for (const line of lines) { // Look for successful responses in wfuzz output const match = line.match(/^\d+.*?(\w+)\.${domain.replace('.', '\\.')}/); if (match && match[1]) { subdomains.push(`${match[1]}.${domain}`); } } } } catch (error) { console.error(`Subdomain fuzzing with ${tool} failed:`, error); } return subdomains; } }

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