Skip to main content
Glama

subdomain_enum

Discover subdomains of target domains using multiple enumeration methods including wordlist fuzzing and automated discovery tools for comprehensive security reconnaissance.

Instructions

Enumerate subdomains of target domain using multiple methods

Input Schema

NameRequiredDescriptionDefault
domainYesTarget domain
fuzz_toolNoUse fuzzing tool for subdomain discovery (ffuf/wfuzz)
use_subfinderNoAlso use subfinder for enumeration
wordlistNoWordlist to use (optional)

Input Schema (JSON Schema)

{ "properties": { "domain": { "description": "Target domain", "type": "string" }, "fuzz_tool": { "description": "Use fuzzing tool for subdomain discovery (ffuf/wfuzz)", "enum": [ "ffuf", "wfuzz" ], "type": "string" }, "use_subfinder": { "description": "Also use subfinder for enumeration", "type": "boolean" }, "wordlist": { "description": "Wordlist to use (optional)", "type": "string" } }, "required": [ "domain" ], "type": "object" }

Implementation Reference

  • Main handler function implementing subdomain enumeration using Certificate Transparency logs, DNS brute force, subfinder, and optional fuzzing with ffuf or wfuzz.
    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) }; } }
  • MCP tool schema definition including input parameters and validation.
    { name: "subdomain_enum", description: "Enumerate subdomains of target domain using multiple methods", inputSchema: { type: "object", properties: { domain: { type: "string", description: "Target domain" }, wordlist: { type: "string", description: "Wordlist to use (optional)" }, use_subfinder: { type: "boolean", description: "Also use subfinder for enumeration" }, fuzz_tool: { type: "string", enum: ["ffuf", "wfuzz"], description: "Use fuzzing tool for subdomain discovery (ffuf/wfuzz)" } }, required: ["domain"] }
  • src/index.ts:508-509 (registration)
    Registration and dispatch of the subdomain_enum tool in the MCP server request handler.
    case "subdomain_enum": return respond(await this.reconTools.subdomainEnum(args.domain, args.wordlist, args.use_subfinder, args.fuzz_tool));
  • Helper function to fetch subdomains from Certificate Transparency logs.
    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 []; } }
  • Helper function for subdomain fuzzing using ffuf or wfuzz.
    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; }

Latest Blog Posts

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