UNPKG

domainlooker

Version:

🕵️ Mission-critical domain intelligence gathering tool with spy-themed interface. Comprehensive WHOIS, DNS, SSL, network analysis, subdomain discovery, and API-ready JSON exports.

202 lines 9.21 kB
import { promises as dns } from 'dns'; import axios from 'axios'; export class SubdomainService { constructor() { this.commonSubdomains = [ 'www', 'mail', 'ftp', 'localhost', 'webmail', 'smtp', 'pop', 'ns1', 'webdisk', 'ns2', 'cpanel', 'whm', 'autodiscover', 'autoconfig', 'mobile', 'm', 'dev', 'staging', 'test', 'api', 'admin', 'blog', 'shop', 'cdn', 'assets', 'static', 'img', 'images', 'media', 'secure', 'vpn', 'remote', 'support', 'help', 'docs', 'status', 'monitor', 'demo', 'beta', 'alpha', 'preview', 'sandbox', 'app', 'portal', 'dashboard', 'panel', 'control', 'git', 'repo', 'code', 'build', 'ci', 'jenkins', 'gitlab', 'github', 'bitbucket', 'email', 'imap', 'pop3', 'exchange', 'outlook', 'office', 'calendar', 'contacts', 'intranet', 'extranet', 'internal', 'external', 'public', 'private', 'secure2', 'news', 'press', 'media2', 'content', 'cms', 'editor', 'author', 'writer', 'video', 'stream', 'live', 'broadcast', 'radio', 'podcast', 'audio', 'music', 'files', 'download', 'upload', 'share', 'cloud', 'storage', 'backup', 'archive', 'search', 'find', 'directory', 'index', 'catalog', 'library', 'database', 'db', 'forum', 'community', 'social', 'chat', 'discuss', 'feedback', 'comments', 'reviews' ]; } async discoverSubdomains(domain) { const sources = { dnsEnumeration: [], certificateTransparency: [], commonNames: [] }; // Run all discovery methods in parallel const [dnsResults, ctResults, commonResults] = await Promise.allSettled([ this.dnsEnumeration(domain), this.certificateTransparencyLookup(domain), this.commonSubdomainCheck(domain) ]); if (dnsResults.status === 'fulfilled') { sources.dnsEnumeration = dnsResults.value; } if (ctResults.status === 'fulfilled') { sources.certificateTransparency = ctResults.value; } if (commonResults.status === 'fulfilled') { sources.commonNames = commonResults.value; } // Combine and deduplicate all found subdomains const allSubdomains = new Set([ ...sources.dnsEnumeration, ...sources.certificateTransparency, ...sources.commonNames ]); const subdomains = Array.from(allSubdomains).sort(); return { subdomains, sources, totalFound: subdomains.length }; } async dnsEnumeration(domain) { const subdomains = []; try { // Try to get NS records and enumerate from them const nsRecords = await dns.resolveNs(domain).catch(() => []); // Check for wildcard DNS const wildcardTest = await this.checkWildcardDNS(domain); if (!wildcardTest) { // Perform zone transfer attempt (usually blocked but worth trying) await this.attemptZoneTransfer(domain, nsRecords, subdomains); } // Try some advanced DNS techniques await this.tryDNSBruteforce(domain, subdomains); } catch (error) { // DNS enumeration failed, continue with other methods } return subdomains; } async certificateTransparencyLookup(domain) { const subdomains = []; try { // Use crt.sh API for certificate transparency logs const response = await axios.get(`https://crt.sh/?q=${encodeURIComponent(domain)}&output=json`, { timeout: 10000, headers: { 'User-Agent': 'DOMAINLOOKER/1.0 Security Research Tool' } }); if (response.data && Array.isArray(response.data)) { const certificates = response.data; const subdomainSet = new Set(); certificates.forEach((cert) => { if (cert.name_value) { const names = cert.name_value.split('\n'); names.forEach((name) => { name = name.trim().toLowerCase(); // Filter valid subdomains if (name.endsWith(`.${domain}`) && name !== domain) { // Remove wildcards and get the subdomain const cleanName = name.replace(/^\*\./, ''); if (cleanName !== domain && this.isValidSubdomain(cleanName, domain)) { subdomainSet.add(cleanName); } } }); } }); subdomains.push(...Array.from(subdomainSet)); } } catch (error) { // Certificate transparency lookup failed } return subdomains; } async commonSubdomainCheck(domain) { const foundSubdomains = []; const batchSize = 10; // Process in batches to avoid overwhelming DNS servers for (let i = 0; i < this.commonSubdomains.length; i += batchSize) { const batch = this.commonSubdomains.slice(i, i + batchSize); const batchPromises = batch.map(subdomain => this.checkSubdomain(subdomain, domain)); const results = await Promise.allSettled(batchPromises); results.forEach((result, index) => { if (result.status === 'fulfilled' && result.value) { foundSubdomains.push(`${batch[index]}.${domain}`); } }); // Small delay between batches to be respectful if (i + batchSize < this.commonSubdomains.length) { await new Promise(resolve => setTimeout(resolve, 100)); } } return foundSubdomains; } async checkSubdomain(subdomain, domain) { const fullDomain = `${subdomain}.${domain}`; try { // Try both A and AAAA records const [aRecords, aaaaRecords] = await Promise.allSettled([ dns.resolve4(fullDomain), dns.resolve6(fullDomain) ]); return aRecords.status === 'fulfilled' || aaaaRecords.status === 'fulfilled'; } catch (error) { return false; } } async checkWildcardDNS(domain) { try { // Check if a random subdomain resolves (indicating wildcard DNS) const randomSubdomain = `random-${Date.now()}-test.${domain}`; await dns.resolve4(randomSubdomain); return true; // Wildcard DNS detected } catch (error) { return false; // No wildcard DNS } } async attemptZoneTransfer(domain, nsRecords, subdomains) { // Zone transfer attempts (usually blocked but educational) // This is a placeholder - actual zone transfer would require more complex DNS queries // Most modern DNS servers block zone transfers for security reasons } async tryDNSBruteforce(domain, subdomains) { // Additional DNS techniques could be added here // Like trying numerical subdomains, etc. } isValidSubdomain(subdomain, baseDomain) { // Basic validation for subdomain format if (!subdomain || subdomain === baseDomain) return false; // Check if it's actually a subdomain of the base domain if (!subdomain.endsWith(`.${baseDomain}`)) return false; // Remove the base domain part const subPart = subdomain.replace(`.${baseDomain}`, ''); // Check for valid subdomain characters const validSubdomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/; return validSubdomainRegex.test(subPart); } // Get subdomain statistics getSubdomainStats(subdomainData) { const stats = { bySource: { 'DNS Enumeration': subdomainData.sources.dnsEnumeration.length, 'Certificate Transparency': subdomainData.sources.certificateTransparency.length, 'Common Names': subdomainData.sources.commonNames.length }, commonPatterns: {}, depthAnalysis: {} }; // Analyze common patterns subdomainData.subdomains.forEach(subdomain => { const parts = subdomain.split('.'); const depth = parts.length - 2; // Subtract base domain parts stats.depthAnalysis[depth] = (stats.depthAnalysis[depth] || 0) + 1; // Extract first level subdomain for pattern analysis if (parts.length >= 3) { const firstLevel = parts[parts.length - 3]; // e.g., 'www' from 'www.example.com' stats.commonPatterns[firstLevel] = (stats.commonPatterns[firstLevel] || 0) + 1; } }); return stats; } } //# sourceMappingURL=subdomain.js.map