Public/Reconnaissance/anonymous/Find-DnsRecords.ps1
using namespace System.Management.Automation class SubdomainCategories : IValidateSetValuesGenerator { [string[]] GetValidValues() { $categories = @('all') if ($script:SessionVariables -and $script:SessionVariables.subdomains -and $script:SessionVariables.subdomains.default) { $categories += $script:SessionVariables.subdomains.default.Keys } return $categories } } function Find-DnsRecords { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias("d", "domain")] [string[]]$Domains, [Parameter(Mandatory = $false)] [ValidateRange(1, 300)] [Alias("delay-min", "min-delay")] [int]$MinDelay = 1, [Parameter(Mandatory = $false)] [ValidateRange(1, 600)] [Alias("delay-max", "max-delay")] [int]$MaxDelay = 3, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias("log-path", "l", "log")] [string]$LogPath = ".\recon_results.log", [Parameter(Mandatory = $false)] [ValidateSet("A", "AAAA", "CNAME", "MX", "NS", "TXT", "SOA", "PTR")] [Alias("record-types", "types", "r")] [string[]]$RecordTypes = @("A", "AAAA", "CNAME", "MX", "TXT"), [Parameter(Mandatory = $false)] [Alias("fast", "f")] [switch]$FastMode, [Parameter(Mandatory = $false)] [ValidateSet("Minimal", "Standard", "Detailed")] [Alias("info-level", "level")] [string]$DNSInfoLevel = "Standard", [Parameter(Mandatory = $false)] [ValidateSet("Object", "JSON", "CSV", "Table")] [Alias("output", "o")] [string]$OutputFormat = "Table", [Parameter(Mandatory = $false)] [Alias("enum-subdomains", "subdomains", "s")] [switch]$EnumerateSubdomains, [Parameter(Mandatory = $false)] [ValidateRange(1, 200)] [Alias("subdomain-throttle", "throttle-limit", "t")] [int]$SubdomainThrottleLimit = 50, [Parameter(Mandatory = $false)] [ValidateSet([SubdomainCategories])] [Alias("subdomain-cat", "cat", "c")] [string]$SubdomainCategory = "common", [Parameter(Mandatory = $false)] [Alias("deep-search", "deep", "ds")] [switch]$DeepSubdomainSearch ) begin { Write-Verbose "Starting DNS reconnaissance with enhanced provider support" if ($MinDelay -gt $MaxDelay) { throw "MinDelay cannot be greater than MaxDelay" } if ($EnumerateSubdomains -and $SubdomainThrottleLimit -gt 200) { Write-Warning "High subdomain throttle limit may cause rate limiting. Consider reducing to 100 or less." } if ($DeepSubdomainSearch -and -not $EnumerateSubdomains) { Write-Warning "DeepSubdomainSearch requires EnumerateSubdomains to be enabled. Enabling subdomain enumeration." $EnumerateSubdomains = $true } $DNSProviders = @{ "Cloudflare" = @{ URL = "https://cloudflare-dns.com/dns-query"; Region = "Global"; Type = "Commercial"; Reliability = 99.9 } "Google" = @{ URL = "https://dns.google/resolve"; Region = "Global"; Type = "Commercial"; Reliability = 99.8 } "NextDNS" = @{ URL = "https://dns.nextdns.io"; Region = "Global"; Type = "Privacy"; Reliability = 99.5 } "DNS.SB" = @{ URL = "https://doh.dns.sb/dns-query"; Region = "Global"; Type = "Privacy"; Reliability = 99.3 } "DNSPod" = @{ URL = "https://dns.pub/dns-query"; Region = "China/Global"; Type = "Commercial"; Reliability = 99.0 } } # User agents - integrate with BlackCat module if available (replaces UseRandomUserAgent parameter) if ($BlackCatAvailable -and $script:SessionVariables -and $script:SessionVariables.userAgents) { Write-Verbose "Using BlackCat module user agents from session variables" $UserAgents = $script:SessionVariables.userAgents.agents | ForEach-Object { $_.value } } else { Write-Verbose "Using built-in user agents (anonymous mode)" $UserAgents = @( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0" ) } # Initialize results and configuration $Results = [System.Collections.ArrayList]::new() $Stats = @{ TotalQueries = 0; SuccessfulQueries = 0; FailedQueries = 0; StartTime = Get-Date } $Config = @{ MinDelay = if ($FastMode) { 0.5 } else { $MinDelay } MaxDelay = if ($FastMode) { 1 } else { $MaxDelay } Timeout = if ($FastMode) { 2 } else { 10 } } } process { foreach ($Domain in $Domains) { Write-Host "🎯 Analyzing domain: $Domain" -ForegroundColor Green # Rotate through providers for load balancing $ProviderNames = $DNSProviders.Keys | Sort-Object { Get-Random } # Build list of domains to query (root domain + subdomains if enabled) $DomainsToQuery = @($Domain) # Add subdomain enumeration for CNAME discovery if ($EnumerateSubdomains -and $RecordTypes -contains "CNAME") { Write-Host " 🔍 Enumerating subdomains for CNAME discovery..." -ForegroundColor Cyan # Determine subdomain type based on DeepSubdomainSearch parameter $SubdomainType = if ($DeepSubdomainSearch) { 'deep' } else { 'default' } # Get subdomain list from session variables (same logic as Find-SubDomain) $SubdomainList = if ($script:SessionVariables -and $script:SessionVariables.subdomains) { Write-Verbose "Using session variable subdomain list (type: $SubdomainType, category: $SubdomainCategory)" $subdomains = [System.Collections.Generic.HashSet[string]]::new() if ($SubdomainCategory -eq 'all') { foreach ($cat in $script:SessionVariables.subdomains[$SubdomainType].Keys) { # Skip the 'common' category when 'all' is selected for improved performance if ($cat -ne 'common') { foreach ($sd in $script:SessionVariables.subdomains[$SubdomainType].$cat) { [void]$subdomains.Add($sd) } } } } else { if ($script:SessionVariables.subdomains[$SubdomainType].ContainsKey($SubdomainCategory)) { foreach ($sd in $script:SessionVariables.subdomains[$SubdomainType].$SubdomainCategory) { [void]$subdomains.Add($sd) } } else { Write-Warning "Category '$SubdomainCategory' not found in session variables. Available categories: $($script:SessionVariables.subdomains[$SubdomainType].Keys -join ', ')" $subdomains = $null } } if ($subdomains) { [array]$subdomains } else { $null } } else { Write-Warning "Session variables not available. Cannot enumerate subdomains without BlackCat framework." $null } if (-not $SubdomainList) { Write-Host " ⚠️ Subdomain enumeration skipped - no subdomain data available" -ForegroundColor Yellow continue } # Generate subdomain candidates $SubdomainCandidates = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($subdomain in $SubdomainList) { [void]$SubdomainCandidates.Add("$subdomain.$Domain") } $SearchTypeMsg = if ($DeepSubdomainSearch) { "deep search" } else { "standard search" } Write-Host " 🎯 Testing $($SubdomainCandidates.Count) subdomain candidates ($SearchTypeMsg, category: $SubdomainCategory)..." -ForegroundColor Yellow # Test subdomains in parallel for existence first (faster DNS resolution check) $ValidSubdomains = $SubdomainCandidates | ForEach-Object -Parallel { try { $null = [System.Net.Dns]::GetHostEntry($_) return $_ } catch { # Subdomain doesn't resolve, skip it return $null } } -ThrottleLimit $SubdomainThrottleLimit | Where-Object { $_ -ne $null } $DomainsToQuery += $ValidSubdomains Write-Host " ✅ Found $($ValidSubdomains.Count) valid subdomains" -ForegroundColor Green } foreach ($RecordType in $RecordTypes) { Write-Host " 🔍 Querying $RecordType records..." -ForegroundColor Yellow # Query each domain (root + valid subdomains) foreach ($QueryDomain in $DomainsToQuery) { # Skip subdomain enumeration for non-CNAME record types on subdomains if ($QueryDomain -ne $Domain -and $RecordType -ne "CNAME") { continue } if ($DNSInfoLevel -eq "Detailed" -and $QueryDomain -ne $Domain) { Write-Host " 🔍 Querying $RecordType for subdomain: $QueryDomain" -ForegroundColor Gray } foreach ($ProviderName in $ProviderNames) { $Provider = $DNSProviders[$ProviderName] if ($DNSInfoLevel -eq "Detailed") { Write-Host " 📡 Using $ProviderName ($($Provider.Reliability)%)" -ForegroundColor Cyan } try { $Stats.TotalQueries++ # JSON DNS query $QueryUrl = "$($Provider.URL)?name=$QueryDomain&type=$RecordType" $Headers = @{ "Accept" = "application/dns-json" "User-Agent" = ($UserAgents | Get-Random) } $Response = Invoke-RestMethod -Uri $QueryUrl -Headers $Headers -TimeoutSec $Config.Timeout -ErrorAction Stop # Debug output for CNAME queries if ($DNSInfoLevel -eq "Detailed" -and $RecordType -eq "CNAME") { Write-Host " 🔍 Query URL: $QueryUrl" -ForegroundColor Gray Write-Host " 🔍 Response has $($Response.Answer.Count) answers" -ForegroundColor Gray } # ... existing code for processing Response.Answer ... # For CNAME queries, also try with DO (DNSSEC OK) bit to get unfiltered responses if ($RecordType -eq "CNAME" -and -not $Response.Answer) { try { $DnssecQueryUrl = "$($Provider.URL)?name=$QueryDomain&type=$RecordType&do=true" $DnssecResponse = Invoke-RestMethod -Uri $DnssecQueryUrl -Headers $Headers -TimeoutSec $Config.Timeout -ErrorAction Stop if ($DnssecResponse.Answer) { $Response = $DnssecResponse if ($DNSInfoLevel -eq "Detailed") { Write-Host " 🔍 DNSSEC query returned $($Response.Answer.Count) answers" -ForegroundColor Gray } } } catch { # DNSSEC query failed, continue with original response if ($DNSInfoLevel -eq "Detailed") { Write-Host " 🔍 DNSSEC query failed: $($_.Exception.Message)" -ForegroundColor Gray } } # If still no CNAME found, try querying for A records to detect proxied CNAMEs if (-not $Response.Answer) { try { $AQueryUrl = "$($Provider.URL)?name=$QueryDomain&type=A" $AResponse = Invoke-RestMethod -Uri $AQueryUrl -Headers $Headers -TimeoutSec $Config.Timeout -ErrorAction Stop if ($AResponse.Answer) { # Check if the A records point to CDN IPs (indicating CNAME flattening/proxying) $CDNRecords = $AResponse.Answer | Where-Object { $_.type -eq 1 -and ( # Cloudflare IP ranges (more comprehensive) ($_.data -match '^(104\.1[6-9]|104\.2[0-7]|104\.21|104\.22|104\.23|104\.24|104\.25|104\.26|104\.27|104\.28|104\.29|104\.30|104\.31|108\.162|141\.101|162\.15[8-9]|172\.6[4-7]|172\.64|172\.65|172\.66|172\.67|172\.68|172\.69|172\.70|172\.71|173\.245|188\.114|190\.93|197\.234|198\.41|131\.0|203\.28)\.') -or # AWS CloudFront ($_.data -match '^(13\.32|13\.35|52\.8[4-5]|54\.23[0-9]|99\.8[4-6]|143\.204|205\.251)\.') -or # Fastly ($_.data -match '^(23\.235|151\.101)\.') -or # KeyCDN ($_.data -match '^(95\.85|104\.16)\.') -or # MaxCDN/StackPath ($_.data -match '^(66\.254|68\.232)\.') ) } if ($CDNRecords) { $Response = $AResponse if ($DNSInfoLevel -eq "Detailed") { Write-Host " 🔍 A record query returned $($CDNRecords.Count) CDN records (likely flattened CNAME)" -ForegroundColor Gray } } } } catch { if ($DNSInfoLevel -eq "Detailed") { Write-Host " 🔍 A record fallback query failed: $($_.Exception.Message)" -ForegroundColor Gray } } } } if ($Response.Answer) { $FoundRecord = $false foreach ($Answer in $Response.Answer) { # More flexible record type matching - check both by type number and handle edge cases $ExpectedType = (@{"A"=1;"AAAA"=28;"CNAME"=5;"MX"=15;"NS"=2;"TXT"=16;"SOA"=6;"PTR"=12}[$RecordType]) # Debug output for CNAME queries when in detailed mode if ($DNSInfoLevel -eq "Detailed" -and $RecordType -eq "CNAME") { Write-Host " 🔍 Answer type: $($Answer.type), Expected: $ExpectedType, Data: $($Answer.data)" -ForegroundColor Gray } # For CNAME queries, check if this is actually a CNAME record if ($RecordType -eq "CNAME" -and $Answer.type -eq 5) { $null = $Results.Add([PSCustomObject]@{ Domain = $QueryDomain RecordType = $RecordType Data = $Answer.data TTL = $Answer.TTL DNSProvider = $ProviderName ProviderType = $Provider.Type ProviderRegion = $Provider.Region Reliability = $Provider.Reliability Timestamp = Get-Date QueryMethod = "DoH-JSON" IsSubdomain = ($QueryDomain -ne $Domain) }) $Stats.SuccessfulQueries++ $DisplayDomain = if ($QueryDomain -ne $Domain) { $QueryDomain } else { $QueryDomain } Write-Host " ✅ $DisplayDomain -> $($Answer.data) (TTL: $($Answer.TTL))" -ForegroundColor Green $FoundRecord = $true } # For non-CNAME queries or exact type matches elseif ($Answer.type -eq $ExpectedType) { $null = $Results.Add([PSCustomObject]@{ Domain = $QueryDomain RecordType = $RecordType Data = $Answer.data TTL = $Answer.TTL DNSProvider = $ProviderName ProviderType = $Provider.Type ProviderRegion = $Provider.Region Reliability = $Provider.Reliability Timestamp = Get-Date QueryMethod = "DoH-JSON" IsSubdomain = ($QueryDomain -ne $Domain) }) $Stats.SuccessfulQueries++ $DisplayDomain = if ($QueryDomain -ne $Domain) { $QueryDomain } else { $QueryDomain } Write-Host " ✅ $DisplayDomain -> $($Answer.data) (TTL: $($Answer.TTL))" -ForegroundColor Green $FoundRecord = $true } } # Special handling for CNAME queries that might be resolved to final A/AAAA records if ($RecordType -eq "CNAME" -and -not $FoundRecord) { # Check if we got A or AAAA records when querying for CNAME (indicates proxied/resolved CNAME) $ProxiedRecords = $Response.Answer | Where-Object { $_.type -eq 1 -or $_.type -eq 28 } if ($ProxiedRecords) { foreach ($ProxiedRecord in $ProxiedRecords) { # Try to detect if this is likely a proxied CNAME by checking common CDN patterns $IsLikelyProxied = $false $ProxyService = "Unknown" # Comprehensive Cloudflare IP ranges if ($ProxiedRecord.data -match '^(104\.1[6-9]|104\.2[0-7]|104\.21|104\.22|104\.23|104\.24|104\.25|104\.26|104\.27|104\.28|104\.29|104\.30|104\.31|108\.162|141\.101|162\.15[8-9]|172\.6[4-7]|172\.64|172\.65|172\.66|172\.67|172\.68|172\.69|172\.70|172\.71|173\.245|188\.114|190\.93|197\.234|198\.41|131\.0|203\.28)\.') { $IsLikelyProxied = $true $ProxyService = "Cloudflare" } # AWS CloudFront patterns elseif ($ProxiedRecord.data -match '^(13\.32|13\.35|52\.8[4-5]|54\.23[0-9]|99\.8[4-6]|143\.204|205\.251)\.') { $IsLikelyProxied = $true $ProxyService = "AWS CloudFront" } # Fastly CDN elseif ($ProxiedRecord.data -match '^(23\.235|151\.101)\.') { $IsLikelyProxied = $true $ProxyService = "Fastly" } # Common other CDN patterns can be added here if ($IsLikelyProxied) { $null = $Results.Add([PSCustomObject]@{ Domain = $QueryDomain RecordType = "CNAME (Flattened)" Data = "$($ProxiedRecord.data) [$ProxyService Flattened/Proxied]" TTL = $ProxiedRecord.TTL DNSProvider = $ProviderName ProviderType = $Provider.Type ProviderRegion = $Provider.Region Reliability = $Provider.Reliability Timestamp = Get-Date QueryMethod = "DoH-JSON" IsSubdomain = ($QueryDomain -ne $Domain) }) $Stats.SuccessfulQueries++ $DisplayDomain = if ($QueryDomain -ne $Domain) { $QueryDomain } else { $QueryDomain } Write-Host " 🔶 $DisplayDomain -> $($ProxiedRecord.data) [$ProxyService Flattened CNAME] (TTL: $($ProxiedRecord.TTL))" -ForegroundColor DarkYellow $FoundRecord = $true } } } } } else { # Handle case where no records are found if ($DNSInfoLevel -ne "Minimal" -and $QueryDomain -eq $Domain) { Write-Host " ℹ️ No $RecordType records found" -ForegroundColor Gray } } # Add delay between requests if ($Config.MaxDelay -gt 0) { $Delay = Get-Random -Minimum $Config.MinDelay -Maximum $Config.MaxDelay Start-Sleep -Seconds $Delay } # Break after successful response (whether records found or authoritative no-records) # Only continue to next provider on actual HTTP/network failures break # Success - got authoritative response from DNS provider } catch { $Stats.FailedQueries++ if ($DNSInfoLevel -eq "Detailed") { Write-Host " ❌ Query failed: $($_.Exception.Message)" -ForegroundColor Red } } } } } } } end { $Duration = (Get-Date) - $Stats.StartTime Write-Host "`n📊 Reconnaissance Summary:" -ForegroundColor Magenta Write-Host " Total Queries: $($Stats.TotalQueries)" -ForegroundColor White Write-Host " Successful: $($Stats.SuccessfulQueries)" -ForegroundColor Green Write-Host " Failed: $($Stats.FailedQueries)" -ForegroundColor Red Write-Host " Duration: $($Duration.TotalSeconds.ToString('F2')) seconds" -ForegroundColor White Write-Host " Records Found: $($Results.Count)" -ForegroundColor Yellow if ($EnumerateSubdomains) { $SubdomainResults = $Results | Where-Object { $_.IsSubdomain -eq $true } Write-Host " Subdomain Records: $($SubdomainResults.Count)" -ForegroundColor Cyan } # Return results in requested format switch ($OutputFormat) { "JSON" { return $Results | ConvertTo-Json -Depth 3 } "CSV" { return $Results | ConvertTo-CSV } "Object" { return $Results } "Table" { return $Results | Format-Table -AutoSize } } } } |