recon_vhost
Identify virtual hosts by fuzzing Host headers with a wordlist to discover hidden subdomains and web applications during security testing.
Instructions
Brute-force virtual hosts by fuzzing the Host header. Returns baseline_length, results (vhost/status/length/length_delta), unique_vhosts, and tested count. Read-only, sends one request per wordlist entry.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| target | Yes | Target IP or domain to send requests to | |
| base_domain | Yes | Base domain for vhost names, e.g. hackycorp.com | |
| wordlist | No | Path to wordlist file. Uses built-in common subdomains if not provided. |
Implementation Reference
- src/tools/recon.ts:143-213 (handler)The handler implementation for the `recon_vhost` tool, which performs vhost brute-forcing by fuzzing the Host header using curl.
async ({ target, base_domain, wordlist }) => { requireTool("curl"); // Built-in common subdomains const defaultSubs = [ "admin", "www", "mail", "ftp", "api", "dev", "staging", "test", "portal", "dashboard", "app", "blog", "shop", "internal", "intranet", "vpn", "remote", "beta", "demo", "docs", "wiki", "git", "jenkins", "ci", "cd", "monitor", "grafana", "kibana", "elastic", "balancer", "proxy", ]; let subs: string[]; if (wordlist && fs.existsSync(wordlist)) { const content = fs.readFileSync(wordlist, "utf-8"); subs = content .split("\n") .map((l) => l.trim()) .filter((l) => l.length > 0) .slice(0, 500); } else { subs = defaultSubs; } // Baseline: request with the raw target as Host const baseline = await runCmd("curl", [ "-sk", "-o", "/dev/null", "-w", "%{http_code}:%{size_download}", `http://${target}/`, ]); const bp = baseline.stdout.split(":"); const baselineLength = bp.length > 1 ? parseInt(bp[1], 10) : 0; const results: Array<{ vhost: string; status: number; length: number; length_delta: number; }> = []; for (const sub of subs) { const vhost = `${sub}.${base_domain}`; const res = await runCmd("curl", [ "-sk", "-o", "/dev/null", "-w", "%{http_code}:%{size_download}", "-H", `Host: ${vhost}`, `http://${target}/`, ]); const parts = res.stdout.split(":"); const status = parts.length > 0 ? parseInt(parts[0], 10) : 0; const length = parts.length > 1 ? parseInt(parts[1], 10) : 0; // Only include results different from baseline if (Math.abs(length - baselineLength) > 20 || (status !== 0 && status !== 301 && status !== 404)) { results.push({ vhost, status, length, length_delta: length - baselineLength, }); } } const result = { baseline_length: baselineLength, results, unique_vhosts: results.map((r) => r.vhost), tested: subs.length, }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } - src/tools/recon.ts:128-142 (registration)Registration of the `recon_vhost` tool within the MCP server, including its schema/parameter definitions.
server.tool( "recon_vhost", "Brute-force virtual hosts by fuzzing the Host header. Returns baseline_length, results (vhost/status/length/length_delta), unique_vhosts, and tested count. Read-only, sends one request per wordlist entry.", { target: z .string() .describe("Target IP or domain to send requests to"), base_domain: z .string() .describe("Base domain for vhost names, e.g. hackycorp.com"), wordlist: z .string() .optional() .describe("Path to wordlist file. Uses built-in common subdomains if not provided."), },