Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ vendor/
.idea
.devcontainer
.vscode
dist
dist
/subfinder
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ builds:
- darwin
goarch:
- amd64
- 386
- '386'
- arm
- arm64

Expand Down
24 changes: 13 additions & 11 deletions pkg/resolve/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,20 @@ type ResolutionPool struct {

// HostEntry defines a host with the source
type HostEntry struct {
Domain string
Host string
Source string
Domain string
Host string
Source string
WildcardCertificate bool
}

// Result contains the result for a host resolution
type Result struct {
Type ResultType
Host string
IP string
Error error
Source string
Type ResultType
Host string
IP string
Error error
Source string
WildcardCertificate bool
}

// ResultType is the type of result found
Expand Down Expand Up @@ -92,13 +94,13 @@ func (r *ResolutionPool) InitWildcards(domain string) error {
func (r *ResolutionPool) resolveWorker() {
for task := range r.Tasks {
if !r.removeWildcard {
r.Results <- Result{Type: Subdomain, Host: task.Host, IP: "", Source: task.Source}
r.Results <- Result{Type: Subdomain, Host: task.Host, IP: "", Source: task.Source, WildcardCertificate: task.WildcardCertificate}
continue
}

hosts, err := r.DNSClient.Lookup(task.Host)
if err != nil {
r.Results <- Result{Type: Error, Host: task.Host, Source: task.Source, Error: err}
r.Results <- Result{Type: Error, Host: task.Host, Source: task.Source, Error: err, WildcardCertificate: task.WildcardCertificate}
continue
}

Expand All @@ -116,7 +118,7 @@ func (r *ResolutionPool) resolveWorker() {
}

if !skip {
r.Results <- Result{Type: Subdomain, Host: task.Host, IP: hosts[0], Source: task.Source}
r.Results <- Result{Type: Subdomain, Host: task.Host, IP: hosts[0], Source: task.Source, WildcardCertificate: task.WildcardCertificate}
}
}
r.wg.Done()
Expand Down
2 changes: 1 addition & 1 deletion pkg/runner/banners.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const banner = `
const ToolName = `subfinder`

// Version is the current version of subfinder
const version = `v2.9.0`
const version = `v2.9.1-dev`

// showBanner is used to show the banner to the user
func showBanner() {
Expand Down
25 changes: 23 additions & 2 deletions pkg/runner/enumerate.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func (r *Runner) EnumerateSingleDomainWithCtx(ctx context.Context, domain string
gologger.Warning().Msgf("Encountered an error with source %s: %s\n", result.Source, result.Error)
case subscraping.Subdomain:
subdomain := replacer.Replace(result.Value)
// check if this subdomain is actually a wildcard subdomain
// that may have furthur subdomains associated with it
isWildcard := strings.Contains(result.Value, "*."+subdomain)

// Validate the subdomain found and remove wildcards from
if !strings.HasSuffix(subdomain, "."+domain) {
Expand All @@ -90,10 +93,17 @@ func (r *Runner) EnumerateSingleDomainWithCtx(ctx context.Context, domain string
// send the subdomain for resolution.
if _, ok := uniqueMap[subdomain]; ok {
skippedCounts[result.Source]++
// even if it is duplicate if it was not marked as wildcard before but this source says it is wildcard
// then we should mark it as wildcard
if !uniqueMap[subdomain].WildcardCertificate && isWildcard {
val := uniqueMap[subdomain]
val.WildcardCertificate = true
uniqueMap[subdomain] = val
}
continue
}

hostEntry := resolve.HostEntry{Domain: domain, Host: subdomain, Source: result.Source}
hostEntry := resolve.HostEntry{Domain: domain, Host: subdomain, Source: result.Source, WildcardCertificate: isWildcard}
if r.options.ResultCallback != nil && !r.options.RemoveWildcard {
r.options.ResultCallback(&hostEntry)
}
Expand All @@ -112,6 +122,7 @@ func (r *Runner) EnumerateSingleDomainWithCtx(ctx context.Context, domain string
if r.options.RemoveWildcard {
close(resolutionPool.Tasks)
}

wg.Done()
}()

Expand All @@ -129,11 +140,21 @@ func (r *Runner) EnumerateSingleDomainWithCtx(ctx context.Context, domain string
if _, ok := foundResults[result.Host]; !ok {
foundResults[result.Host] = result
if r.options.ResultCallback != nil {
r.options.ResultCallback(&resolve.HostEntry{Domain: domain, Host: result.Host, Source: result.Source})
r.options.ResultCallback(&resolve.HostEntry{Domain: domain, Host: result.Host, Source: result.Source, WildcardCertificate: result.WildcardCertificate})
}
}
}
}

// Merge wildcard certificate information from uniqueMap into foundResults
// This handles cases where a later source marked a subdomain as wildcard
// after it was already sent to the resolution pool
for host, result := range foundResults {
if entry, ok := uniqueMap[host]; ok && entry.WildcardCertificate && !result.WildcardCertificate {
result.WildcardCertificate = true
foundResults[host] = result
}
}
}
wg.Wait()
outputWriter := NewOutputWriter(r.options.JSON)
Expand Down
28 changes: 16 additions & 12 deletions pkg/runner/outputter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,25 @@ type OutputWriter struct {
}

type jsonSourceResult struct {
Host string `json:"host"`
Input string `json:"input"`
Source string `json:"source"`
Host string `json:"host"`
Input string `json:"input"`
Source string `json:"source"`
WildcardCertificate bool `json:"wildcard_certificate,omitempty"`
}

type jsonSourceIPResult struct {
Host string `json:"host"`
IP string `json:"ip"`
Input string `json:"input"`
Source string `json:"source"`
Host string `json:"host"`
IP string `json:"ip"`
Input string `json:"input"`
Source string `json:"source"`
WildcardCertificate bool `json:"wildcard_certificate,omitempty"`
}

type jsonSourcesResult struct {
Host string `json:"host"`
Input string `json:"input"`
Sources []string `json:"sources"`
Host string `json:"host"`
Input string `json:"input"`
Sources []string `json:"sources"`
WildcardCertificate bool `json:"wildcard_certificate,omitempty"`
}

// NewOutputWriter creates a new OutputWriter
Expand Down Expand Up @@ -117,7 +120,7 @@ func writeJSONHostIP(input string, results map[string]resolve.Result, writer io.
data.IP = result.IP
data.Input = input
data.Source = result.Source

data.WildcardCertificate = result.WildcardCertificate
err := encoder.Encode(&data)
if err != nil {
return err
Expand All @@ -130,7 +133,7 @@ func writeJSONHostIP(input string, results map[string]resolve.Result, writer io.
func (o *OutputWriter) WriteHostNoWildcard(input string, results map[string]resolve.Result, writer io.Writer) error {
hosts := make(map[string]resolve.HostEntry)
for host, result := range results {
hosts[host] = resolve.HostEntry{Domain: host, Host: result.Host, Source: result.Source}
hosts[host] = resolve.HostEntry{Domain: host, Host: result.Host, Source: result.Source, WildcardCertificate: result.WildcardCertificate}
}

return o.WriteHost(input, hosts, writer)
Expand Down Expand Up @@ -175,6 +178,7 @@ func writeJSONHost(input string, results map[string]resolve.HostEntry, writer io
data.Host = result.Host
data.Input = input
data.Source = result.Source
data.WildcardCertificate = result.WildcardCertificate
err := encoder.Encode(data)
if err != nil {
return err
Expand Down
Loading