diff --git a/v2/internal/runner/config.go b/v2/internal/runner/config.go index ccee2c93..8dca643e 100644 --- a/v2/internal/runner/config.go +++ b/v2/internal/runner/config.go @@ -74,7 +74,7 @@ const nucleiIgnoreFile = ".nuclei-ignore" // readNucleiIgnoreFile reads the nuclei ignore file marking it in map func (r *Runner) readNucleiIgnoreFile() { - file, err := os.Open(path.Join(r.templatesConfig.TemplatesDirectory, nucleiIgnoreFile)) + file, err := os.Open(r.getIgnoreFilePath()) if err != nil { return } @@ -116,3 +116,21 @@ func (r *Runner) checkIfInNucleiIgnore(item string) bool { return false } + +func (r *Runner) getIgnoreFilePath() string { + defIgnoreFilePath := path.Join(r.templatesConfig.TemplatesDirectory, nucleiIgnoreFile) + + cwd, err := os.Getwd() + if err != nil { + return defIgnoreFilePath + } + + cwdIgnoreFilePath := path.Join(cwd, nucleiIgnoreFile) + + cwdIfpInfo, err := os.Stat(cwdIgnoreFilePath) + if os.IsNotExist(err) || cwdIfpInfo.IsDir() { + return defIgnoreFilePath + } + + return cwdIgnoreFilePath +} diff --git a/v2/internal/runner/options.go b/v2/internal/runner/options.go index 423161d0..bbe66a46 100644 --- a/v2/internal/runner/options.go +++ b/v2/internal/runner/options.go @@ -13,32 +13,31 @@ import ( // Options contains the configuration options for tuning // the template requesting process. type Options struct { - Debug bool // Debug mode allows debugging request/responses for the engine - Silent bool // Silent suppresses any extra text and only writes found URLs on screen. - Version bool // Version specifies if we should just show version and exit - Verbose bool // Verbose flag indicates whether to show verbose output or not - NoColor bool // No-Color disables the colored output. - UpdateTemplates bool // UpdateTemplates updates the templates installed at startup - JSON bool // JSON writes json output to files - JSONRequests bool // write requests/responses for matches in JSON output - EnableProgressBar bool // Enable progrss bar - TemplateList bool // List available templates - - Stdin bool // Stdin specifies whether stdin input was given to the process Templates multiStringFlag // Signature specifies the template/templates to use ExcludedTemplates multiStringFlag // Signature specifies the template/templates to exclude + CustomHeaders requests.CustomHeaders // Custom global headers Severity string // Filter templates based on their severity and only run the matching ones. Target string // Target is a single URL/Domain to scan usng a template Targets string // Targets specifies the targets to scan using templates. - Threads int // Thread controls the number of concurrent requests to make. - Timeout int // Timeout is the seconds to wait for a response from the server. - Retries int // Retries is the number of times to retry the request Output string // Output is the file to write found subdomains to. ProxyURL string // ProxyURL is the URL for the proxy server ProxySocksURL string // ProxySocksURL is the URL for the proxy socks server - CustomHeaders requests.CustomHeaders // Custom global headers TemplatesDirectory string // TemplatesDirectory is the directory to use for storing templates + Threads int // Thread controls the number of concurrent requests to make. + Timeout int // Timeout is the seconds to wait for a response from the server. + Retries int // Retries is the number of times to retry the request RateLimit int // Rate-Limit of requests per specified target + Debug bool // Debug mode allows debugging request/responses for the engine + Silent bool // Silent suppresses any extra text and only writes found URLs on screen. + Version bool // Version specifies if we should just show version and exit + Verbose bool // Verbose flag indicates whether to show verbose output or not + NoColor bool // No-Color disables the colored output. + UpdateTemplates bool // UpdateTemplates updates the templates installed at startup + JSON bool // JSON writes json output to files + JSONRequests bool // write requests/responses for matches in JSON output + EnableProgressBar bool // Enable progrss bar + TemplateList bool // List available templates + Stdin bool // Stdin specifies whether stdin input was given to the process StopAtFirstMatch bool // Stop processing template at first full match (this may break chained requests) BulkSize int // Number of targets analyzed in parallel for each template } diff --git a/v2/internal/runner/processor.go b/v2/internal/runner/processor.go index 8b3d0656..02b80406 100644 --- a/v2/internal/runner/processor.go +++ b/v2/internal/runner/processor.go @@ -88,7 +88,7 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat go func(URL string) { defer wg.Done() - var result executer.Result + var result *executer.Result if httpExecuter != nil { result = httpExecuter.ExecuteHTTP(p, URL) diff --git a/v2/pkg/executer/executer_dns.go b/v2/pkg/executer/executer_dns.go index 4a8baf00..0044bfba 100644 --- a/v2/pkg/executer/executer_dns.go +++ b/v2/pkg/executer/executer_dns.go @@ -77,7 +77,9 @@ func NewDNSExecuter(options *DNSOptions) *DNSExecuter { } // ExecuteDNS executes the DNS request on a URL -func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) (result Result) { +func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) *Result { + result := &Result{} + // Parse the URL and return domain if URL. var domain string if isURL(reqURL) { @@ -93,7 +95,7 @@ func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) (result Re p.Drop(1) - return + return result } if e.debug { @@ -108,7 +110,7 @@ func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) (result Re p.Drop(1) - return + return result } p.Update() @@ -127,7 +129,7 @@ func (e *DNSExecuter) ExecuteDNS(p progress.IProgress, reqURL string) (result Re if !matcher.MatchDNS(resp) { // If the condition is AND we haven't matched, return. if matcherCondition == matchers.ANDCondition { - return + return result } } else { // If the matcher has matched, and its an OR diff --git a/v2/pkg/executer/executer_http.go b/v2/pkg/executer/executer_http.go index 6934f941..2ac615ae 100644 --- a/v2/pkg/executer/executer_http.go +++ b/v2/pkg/executer/executer_http.go @@ -40,42 +40,41 @@ const ( // HTTPExecuter is client for performing HTTP requests // for a template. type HTTPExecuter struct { - coloredOutput bool - debug bool - Results bool - jsonOutput bool - jsonRequest bool - httpClient *retryablehttp.Client - rawHttpClient *rawhttp.Client - template *templates.Template - bulkHTTPRequest *requests.BulkHTTPRequest - writer *bufwriter.Writer - customHeaders requests.CustomHeaders - CookieJar *cookiejar.Jar - + customHeaders requests.CustomHeaders colorizer colorizer.NucleiColorizer + httpClient *retryablehttp.Client + rawHTTPClient *rawhttp.Client + template *templates.Template + bulkHTTPRequest *requests.BulkHTTPRequest + writer *bufwriter.Writer + CookieJar *cookiejar.Jar decolorizer *regexp.Regexp + coloredOutput bool + debug bool + Results bool + jsonOutput bool + jsonRequest bool stopAtFirstMatch bool } // HTTPOptions contains configuration options for the HTTP executer. type HTTPOptions struct { - Debug bool - JSON bool - JSONRequests bool - CookieReuse bool - ColoredOutput bool + CustomHeaders requests.CustomHeaders + ProxyURL string + ProxySocksURL string Template *templates.Template BulkHTTPRequest *requests.BulkHTTPRequest Writer *bufwriter.Writer Timeout int Retries int - ProxyURL string - ProxySocksURL string - CustomHeaders requests.CustomHeaders CookieJar *cookiejar.Jar Colorizer *colorizer.NucleiColorizer Decolorizer *regexp.Regexp + Debug bool + JSON bool + JSONRequests bool + CookieReuse bool + ColoredOutput bool StopAtFirstMatch bool } @@ -117,7 +116,7 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) { jsonOutput: options.JSON, jsonRequest: options.JSONRequests, httpClient: client, - rawHttpClient: rawClient, + rawHTTPClient: rawClient, template: options.Template, bulkHTTPRequest: options.BulkHTTPRequest, writer: options.Writer, @@ -132,14 +131,17 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) { return executer, nil } -func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string) (result Result) { - result.Matches = make(map[string]interface{}) - result.Extractions = make(map[string]interface{}) +func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string) *Result { + result := &Result{ + Matches: make(map[string]interface{}), + Extractions: make(map[string]interface{}), + } + dynamicvalues := make(map[string]interface{}) // verify if the URL is already being processed if e.bulkHTTPRequest.HasGenerator(reqURL) { - return + return result } remaining := e.bulkHTTPRequest.GetRequestCount() @@ -161,7 +163,7 @@ func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string) globalratelimiter.Take(reqURL) // If the request was built correctly then execute it - err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, &result) + err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, result) if err != nil { result.Error = errors.Wrap(err, "could not handle http request") p.Drop(remaining) @@ -176,14 +178,17 @@ func (e *HTTPExecuter) ExecuteParallelHTTP(p progress.IProgress, reqURL string) return result } -func (e *HTTPExecuter) ExecuteTurboHTTP(p progress.IProgress, reqURL string) (result Result) { - result.Matches = make(map[string]interface{}) - result.Extractions = make(map[string]interface{}) +func (e *HTTPExecuter) ExecuteTurboHTTP(p progress.IProgress, reqURL string) *Result { + result := &Result{ + Matches: make(map[string]interface{}), + Extractions: make(map[string]interface{}), + } + dynamicvalues := make(map[string]interface{}) // verify if the URL is already being processed if e.bulkHTTPRequest.HasGenerator(reqURL) { - return + return result } remaining := e.bulkHTTPRequest.GetRequestCount() @@ -192,7 +197,7 @@ func (e *HTTPExecuter) ExecuteTurboHTTP(p progress.IProgress, reqURL string) (re // need to extract the target from the url URL, err := url.Parse(reqURL) if err != nil { - return + return result } pipeOptions := rawhttp.DefaultPipelineOptions @@ -227,13 +232,12 @@ func (e *HTTPExecuter) ExecuteTurboHTTP(p progress.IProgress, reqURL string) (re // If the request was built correctly then execute it request.Pipeline = true request.PipelineClient = pipeclient - err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, &result) + err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, result) if err != nil { result.Error = errors.Wrap(err, "could not handle http request") p.Drop(remaining) } request.PipelineClient = nil - }(request) } @@ -246,7 +250,7 @@ func (e *HTTPExecuter) ExecuteTurboHTTP(p progress.IProgress, reqURL string) (re } // ExecuteHTTP executes the HTTP request on a URL -func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) (result Result) { +func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) *Result { // verify if pipeline was requested if e.bulkHTTPRequest.Pipeline { return e.ExecuteTurboHTTP(p, reqURL) @@ -256,14 +260,17 @@ func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) (result return e.ExecuteParallelHTTP(p, reqURL) } - result.Matches = make(map[string]interface{}) - result.Extractions = make(map[string]interface{}) + result := &Result{ + Matches: make(map[string]interface{}), + Extractions: make(map[string]interface{}), + } + dynamicvalues := make(map[string]interface{}) _ = dynamicvalues // verify if the URL is already being processed if e.bulkHTTPRequest.HasGenerator(reqURL) { - return + return result } remaining := e.bulkHTTPRequest.GetRequestCount() @@ -277,7 +284,7 @@ func (e *HTTPExecuter) ExecuteHTTP(p progress.IProgress, reqURL string) (result } else { globalratelimiter.Take(reqURL) // If the request was built correctly then execute it - err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, &result) + err = e.handleHTTP(reqURL, httpRequest, dynamicvalues, result) if err != nil { result.Error = errors.Wrap(err, "could not handle http request") p.Drop(remaining) @@ -310,9 +317,9 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest, ) if e.debug { - dumpedRequest, err := requests.Dump(request, reqURL) - if err != nil { - return err + dumpedRequest, dumpErr := requests.Dump(request, reqURL) + if dumpErr != nil { + return dumpErr } gologger.Infof("Dumped HTTP request for %s (%s)\n\n", reqURL, e.template.ID) @@ -323,17 +330,23 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest, if request.Pipeline { resp, err = request.PipelineClient.DoRaw(request.RawRequest.Method, reqURL, request.RawRequest.Path, requests.ExpandMapValues(request.RawRequest.Headers), ioutil.NopCloser(strings.NewReader(request.RawRequest.Data))) if err != nil { + if resp != nil { + resp.Body.Close() + } return err } } else if request.Unsafe { // rawhttp // burp uses "\r\n" as new line character request.RawRequest.Data = strings.ReplaceAll(request.RawRequest.Data, "\n", "\r\n") - options := e.rawHttpClient.Options + options := e.rawHTTPClient.Options options.AutomaticContentLength = request.AutomaticContentLengthHeader options.AutomaticHostHeader = request.AutomaticHostHeader - resp, err = e.rawHttpClient.DoRawWithOptions(request.RawRequest.Method, reqURL, request.RawRequest.Path, requests.ExpandMapValues(request.RawRequest.Headers), ioutil.NopCloser(strings.NewReader(request.RawRequest.Data)), options) + resp, err = e.rawHTTPClient.DoRawWithOptions(request.RawRequest.Method, reqURL, request.RawRequest.Path, requests.ExpandMapValues(request.RawRequest.Headers), ioutil.NopCloser(strings.NewReader(request.RawRequest.Data)), options) if err != nil { + if resp != nil { + resp.Body.Close() + } return err } } else { @@ -346,6 +359,7 @@ func (e *HTTPExecuter) handleHTTP(reqURL string, request *requests.HTTPRequest, return err } } + duration := time.Since(timeStart) if e.debug { diff --git a/v2/pkg/requests/bulk-http-request.go b/v2/pkg/requests/bulk-http-request.go index c2185a90..1111cae0 100644 --- a/v2/pkg/requests/bulk-http-request.go +++ b/v2/pkg/requests/bulk-http-request.go @@ -2,6 +2,7 @@ package requests import ( "bufio" + "context" "fmt" "io/ioutil" "net" @@ -106,6 +107,8 @@ func (r *BulkHTTPRequest) GetRequestCount() int64 { // MakeHTTPRequest makes the HTTP request func (r *BulkHTTPRequest) MakeHTTPRequest(baseURL string, dynamicValues map[string]interface{}, data string) (*HTTPRequest, error) { + ctx := context.Background() + parsed, err := url.Parse(baseURL) if err != nil { return nil, err @@ -120,19 +123,19 @@ func (r *BulkHTTPRequest) MakeHTTPRequest(baseURL string, dynamicValues map[stri // if data contains \n it's a raw request if strings.Contains(data, "\n") { - return r.makeHTTPRequestFromRaw(baseURL, data, values) + return r.makeHTTPRequestFromRaw(ctx, baseURL, data, values) } - return r.makeHTTPRequestFromModel(data, values) + return r.makeHTTPRequestFromModel(ctx, data, values) } // MakeHTTPRequestFromModel creates a *http.Request from a request template -func (r *BulkHTTPRequest) makeHTTPRequestFromModel(data string, values map[string]interface{}) (*HTTPRequest, error) { +func (r *BulkHTTPRequest) makeHTTPRequestFromModel(ctx context.Context, data string, values map[string]interface{}) (*HTTPRequest, error) { replacer := newReplacer(values) URL := replacer.Replace(data) // Build a request on the specified URL - req, err := http.NewRequest(r.Method, URL, nil) + req, err := http.NewRequestWithContext(ctx, r.Method, URL, nil) if err != nil { return nil, err } @@ -166,7 +169,7 @@ func (r *BulkHTTPRequest) ReadOne(reqURL string) { } // makeHTTPRequestFromRaw creates a *http.Request from a raw request -func (r *BulkHTTPRequest) makeHTTPRequestFromRaw(baseURL, data string, values map[string]interface{}) (*HTTPRequest, error) { +func (r *BulkHTTPRequest) makeHTTPRequestFromRaw(ctx context.Context, baseURL, data string, values map[string]interface{}) (*HTTPRequest, error) { // Add trailing line data += "\n" @@ -174,14 +177,14 @@ func (r *BulkHTTPRequest) makeHTTPRequestFromRaw(baseURL, data string, values ma r.gsfm.InitOrSkip(baseURL) r.ReadOne(baseURL) - return r.handleRawWithPaylods(data, baseURL, values, r.gsfm.Value(baseURL)) + return r.handleRawWithPaylods(ctx, data, baseURL, values, r.gsfm.Value(baseURL)) } // otherwise continue with normal flow - return r.handleRawWithPaylods(data, baseURL, values, nil) + return r.handleRawWithPaylods(ctx, data, baseURL, values, nil) } -func (r *BulkHTTPRequest) handleRawWithPaylods(raw, baseURL string, values, genValues map[string]interface{}) (*HTTPRequest, error) { +func (r *BulkHTTPRequest) handleRawWithPaylods(ctx context.Context, raw, baseURL string, values, genValues map[string]interface{}) (*HTTPRequest, error) { baseValues := generators.CopyMap(values) finValues := generators.MergeMaps(baseValues, genValues) @@ -225,7 +228,7 @@ func (r *BulkHTTPRequest) handleRawWithPaylods(raw, baseURL string, values, genV } // retryablehttp - req, err := http.NewRequest(rawRequest.Method, rawRequest.FullURL, strings.NewReader(rawRequest.Data)) + req, err := http.NewRequestWithContext(ctx, rawRequest.Method, rawRequest.FullURL, strings.NewReader(rawRequest.Data)) if err != nil { return nil, err } diff --git a/v2/pkg/workflows/var.go b/v2/pkg/workflows/var.go index 21d02a8e..be5a61e1 100644 --- a/v2/pkg/workflows/var.go +++ b/v2/pkg/workflows/var.go @@ -95,7 +95,7 @@ func (n *NucleiVar) Call(args ...tengo.Object) (ret tengo.Object, err error) { if result.GotResults { gotResult.Or(result.GotResults) - n.addResults(&result) + n.addResults(result) } } } @@ -115,7 +115,7 @@ func (n *NucleiVar) Call(args ...tengo.Object) (ret tengo.Object, err error) { if result.GotResults { gotResult.Or(result.GotResults) - n.addResults(&result) + n.addResults(result) } } }