diff --git a/internal/runner/runner.go b/internal/runner/runner.go index d0e96a0f..1e43f8ec 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -77,13 +77,24 @@ func (r *Runner) RunEnumeration() { } // process http requests + var results bool for _, request := range template.RequestsHTTP { - r.processTemplateRequest(template, request) + results = r.processTemplateRequest(template, request) } - // process dns requests for _, request := range template.RequestsDNS { - r.processTemplateRequest(template, request) + dnsResults := r.processTemplateRequest(template, request) + if !results { + results = dnsResults + } + } + if !results { + if r.output != nil { + outputFile := r.output.Name() + r.output.Close() + os.Remove(outputFile) + } + gologger.Infof("No results found for the template. Happy hacking!") } return } @@ -95,6 +106,7 @@ func (r *Runner) RunEnumeration() { gologger.Fatalf("Could not evaluate template path '%s': %s\n", r.options.Templates, err) } + var results bool for _, match := range matches { template, err := templates.ParseTemplate(match) if err != nil { @@ -102,12 +114,26 @@ func (r *Runner) RunEnumeration() { return } for _, request := range template.RequestsDNS { - r.processTemplateRequest(template, request) + dnsResults := r.processTemplateRequest(template, request) + if dnsResults { + results = dnsResults + } } for _, request := range template.RequestsHTTP { - r.processTemplateRequest(template, request) + httpResults := r.processTemplateRequest(template, request) + if httpResults { + results = httpResults + } } } + if !results { + if r.output != nil { + outputFile := r.output.Name() + r.output.Close() + os.Remove(outputFile) + } + gologger.Infof("No results found for the templates. Happy hacking!") + } return } // If the template passed is a directory @@ -132,6 +158,7 @@ func (r *Runner) RunEnumeration() { if len(matches) == 0 { gologger.Fatalf("Error, no templates found in directory: '%s'\n", r.options.Templates) } + var results bool for _, match := range matches { template, err := templates.ParseTemplate(match) if err != nil { @@ -139,17 +166,31 @@ func (r *Runner) RunEnumeration() { return } for _, request := range template.RequestsDNS { - r.processTemplateRequest(template, request) + dnsResults := r.processTemplateRequest(template, request) + if dnsResults { + results = dnsResults + } } for _, request := range template.RequestsHTTP { - r.processTemplateRequest(template, request) + httpResults := r.processTemplateRequest(template, request) + if httpResults { + results = httpResults + } } } + if !results { + if r.output != nil { + outputFile := r.output.Name() + r.output.Close() + os.Remove(outputFile) + } + gologger.Infof("No results found for the template. Happy hacking!") + } return } // processTemplate processes a template and runs the enumeration on all the targets -func (r *Runner) processTemplateRequest(template *templates.Template, request interface{}) { +func (r *Runner) processTemplateRequest(template *templates.Template, request interface{}) bool { var file *os.File var err error @@ -162,12 +203,13 @@ func (r *Runner) processTemplateRequest(template *templates.Template, request in if err != nil { gologger.Fatalf("Could not open targets file '%s': %s\n", r.options.Targets, err) } - r.processTemplateWithList(template, request, file) + results := r.processTemplateWithList(template, request, file) file.Close() + return results } // processDomain processes the list with a template -func (r *Runner) processTemplateWithList(template *templates.Template, request interface{}, reader io.Reader) { +func (r *Runner) processTemplateWithList(template *templates.Template, request interface{}, reader io.Reader) bool { // Display the message for the template message := fmt.Sprintf("[%s] Loaded template %s (@%s)", template.ID, template.Info.Name, template.Info.Author) if template.Info.Severity != "" { @@ -209,7 +251,7 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i } if err != nil { gologger.Warningf("Could not create http client: %s\n", err) - return + return false } limiter := make(chan struct{}, r.options.Threads) @@ -242,4 +284,16 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i } close(limiter) wg.Wait() + + // See if we got any results from the executors + var results bool + if httpExecutor != nil { + results = httpExecutor.GotResults() + } + if dnsExecutor != nil { + if !results { + results = dnsExecutor.GotResults() + } + } + return results } diff --git a/pkg/executor/executer_http.go b/pkg/executor/executer_http.go index f5883e6c..c64c59e5 100644 --- a/pkg/executor/executer_http.go +++ b/pkg/executor/executer_http.go @@ -12,6 +12,7 @@ import ( "os" "strings" "sync" + "sync/atomic" "time" "github.com/pkg/errors" @@ -28,6 +29,7 @@ import ( // for a template. type HTTPExecutor struct { debug bool + results uint32 httpClient *retryablehttp.Client template *templates.Template httpRequest *requests.HTTPRequest @@ -68,6 +70,7 @@ func NewHTTPExecutor(options *HTTPOptions) (*HTTPExecutor, error) { executer := &HTTPExecutor{ debug: options.Debug, + results: 0, httpClient: client, template: options.Template, httpRequest: options.HTTPRequest, @@ -78,6 +81,14 @@ func NewHTTPExecutor(options *HTTPOptions) (*HTTPExecutor, error) { return executer, nil } +// GotResults returns true if there were any results for the executor +func (e *HTTPExecutor) GotResults() bool { + if atomic.LoadUint32(&e.results) == 0 { + return false + } + return true +} + // ExecuteHTTP executes the HTTP request on a URL func (e *HTTPExecutor) ExecuteHTTP(URL string) error { // Compile each request for the template based on the URL @@ -159,6 +170,7 @@ mainLoop: // write the first output then move to next matcher. if matcherCondition == matchers.ORCondition && len(e.httpRequest.Extractors) == 0 { e.writeOutputHTTP(compiledRequest, matcher, nil) + atomic.CompareAndSwapUint32(&e.results, 0, 1) } } } @@ -180,6 +192,7 @@ mainLoop: // AND or if we have extractors for the mechanism too. if len(e.httpRequest.Extractors) > 0 || matcherCondition == matchers.ANDCondition { e.writeOutputHTTP(compiledRequest, nil, extractorResults) + atomic.CompareAndSwapUint32(&e.results, 0, 1) } } diff --git a/pkg/executor/executor_dns.go b/pkg/executor/executor_dns.go index 10c7628f..d833ec41 100644 --- a/pkg/executor/executor_dns.go +++ b/pkg/executor/executor_dns.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "sync" + "sync/atomic" "github.com/pkg/errors" "github.com/projectdiscovery/gologger" @@ -18,6 +19,7 @@ import ( // for a template. type DNSExecutor struct { debug bool + results uint32 dnsClient *retryabledns.Client template *templates.Template dnsRequest *requests.DNSRequest @@ -48,6 +50,7 @@ func NewDNSExecutor(options *DNSOptions) *DNSExecutor { executer := &DNSExecutor{ debug: options.Debug, + results: 0, dnsClient: dnsClient, template: options.Template, dnsRequest: options.DNSRequest, @@ -57,6 +60,14 @@ func NewDNSExecutor(options *DNSOptions) *DNSExecutor { return executer } +// GotResults returns true if there were any results for the executor +func (e *DNSExecutor) GotResults() bool { + if atomic.LoadUint32(&e.results) == 0 { + return false + } + return true +} + // ExecuteDNS executes the DNS request on a URL func (e *DNSExecutor) ExecuteDNS(URL string) error { // Parse the URL and return domain if URL. @@ -104,6 +115,7 @@ func (e *DNSExecutor) ExecuteDNS(URL string) error { // write the first output then move to next matcher. if matcherCondition == matchers.ORCondition && len(e.dnsRequest.Extractors) == 0 { e.writeOutputDNS(domain, matcher, nil) + atomic.CompareAndSwapUint32(&e.results, 0, 1) } } } @@ -121,6 +133,7 @@ func (e *DNSExecutor) ExecuteDNS(URL string) error { // AND or if we have extractors for the mechanism too. if len(e.dnsRequest.Extractors) > 0 || matcherCondition == matchers.ANDCondition { e.writeOutputDNS(domain, nil, extractorResults) + atomic.CompareAndSwapUint32(&e.results, 0, 1) } return nil }