Public/Reconnaissance/anonymous/Find-SubDomain.ps1
using namespace System.Management.Automation # Used for auto-generating the valid values for the Category parameter class SubdomainCategories : IValidateSetValuesGenerator { [string[]] GetValidValues() { # Get all category keys and add 'all' as an option $categories = @('all') $categories += $script:SessionVariables.subdomains.default.Keys return $categories } } function Find-SubDomain { [CmdletBinding()] [OutputType([System.Collections.ArrayList])] param ( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)] [Alias('domain-name', 'domain')] [ValidatePattern('^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$', ErrorMessage = 'Domain name must be in valid format (e.g., example.com)')] [string[]]$DomainName, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('cat', 'c')] [ValidateSet([SubdomainCategories])] [string]$Category = 'all', [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [Alias('word-list', 'w')] [string]$WordList, [Parameter(Mandatory = $false)] [Alias('throttle-limit', 'threads', 't')] [int]$ThrottleLimit = 100, [Parameter(Mandatory = $false)] [Alias('deep', 'd')] [switch]$DeepSearch, [Parameter(Mandatory = $false)] [Alias('json', 'raw')] [switch]$AsJson, [Parameter(Mandatory = $false)] [Alias('table', 'list')] [switch]$Detailed ) begin { Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)" $results = [System.Collections.ArrayList]::new() $type = if ($DeepSearch) { 'deep' } else { 'default' } if ($type -eq 'deep') { Write-Message -FunctionName $($MyInvocation.MyCommand.Name) "Deep search enabled" -Severity Information } } process { try { # Get subdomain list $subdomains = [System.Collections.Generic.HashSet[string]]::new() if ($WordList) { (Get-Content -Path $WordList) | ForEach-Object { [void]$subdomains.Add($_) } Write-Information "$($MyInvocation.MyCommand.Name): Loaded $($subdomains.Count) subdomains from '$WordList'" -InformationAction Continue } if ($Category -eq 'all') { foreach ($cat in $SessionVariables.subdomains[$type].Keys) { # Skip the 'common' category when 'all' is selected for improved performance if ($cat -ne 'common') { foreach ($sd in $SessionVariables.subdomains[$type].$cat) { [void]$subdomains.Add($sd) } } } } else { foreach ($sd in $SessionVariables.subdomains[$type].$Category) { [void]$subdomains.Add($sd) } } Write-Verbose "$($MyInvocation.MyCommand.Name): Loaded $($subdomains.Count) subdomains from session" # Process each domain in the array foreach ($domain in $DomainName) { Write-Verbose "$($MyInvocation.MyCommand.Name): Processing domain: $domain" $dnsNames = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($sd in $subdomains) { [void]$dnsNames.Add("$sd.$domain") } $totalDns = $dnsNames.Count Write-Verbose "$($MyInvocation.MyCommand.Name): Starting DNS resolution for $totalDns names for domain $domain..." $dnsNames | ForEach-Object -Parallel { $results = $using:results $type = $using:type $domain = $using:domain $subdomains = $using:SessionVariables.subdomains try { Write-Verbose "$($MyInvocation.MyCommand.Name): Resolving DNS for '$_'" $dnsInfo = [System.Net.Dns]::GetHostEntry($_) $ipAddress = $dnsInfo.AddressList.IpAddressToString $subdomain = $_.Split('.')[0] $uri = "https://$_" $hostName = $dnsInfo.HostName $foundCategory = $null foreach ($catLookup in $subdomains[$type].Keys) { # Skip the 'common' category when 'all' is selected if ($using:Category -eq 'all' -and $catLookup -eq 'common') { continue } if ($subdomains[$type].$catLookup -contains $subdomain) { $foundCategory = $catLookup Write-Verbose "Found category '$catLookup' for subdomain '$_'" break } } $resultObject = [PSCustomObject]@{ Domain = $domain Category = $foundCategory Url = $uri HostName = $hostName IpAddress = $ipAddress } [void]$results.Add($resultObject) } catch [System.Net.Sockets.SocketException] { Write-Verbose "$($MyInvocation.MyCommand.Name): DNS resolution failed for '$_' - $($_.Exception.Message)" } } -ThrottleLimit $ThrottleLimit } } catch { Write-Error $_.Exception.Message -ErrorAction Continue } } end { Write-Verbose "Function $($MyInvocation.MyCommand.Name) completed" if ($results.Count -gt 0) { if ($AsJson) { return ($results | ConvertTo-Json -Depth 4) } elseif ($Detailed) { return ($results | Format-Table -AutoSize) } else { return $results | Select-Object Domain, Category, Url } } else { Write-Information "No public resources found" -InformationAction Continue } } <# .SYNOPSIS Discovers active subdomains for specified domain names through DNS resolution. .DESCRIPTION Find-SubDomain performs subdomain enumeration by testing a list of common subdomain prefixes against one or more domain names. It resolves each potential subdomain via DNS lookups and reports those that successfully resolve. The function can use built-in subdomain lists organized by categories (such as 'common', 'dev', etc.) or a custom word list provided by the user. A 'deep' search option is available for more thorough enumeration with expanded subdomain lists. .PARAMETER DomainName One or more domain names to enumerate subdomains for (e.g., example.com). Must be in valid domain format (e.g., example.com). .PARAMETER Category The category of subdomains to check. Available options include 'all' and any categories defined in the session variables. Default is 'common'. .PARAMETER WordList Path to a custom file containing subdomain prefixes to check, one per line. When specified, these prefixes will be used in addition to any from the selected category. .PARAMETER ThrottleLimit Maximum number of concurrent DNS resolutions to perform. Default is 100. Adjust based on available system resources. .PARAMETER DeepSearch When specified, uses an expanded list of subdomains for more thorough enumeration. This significantly increases the number of DNS lookups performed. .PARAMETER AsJson Returns results in JSON format. Aliases: json, raw .PARAMETER Detailed Returns results in detailed table format. Aliases: table, list .EXAMPLE Find-SubDomain -DomainName example.com Checks for common subdomains of example.com. .EXAMPLE Find-SubDomain -DomainName example.com -Category dev Checks for development-related subdomains of example.com. .EXAMPLE Find-SubDomain -DomainName example.com,sample.org -DeepSearch Performs a deep search for subdomains on both example.com and sample.org. .EXAMPLE Find-SubDomain -DomainName example.com -WordList .\my-subdomains.txt -ThrottleLimit 50 Checks subdomains from a custom list against example.com with 50 concurrent threads. .OUTPUTS System.Collections.ArrayList Returns a collection of PSCustomObjects containing Domain, Category, Url, HostName, and IpAddress properties. #> } |