From d1303797c0e5f88343a1f2ed781955dd086df03d Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Wed, 12 Jan 2022 18:33:17 +0530 Subject: [PATCH 01/23] * Add support to run remote template * Add remote-template-domain config only flag to specify allowed domain list to load remote templates from --- v2/cmd/nuclei/main.go | 1 + v2/pkg/catalog/find.go | 23 +++++++---- v2/pkg/catalog/loader/loader.go | 54 +++++++++++++------------- v2/pkg/catalog/loader/remote_loader.go | 38 ++++++++++++++++-- v2/pkg/parsers/parser.go | 35 +++++++++++++---- v2/pkg/protocols/dns/request.go | 15 +------ v2/pkg/templates/compile.go | 33 ++++++++++++---- v2/pkg/types/types.go | 2 + v2/pkg/utils/utils.go | 16 ++++++++ 9 files changed, 151 insertions(+), 66 deletions(-) diff --git a/v2/cmd/nuclei/main.go b/v2/cmd/nuclei/main.go index 6c78ec5f6..7d8ea344d 100644 --- a/v2/cmd/nuclei/main.go +++ b/v2/cmd/nuclei/main.go @@ -89,6 +89,7 @@ on extensive configurability, massive extensibility and ease of use.`) flagSet.StringSliceVarP(&options.WorkflowURLs, "workflow-url", "wu", []string{}, "URL containing list of workflows to run"), flagSet.BoolVar(&options.Validate, "validate", false, "validate the passed templates to nuclei"), flagSet.BoolVar(&options.TemplateList, "tl", false, "list all available templates"), + flagSet.StringSliceVarConfigOnly(&options.RemoteTemplateDomainList, "remote-template-domain", []string{"api.nuclei.sh"}, "allowed domain list to load remote templates from"), ) createGroup(flagSet, "filters", "Filtering", diff --git a/v2/pkg/catalog/find.go b/v2/pkg/catalog/find.go index 7b3ffc6b4..348c2841d 100644 --- a/v2/pkg/catalog/find.go +++ b/v2/pkg/catalog/find.go @@ -18,14 +18,21 @@ func (c *Catalog) GetTemplatesPath(definitions []string) []string { allTemplates := []string{} for _, t := range definitions { - paths, err := c.GetTemplatePath(t) - if err != nil { - gologger.Error().Msgf("Could not find template '%s': %s\n", t, err) - } - for _, path := range paths { - if _, ok := processed[path]; !ok { - processed[path] = true - allTemplates = append(allTemplates, path) + if strings.HasPrefix(t, "http") && (strings.HasSuffix(t, ".yaml") || strings.HasSuffix(t, ".yml")) { + if _, ok := processed[t]; !ok { + processed[t] = true + allTemplates = append(allTemplates, t) + } + } else { + paths, err := c.GetTemplatePath(t) + if err != nil { + gologger.Error().Msgf("Could not find template '%s': %s\n", t, err) + } + for _, path := range paths { + if _, ok := processed[path]; !ok { + processed[path] = true + allTemplates = append(allTemplates, path) + } } } } diff --git a/v2/pkg/catalog/loader/loader.go b/v2/pkg/catalog/loader/loader.go index b4eec967b..ab67f76f5 100644 --- a/v2/pkg/catalog/loader/loader.go +++ b/v2/pkg/catalog/loader/loader.go @@ -17,12 +17,13 @@ import ( // Config contains the configuration options for the loader type Config struct { - Templates []string - TemplateURLs []string - Workflows []string - WorkflowURLs []string - ExcludeTemplates []string - IncludeTemplates []string + Templates []string + TemplateURLs []string + Workflows []string + WorkflowURLs []string + ExcludeTemplates []string + IncludeTemplates []string + RemoteTemplateDomainList []string Tags []string ExcludeTags []string @@ -57,25 +58,26 @@ type Store struct { // NewConfig returns a new loader config func NewConfig(options *types.Options, catalog *catalog.Catalog, executerOpts protocols.ExecuterOptions) *Config { loaderConfig := Config{ - Templates: options.Templates, - Workflows: options.Workflows, - TemplateURLs: options.TemplateURLs, - WorkflowURLs: options.WorkflowURLs, - ExcludeTemplates: options.ExcludedTemplates, - Tags: options.Tags, - ExcludeTags: options.ExcludeTags, - IncludeTemplates: options.IncludeTemplates, - Authors: options.Authors, - Severities: options.Severities, - ExcludeSeverities: options.ExcludeSeverities, - IncludeTags: options.IncludeTags, - IncludeIds: options.IncludeIds, - ExcludeIds: options.ExcludeIds, - TemplatesDirectory: options.TemplatesDirectory, - Protocols: options.Protocols, - ExcludeProtocols: options.ExcludeProtocols, - Catalog: catalog, - ExecutorOptions: executerOpts, + Templates: options.Templates, + Workflows: options.Workflows, + RemoteTemplateDomainList: options.RemoteTemplateDomainList, + TemplateURLs: options.TemplateURLs, + WorkflowURLs: options.WorkflowURLs, + ExcludeTemplates: options.ExcludedTemplates, + Tags: options.Tags, + ExcludeTags: options.ExcludeTags, + IncludeTemplates: options.IncludeTemplates, + Authors: options.Authors, + Severities: options.Severities, + ExcludeSeverities: options.ExcludeSeverities, + IncludeTags: options.IncludeTags, + IncludeIds: options.IncludeIds, + ExcludeIds: options.ExcludeIds, + TemplatesDirectory: options.TemplatesDirectory, + Protocols: options.Protocols, + ExcludeProtocols: options.ExcludeProtocols, + Catalog: catalog, + ExecutorOptions: executerOpts, } return &loaderConfig } @@ -107,7 +109,7 @@ func New(config *Config) (*Store, error) { urlBasedTemplatesProvided := len(config.TemplateURLs) > 0 || len(config.WorkflowURLs) > 0 if urlBasedTemplatesProvided { - remoteTemplates, remoteWorkflows, err := getRemoteTemplatesAndWorkflows(config.TemplateURLs, config.WorkflowURLs) + remoteTemplates, remoteWorkflows, err := getRemoteTemplatesAndWorkflows(config.TemplateURLs, config.WorkflowURLs, config.RemoteTemplateDomainList) if err != nil { return store, err } diff --git a/v2/pkg/catalog/loader/remote_loader.go b/v2/pkg/catalog/loader/remote_loader.go index c787e9601..410cb4072 100644 --- a/v2/pkg/catalog/loader/remote_loader.go +++ b/v2/pkg/catalog/loader/remote_loader.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "net/http" + "net/url" "strings" "github.com/pkg/errors" @@ -22,14 +23,14 @@ type RemoteContentError struct { Error error } -func getRemoteTemplatesAndWorkflows(templateURLs []string, workflowURLs []string) ([]string, []string, error) { +func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, rtdl []string) ([]string, []string, error) { remoteContentErrorChannel := make(chan RemoteContentError) for _, templateURL := range templateURLs { - go getRemoteContent(templateURL, remoteContentErrorChannel, Template) + go getRemoteContent(templateURL, rtdl, remoteContentErrorChannel, Template) } for _, workflowURL := range workflowURLs { - go getRemoteContent(workflowURL, remoteContentErrorChannel, Workflow) + go getRemoteContent(workflowURL, rtdl, remoteContentErrorChannel, Workflow) } var remoteTemplateList []string @@ -55,7 +56,27 @@ func getRemoteTemplatesAndWorkflows(templateURLs []string, workflowURLs []string return remoteTemplateList, remoteWorkFlowList, err } -func getRemoteContent(URL string, w chan<- RemoteContentError, contentType ContentType) { +func getRemoteContent(URL string, rtdl []string, w chan<- RemoteContentError, contentType ContentType) { + if strings.HasPrefix(URL, "http") && (strings.HasSuffix(URL, ".yaml") || strings.HasSuffix(URL, ".yml")) { + parsed, err := url.Parse(URL) + if err != nil { + w <- RemoteContentError{ + Error: err, + } + return + } + if !stringSliceContains(rtdl, parsed.Host) { + w <- RemoteContentError{ + Error: errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsed.Host), + } + return + } + w <- RemoteContentError{ + Content: []string{URL}, + Type: contentType, + } + return + } response, err := http.Get(URL) if err != nil { w <- RemoteContentError{ @@ -93,3 +114,12 @@ func getRemoteContent(URL string, w chan<- RemoteContentError, contentType Conte Type: contentType, } } + +func stringSliceContains(slice []string, item string) bool { + for _, i := range slice { + if strings.EqualFold(i, item) { + return true + } + } + return false +} diff --git a/v2/pkg/parsers/parser.go b/v2/pkg/parsers/parser.go index f8fa75925..c060f679e 100644 --- a/v2/pkg/parsers/parser.go +++ b/v2/pkg/parsers/parser.go @@ -3,6 +3,7 @@ package parsers import ( "fmt" "io/ioutil" + "net/http" "os" "regexp" "strings" @@ -129,14 +130,7 @@ func ParseTemplate(templatePath string) (*templates.Template, error) { if value, err := parsedTemplatesCache.Has(templatePath); value != nil { return value.(*templates.Template), err } - - f, err := os.Open(templatePath) - if err != nil { - return nil, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := readFromTemplatePath(templatePath) if err != nil { return nil, err } @@ -158,3 +152,28 @@ func ParseTemplate(templatePath string) (*templates.Template, error) { parsedTemplatesCache.Store(templatePath, template, nil) return template, nil } + +func readFromTemplatePath(templatePath string) (data []byte, err error) { + if utils.IsURL(templatePath) { + resp, err := http.Get(templatePath) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + } else { + f, err := os.Open(templatePath) + if err != nil { + return nil, err + } + defer f.Close() + data, err = ioutil.ReadAll(f) + if err != nil { + return nil, err + } + } + return +} diff --git a/v2/pkg/protocols/dns/request.go b/v2/pkg/protocols/dns/request.go index 1b6516b85..8bc7d3537 100644 --- a/v2/pkg/protocols/dns/request.go +++ b/v2/pkg/protocols/dns/request.go @@ -13,6 +13,7 @@ import ( "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter" templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types" + "github.com/projectdiscovery/nuclei/v2/pkg/utils" "github.com/projectdiscovery/retryabledns" ) @@ -27,7 +28,7 @@ func (request *Request) Type() templateTypes.ProtocolType { func (request *Request) ExecuteWithResults(input string, metadata /*TODO review unused parameter*/, previous output.InternalEvent, callback protocols.OutputEventCallback) error { // Parse the URL and return domain if URL. var domain string - if isURL(input) { + if utils.IsURL(input) { domain = extractDomain(input) } else { domain = input @@ -125,18 +126,6 @@ func dumpTraceData(event *output.InternalWrappedEvent, requestOptions *protocols } } -// isURL tests a string to determine if it is a well-structured url or not. -func isURL(toTest string) bool { - if _, err := url.ParseRequestURI(toTest); err != nil { - return false - } - u, err := url.Parse(toTest) - if err != nil || u.Scheme == "" || u.Host == "" { - return false - } - return true -} - // extractDomain extracts the domain name of a URL func extractDomain(theURL string) string { u, err := url.Parse(theURL) diff --git a/v2/pkg/templates/compile.go b/v2/pkg/templates/compile.go index 6ed64dc6d..f605bd026 100644 --- a/v2/pkg/templates/compile.go +++ b/v2/pkg/templates/compile.go @@ -3,6 +3,7 @@ package templates import ( "fmt" "io/ioutil" + "net/http" "os" "reflect" "strings" @@ -38,13 +39,7 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Execute template := &Template{} - f, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer f.Close() - - data, err := ioutil.ReadAll(f) + data, err := readFromTemplatePath(filePath) if err != nil { return nil, err } @@ -105,6 +100,30 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Execute parsedTemplatesCache.Store(filePath, template, err) return template, nil } +func readFromTemplatePath(templatePath string) (data []byte, err error) { + if utils.IsURL(templatePath) { + resp, err := http.Get(templatePath) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + } else { + f, err := os.Open(templatePath) + if err != nil { + return nil, err + } + defer f.Close() + data, err = ioutil.ReadAll(f) + if err != nil { + return nil, err + } + } + return +} // parseSelfContainedRequests parses the self contained template requests. func (template *Template) parseSelfContainedRequests() { diff --git a/v2/pkg/types/types.go b/v2/pkg/types/types.go index cc548262e..b079873ba 100644 --- a/v2/pkg/types/types.go +++ b/v2/pkg/types/types.go @@ -23,6 +23,8 @@ type Options struct { Templates goflags.StringSlice // TemplateURLs specifies URLs to a list of templates to use TemplateURLs goflags.StringSlice + // RemoteTemplates specifies list of allowed URLs to load remote templates from + RemoteTemplateDomainList goflags.StringSlice // ExcludedTemplates specifies the template/templates to exclude ExcludedTemplates goflags.StringSlice // CustomHeaders is the list of custom global headers to send with each request. diff --git a/v2/pkg/utils/utils.go b/v2/pkg/utils/utils.go index ba4bf87cb..57b092803 100644 --- a/v2/pkg/utils/utils.go +++ b/v2/pkg/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "errors" + "net/url" "strings" "github.com/projectdiscovery/fileutil" @@ -37,3 +38,18 @@ func LoadFile(filename string) ([]string, error) { } return items, nil } + +// IsURL tests a string to determine if it is a well-structured url or not. +func IsURL(input string) bool { + _, err := url.ParseRequestURI(input) + if err != nil { + return false + } + + u, err := url.Parse(input) + if err != nil || u.Scheme == "" || u.Host == "" { + return false + } + + return true +} From 9865b14c709af3b5b14144b88a21eebdb6e80304 Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Mon, 17 Jan 2022 13:29:28 +0530 Subject: [PATCH 02/23] update goflags to config-only-flag branch --- v2/go.mod | 2 +- v2/go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/v2/go.mod b/v2/go.mod index 005fb47e0..422ef951f 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -30,7 +30,7 @@ require ( github.com/projectdiscovery/fastdialer v0.0.13 github.com/projectdiscovery/filekv v0.0.0-20210915124239-3467ef45dd08 github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 - github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a + github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994 github.com/projectdiscovery/gologger v1.1.4 github.com/projectdiscovery/hmap v0.0.2-0.20210917080408-0fd7bd286bfa github.com/projectdiscovery/interactsh v0.0.8-0.20211231143029-74e9182d2cbe diff --git a/v2/go.sum b/v2/go.sum index b7c6ade11..8690cd10c 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -622,6 +622,8 @@ github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e/go.mod github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a h1:EzwVm8i4zmzqZX55vrDtyfogwHh8AAZ3cWCJe4fEduk= github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= +github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994 h1:4EuhJ+YTGA1eqMnR7zI9BLV0Wu0BvHaDKE9LbxSUFb8= +github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= From 369255a4feecd68816129ceaa274fe53f54259b3 Mon Sep 17 00:00:00 2001 From: mzack Date: Wed, 19 Jan 2022 14:10:11 +0100 Subject: [PATCH 03/23] Implementing lexer with runtime expression validation --- v2/go.mod | 2 +- v2/go.sum | 3 +- .../common/expressions/expressions.go | 121 +++++++++++++++--- .../common/expressions/expressions_test.go | 24 ++-- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 5cefa50d1..735efcc60 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -38,7 +38,7 @@ require ( github.com/projectdiscovery/rawhttp v0.0.7 github.com/projectdiscovery/retryabledns v1.0.13-0.20211109182249-43d38df59660 github.com/projectdiscovery/retryablehttp-go v1.0.2 - github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 + github.com/projectdiscovery/stringsutil v0.0.0-20220119085121-22513a958700 github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.3.0 // indirect diff --git a/v2/go.sum b/v2/go.sum index 7f4eb90da..67e4eda7c 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -457,8 +457,9 @@ github.com/projectdiscovery/retryablehttp-go v1.0.2 h1:LV1/KAQU+yeWhNVlvveaYFsjB github.com/projectdiscovery/retryablehttp-go v1.0.2/go.mod h1:dx//aY9V247qHdsRf0vdWHTBZuBQ2vm6Dq5dagxrDYI= github.com/projectdiscovery/stringsutil v0.0.0-20210804142656-fd3c28dbaafe/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= github.com/projectdiscovery/stringsutil v0.0.0-20210823090203-2f5f137e8e1d/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= -github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 h1:xbL1/7h0k6HE3RzPdYk9W/8pUxESrGWewTaZdIB5Pes= github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= +github.com/projectdiscovery/stringsutil v0.0.0-20220119085121-22513a958700 h1:L7Vb5AdzIV1Xs088Nvslfhh/piKP9gjTxjxfiqnd4mk= +github.com/projectdiscovery/stringsutil v0.0.0-20220119085121-22513a958700/go.mod h1:oTRc18WBv9t6BpaN9XBY+QmG28PUpsyDzRht56Qf49I= github.com/projectdiscovery/yamldoc-go v1.0.2/go.mod h1:7uSxfMXaBmzvw8m5EhOEjB6nhz0rK/H9sUjq1ciZu24= github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6 h1:DvWRQpw7Ib2CRL3ogYm/BWM+X0UGPfz1n9Ix9YKgFM8= github.com/projectdiscovery/yamldoc-go v1.0.3-0.20211126104922-00d2c6bb43b6/go.mod h1:8OfZj8p/axkUM/TJoS/O9LDjj/S8u17rxRbqluE9CU4= diff --git a/v2/pkg/protocols/common/expressions/expressions.go b/v2/pkg/protocols/common/expressions/expressions.go index 742acc916..b81a408c1 100644 --- a/v2/pkg/protocols/common/expressions/expressions.go +++ b/v2/pkg/protocols/common/expressions/expressions.go @@ -6,9 +6,9 @@ import ( "github.com/Knetic/govaluate" "github.com/projectdiscovery/nuclei/v2/pkg/operators/common/dsl" - "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/marker" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer" + "github.com/projectdiscovery/stringsutil" ) // Evaluate checks if the match contains a dynamic variable, for each @@ -33,13 +33,17 @@ func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) { } func evaluate(data string, base map[string]interface{}) (string, error) { - data = replacer.Replace(data, base) - + // expressions can be: + // - simple: containing base values keys (variables) + // - complex: containing helper functions [ + variables] + // literals like {{2+2}} are not considered expressions + expressions := findExpressions(data, mergeFunctions(dsl.HelperFunctions(), mapToFunctions(base))) dynamicValues := make(map[string]interface{}) - for _, match := range findMatches(data) { - expr := generators.TrimDelimiters(match) - - compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, dsl.HelperFunctions()) + for _, expression := range expressions { + // replace variable placeholders with base values + expression = replacer.Replace(expression, base) + // turns expressions (either helper functions+base values or base values) + compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expression, dsl.HelperFunctions()) if err != nil { continue } @@ -47,19 +51,104 @@ func evaluate(data string, base map[string]interface{}) (string, error) { if err != nil { continue } - dynamicValues[expr] = result + dynamicValues[expression] = result } - // Replacer dynamic values if any in raw request and parse it + // Replacer dynamic values if any in raw request and parse it return replacer.Replace(data, dynamicValues), nil } -func findMatches(data string) []string { - var matches []string - for _, token := range strings.Split(data, marker.ParenthesisOpen) { - closingToken := strings.LastIndex(token, marker.ParenthesisClose) - if closingToken > 0 { - matches = append(matches, token[:closingToken]) +// maxIterations to avoid infinite loop +const maxIterations = 250 + +func findExpressions(data string, functions map[string]govaluate.ExpressionFunction) []string { + var ( + iterations int + exps []string + ) + for { + // check if we reached the maximum number of iterations + if iterations > maxIterations { + break + } + iterations++ + // attempt to find open markers + indexOpenMarker := strings.Index(data, marker.ParenthesisOpen) + // exits if not found + if indexOpenMarker < 0 { + break + } + + indexOpenMarkerOffset := indexOpenMarker + len(marker.ParenthesisOpen) + + shouldSearchCloseMarker := true + closeMarkerFound := false + innerData := data + var potentialMatch string + var indexCloseMarker, indexCloseMarkerOffset int + skip := indexOpenMarkerOffset + for shouldSearchCloseMarker { + // attempt to find close marker + indexCloseMarker = stringsutil.IndexAt(innerData, marker.ParenthesisClose, skip) + // if no close markers are found exit + if indexCloseMarker < 0 { + shouldSearchCloseMarker = false + continue + } + indexCloseMarkerOffset = indexCloseMarker + len(marker.ParenthesisClose) + + potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker] + if isExpression(potentialMatch, functions) { + closeMarkerFound = true + shouldSearchCloseMarker = false + exps = append(exps, potentialMatch) + } else { + skip = indexCloseMarkerOffset + } + } + + if closeMarkerFound { + // move after the close marker + data = data[indexCloseMarkerOffset:] + } else { + // move after the open marker + data = data[indexOpenMarkerOffset:] } } - return matches + return exps +} + +func isExpression(data string, functions map[string]govaluate.ExpressionFunction) bool { + if _, err := govaluate.NewEvaluableExpression(data); err == nil { + return stringsutil.ContainsAny(data, getFunctionsNames(functions)...) + } + + // check if it's a complex expression + _, err := govaluate.NewEvaluableExpressionWithFunctions(data, dsl.HelperFunctions()) + return err == nil +} + +func mapToFunctions(vars map[string]interface{}) map[string]govaluate.ExpressionFunction { + f := make(map[string]govaluate.ExpressionFunction) + for k := range vars { + f[k] = nil + } + return f +} + +func mergeFunctions(m ...map[string]govaluate.ExpressionFunction) map[string]govaluate.ExpressionFunction { + o := make(map[string]govaluate.ExpressionFunction) + for _, mm := range m { + for k, v := range mm { + o[k] = v + } + } + return o +} + +func getFunctionsNames(m map[string]govaluate.ExpressionFunction) []string { + var keys []string + for k := range m { + keys = append(keys, k) + } + return keys } diff --git a/v2/pkg/protocols/common/expressions/expressions_test.go b/v2/pkg/protocols/common/expressions/expressions_test.go index 668b3b455..a154eba77 100644 --- a/v2/pkg/protocols/common/expressions/expressions_test.go +++ b/v2/pkg/protocols/common/expressions/expressions_test.go @@ -14,19 +14,19 @@ func TestEvaluate(t *testing.T) { }{ {input: "{{url_encode('test}aaa')}}", expected: "test%7Daaa", extra: map[string]interface{}{}}, {input: "{{hex_encode('PING')}}", expected: "50494e47", extra: map[string]interface{}{}}, - // TODO #1501 - //{input: "{{hex_encode('{{')}}", expected: "7b7b", extra: map[string]interface{}{}}, - //{input: `{{concat("{{", 123, "*", 123, "}}")}}`, expected: "{{123*123}}", extra: map[string]interface{}{}}, - //{input: `{{concat("{{", "123*123", "}}")}}`, expected: "{{123*123}}", extra: map[string]interface{}{}}, - //{input: `{{"{{" + '123*123' + "}}"}}`, expected: "{{123*123}}", extra: map[string]interface{}{}}, + {input: "{{hex_encode('{{')}}", expected: "7b7b", extra: map[string]interface{}{}}, + {input: `{{concat("{{", 123, "*", 123, "}}")}}`, expected: "{{123*123}}", extra: map[string]interface{}{}}, + {input: `{{concat("{{", "123*123", "}}")}}`, expected: "{{123*123}}", extra: map[string]interface{}{}}, + {input: `{{"{{" + '123*123' + "}}"}}`, expected: `{{"{{" + '123*123' + "}}"}}`, extra: map[string]interface{}{}}, + {input: `{{a + '123*123' + b}}`, expected: `aa123*123bb`, extra: map[string]interface{}{"a": "aa", "b": "bb"}}, {input: `{{concat(123,'*',123)}}`, expected: "123*123", extra: map[string]interface{}{}}, - {input: `{{1+1}}`, expected: "2", extra: map[string]interface{}{}}, - {input: `{{"1"+"1"}}`, expected: "11", extra: map[string]interface{}{}}, - {input: `{{"1" + '*' + "1"}}`, expected: "1*1", extra: map[string]interface{}{}}, - {input: `{{"a" + 'b' + "c"}}`, expected: "abc", extra: map[string]interface{}{}}, - {input: `{{10*2}}`, expected: "20", extra: map[string]interface{}{}}, - {input: `{{10/2}}`, expected: "5", extra: map[string]interface{}{}}, - {input: `{{10-2}}`, expected: "8", extra: map[string]interface{}{}}, + {input: `{{1+1}}`, expected: "{{1+1}}", extra: map[string]interface{}{}}, + {input: `{{"1"+"1"}}`, expected: `{{"1"+"1"}}`, extra: map[string]interface{}{}}, + {input: `{{"1" + '*' + "1"}}`, expected: `{{"1" + '*' + "1"}}`, extra: map[string]interface{}{}}, + {input: `{{"a" + 'b' + "c"}}`, expected: `{{"a" + 'b' + "c"}}`, extra: map[string]interface{}{}}, + {input: `{{10*2}}`, expected: `{{10*2}}`, extra: map[string]interface{}{}}, + {input: `{{10/2}}`, expected: `{{10/2}}`, extra: map[string]interface{}{}}, + {input: `{{10-2}}`, expected: `{{10-2}}`, extra: map[string]interface{}{}}, {input: "test", expected: "test", extra: map[string]interface{}{}}, {input: "{{hex_encode(Item)}}", expected: "50494e47", extra: map[string]interface{}{"Item": "PING"}}, {input: "{{hex_encode(Item)}}\r\n", expected: "50494e47\r\n", extra: map[string]interface{}{"Item": "PING"}}, From adf4721833907ca9b67ecef63118ec988b547d27 Mon Sep 17 00:00:00 2001 From: mzack Date: Wed, 19 Jan 2022 14:57:25 +0100 Subject: [PATCH 04/23] making markers parametric + simple match/replace for basic variables --- .../common/expressions/expressions.go | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/v2/pkg/protocols/common/expressions/expressions.go b/v2/pkg/protocols/common/expressions/expressions.go index b81a408c1..63bc3ce22 100644 --- a/v2/pkg/protocols/common/expressions/expressions.go +++ b/v2/pkg/protocols/common/expressions/expressions.go @@ -33,11 +33,14 @@ func EvaluateByte(data []byte, base map[string]interface{}) ([]byte, error) { } func evaluate(data string, base map[string]interface{}) (string, error) { + // replace simple placeholders (key => value) MarkerOpen + key + MarkerClose and General + key + General to value + data = replacer.Replace(data, base) + // expressions can be: // - simple: containing base values keys (variables) // - complex: containing helper functions [ + variables] // literals like {{2+2}} are not considered expressions - expressions := findExpressions(data, mergeFunctions(dsl.HelperFunctions(), mapToFunctions(base))) + expressions := findExpressions(data, marker.ParenthesisOpen, marker.ParenthesisClose, mergeFunctions(dsl.HelperFunctions(), mapToFunctions(base))) dynamicValues := make(map[string]interface{}) for _, expression := range expressions { // replace variable placeholders with base values @@ -60,7 +63,7 @@ func evaluate(data string, base map[string]interface{}) (string, error) { // maxIterations to avoid infinite loop const maxIterations = 250 -func findExpressions(data string, functions map[string]govaluate.ExpressionFunction) []string { +func findExpressions(data, OpenMarker, CloseMarker string, functions map[string]govaluate.ExpressionFunction) []string { var ( iterations int exps []string @@ -72,13 +75,13 @@ func findExpressions(data string, functions map[string]govaluate.ExpressionFunct } iterations++ // attempt to find open markers - indexOpenMarker := strings.Index(data, marker.ParenthesisOpen) + indexOpenMarker := strings.Index(data, OpenMarker) // exits if not found if indexOpenMarker < 0 { break } - indexOpenMarkerOffset := indexOpenMarker + len(marker.ParenthesisOpen) + indexOpenMarkerOffset := indexOpenMarker + len(OpenMarker) shouldSearchCloseMarker := true closeMarkerFound := false @@ -88,13 +91,13 @@ func findExpressions(data string, functions map[string]govaluate.ExpressionFunct skip := indexOpenMarkerOffset for shouldSearchCloseMarker { // attempt to find close marker - indexCloseMarker = stringsutil.IndexAt(innerData, marker.ParenthesisClose, skip) + indexCloseMarker = stringsutil.IndexAt(innerData, CloseMarker, skip) // if no close markers are found exit if indexCloseMarker < 0 { shouldSearchCloseMarker = false continue } - indexCloseMarkerOffset = indexCloseMarker + len(marker.ParenthesisClose) + indexCloseMarkerOffset = indexCloseMarker + len(CloseMarker) potentialMatch = innerData[indexOpenMarkerOffset:indexCloseMarker] if isExpression(potentialMatch, functions) { @@ -152,3 +155,11 @@ func getFunctionsNames(m map[string]govaluate.ExpressionFunction) []string { } return keys } + +func getMapKeysAsValues(m map[string]interface{}) map[string]interface{} { + mapOut := make(map[string]interface{}) + for k := range m { + mapOut[k] = k + } + return mapOut +} From 8559cfabca1fdcc48fe54f9f1822aac803c5c0e7 Mon Sep 17 00:00:00 2001 From: mzack Date: Wed, 19 Jan 2022 15:16:47 +0100 Subject: [PATCH 05/23] removing unused code --- v2/pkg/protocols/common/expressions/expressions.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/v2/pkg/protocols/common/expressions/expressions.go b/v2/pkg/protocols/common/expressions/expressions.go index 63bc3ce22..0eed8976c 100644 --- a/v2/pkg/protocols/common/expressions/expressions.go +++ b/v2/pkg/protocols/common/expressions/expressions.go @@ -155,11 +155,3 @@ func getFunctionsNames(m map[string]govaluate.ExpressionFunction) []string { } return keys } - -func getMapKeysAsValues(m map[string]interface{}) map[string]interface{} { - mapOut := make(map[string]interface{}) - for k := range m { - mapOut[k] = k - } - return mapOut -} From ba33622073c2fe7bfd9764aed8ce075943dd28b0 Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Fri, 21 Jan 2022 20:14:50 +0530 Subject: [PATCH 06/23] add unit test coverage for remote tempates --- v2/go.mod | 2 +- v2/go.sum | 2 + v2/pkg/catalog/loader/loader_test.go | 53 ++++++++++++++++++++++++++ v2/pkg/catalog/loader/remote_loader.go | 10 ++--- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 422ef951f..4dee31b2a 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -30,7 +30,7 @@ require ( github.com/projectdiscovery/fastdialer v0.0.13 github.com/projectdiscovery/filekv v0.0.0-20210915124239-3467ef45dd08 github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5 - github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994 + github.com/projectdiscovery/goflags v0.0.8-0.20220121110825-48035ad3ffe0 github.com/projectdiscovery/gologger v1.1.4 github.com/projectdiscovery/hmap v0.0.2-0.20210917080408-0fd7bd286bfa github.com/projectdiscovery/interactsh v0.0.8-0.20211231143029-74e9182d2cbe diff --git a/v2/go.sum b/v2/go.sum index 8690cd10c..8ab60d32b 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -624,6 +624,8 @@ github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a h1:EzwV github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994 h1:4EuhJ+YTGA1eqMnR7zI9BLV0Wu0BvHaDKE9LbxSUFb8= github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= +github.com/projectdiscovery/goflags v0.0.8-0.20220121110825-48035ad3ffe0 h1:KtCp/dCsxXNdT8m0yyWc/4ou4YaKWVakAr3G03TjQCk= +github.com/projectdiscovery/goflags v0.0.8-0.20220121110825-48035ad3ffe0/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= diff --git a/v2/pkg/catalog/loader/loader_test.go b/v2/pkg/catalog/loader/loader_test.go index b9f6ab43e..ce8c77822 100644 --- a/v2/pkg/catalog/loader/loader_test.go +++ b/v2/pkg/catalog/loader/loader_test.go @@ -1,6 +1,7 @@ package loader import ( + "reflect" "testing" "github.com/stretchr/testify/require" @@ -38,3 +39,55 @@ func TestLoadTemplates(t *testing.T) { require.Equal(t, []string{templatesDirectory}, store.finalTemplates, "could not get correct templates") }) } + +func TestRemoteTemplates(t *testing.T) { + var nilStringSlice []string + type args struct { + config *Config + } + tests := []struct { + name string + args args + want *Store + wantErr bool + }{ + { + name: "remote-templates-positive", + args: args{ + config: &Config{ + TemplateURLs: []string{"https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/master/technologies/tech-detect.yaml"}, + RemoteTemplateDomainList: []string{"localhost","raw.githubusercontent.com"}, + }, + }, + want: &Store{ + finalTemplates: []string{"https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/master/technologies/tech-detect.yaml"}, + }, + wantErr: false, + }, + { + name: "remote-templates-negative", + args: args{ + config: &Config{ + TemplateURLs: []string{"https://raw.githubusercontent.com/projectdiscovery/nuclei-templates/master/technologies/tech-detect.yaml"}, + RemoteTemplateDomainList: []string{"localhost"}, + }, + }, + want: &Store{ + finalTemplates: nilStringSlice, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := New(tt.args.config) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got.finalTemplates, tt.want.finalTemplates) { + t.Errorf("New() = %v, want %v", got.finalTemplates, tt.want.finalTemplates) + } + }) + } +} diff --git a/v2/pkg/catalog/loader/remote_loader.go b/v2/pkg/catalog/loader/remote_loader.go index 410cb4072..f9b923316 100644 --- a/v2/pkg/catalog/loader/remote_loader.go +++ b/v2/pkg/catalog/loader/remote_loader.go @@ -23,14 +23,14 @@ type RemoteContentError struct { Error error } -func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, rtdl []string) ([]string, []string, error) { +func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, remoteTemplateDomainList []string) ([]string, []string, error) { remoteContentErrorChannel := make(chan RemoteContentError) for _, templateURL := range templateURLs { - go getRemoteContent(templateURL, rtdl, remoteContentErrorChannel, Template) + go getRemoteContent(templateURL, remoteTemplateDomainList, remoteContentErrorChannel, Template) } for _, workflowURL := range workflowURLs { - go getRemoteContent(workflowURL, rtdl, remoteContentErrorChannel, Workflow) + go getRemoteContent(workflowURL, remoteTemplateDomainList, remoteContentErrorChannel, Workflow) } var remoteTemplateList []string @@ -56,7 +56,7 @@ func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, rtdl []string) ( return remoteTemplateList, remoteWorkFlowList, err } -func getRemoteContent(URL string, rtdl []string, w chan<- RemoteContentError, contentType ContentType) { +func getRemoteContent(URL string, remoteTemplateDomainList []string, w chan<- RemoteContentError, contentType ContentType) { if strings.HasPrefix(URL, "http") && (strings.HasSuffix(URL, ".yaml") || strings.HasSuffix(URL, ".yml")) { parsed, err := url.Parse(URL) if err != nil { @@ -65,7 +65,7 @@ func getRemoteContent(URL string, rtdl []string, w chan<- RemoteContentError, co } return } - if !stringSliceContains(rtdl, parsed.Host) { + if !stringSliceContains(remoteTemplateDomainList, parsed.Host) { w <- RemoteContentError{ Error: errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsed.Host), } From c6de2ca4060323e13baf112bf1eed74202c38d89 Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Mon, 24 Jan 2022 16:48:12 +0530 Subject: [PATCH 07/23] rename few vars, extract duplicate functions to utils --- v2/pkg/catalog/loader/remote_loader.go | 56 +++++++++++--------------- v2/pkg/parsers/parser.go | 30 +------------- v2/pkg/templates/compile.go | 29 +------------ v2/pkg/utils/utils.go | 39 ++++++++++++++++++ 4 files changed, 65 insertions(+), 89 deletions(-) diff --git a/v2/pkg/catalog/loader/remote_loader.go b/v2/pkg/catalog/loader/remote_loader.go index f9b923316..2aa334fc3 100644 --- a/v2/pkg/catalog/loader/remote_loader.go +++ b/v2/pkg/catalog/loader/remote_loader.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/pkg/errors" + "github.com/projectdiscovery/nuclei/v2/pkg/utils" ) type ContentType string @@ -17,38 +18,38 @@ const ( Workflow ContentType = "Workflow" ) -type RemoteContentError struct { +type RemoteContent struct { Content []string Type ContentType Error error } func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, remoteTemplateDomainList []string) ([]string, []string, error) { - remoteContentErrorChannel := make(chan RemoteContentError) + remoteContentChannel := make(chan RemoteContent) for _, templateURL := range templateURLs { - go getRemoteContent(templateURL, remoteTemplateDomainList, remoteContentErrorChannel, Template) + go getRemoteContent(templateURL, remoteTemplateDomainList, remoteContentChannel, Template) } for _, workflowURL := range workflowURLs { - go getRemoteContent(workflowURL, remoteTemplateDomainList, remoteContentErrorChannel, Workflow) + go getRemoteContent(workflowURL, remoteTemplateDomainList, remoteContentChannel, Workflow) } var remoteTemplateList []string var remoteWorkFlowList []string var err error for i := 0; i < (len(templateURLs) + len(workflowURLs)); i++ { - remoteContentError := <-remoteContentErrorChannel - if remoteContentError.Error != nil { + remoteContent := <-remoteContentChannel + if remoteContent.Error != nil { if err != nil { - err = errors.New(remoteContentError.Error.Error() + ": " + err.Error()) + err = errors.New(remoteContent.Error.Error() + ": " + err.Error()) } else { - err = remoteContentError.Error + err = remoteContent.Error } } else { - if remoteContentError.Type == Template { - remoteTemplateList = append(remoteTemplateList, remoteContentError.Content...) - } else if remoteContentError.Type == Workflow { - remoteWorkFlowList = append(remoteWorkFlowList, remoteContentError.Content...) + if remoteContent.Type == Template { + remoteTemplateList = append(remoteTemplateList, remoteContent.Content...) + } else if remoteContent.Type == Workflow { + remoteWorkFlowList = append(remoteWorkFlowList, remoteContent.Content...) } } } @@ -56,22 +57,22 @@ func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, remoteTemplateDo return remoteTemplateList, remoteWorkFlowList, err } -func getRemoteContent(URL string, remoteTemplateDomainList []string, w chan<- RemoteContentError, contentType ContentType) { +func getRemoteContent(URL string, remoteTemplateDomainList []string, remoteContentChannel chan<- RemoteContent, contentType ContentType) { if strings.HasPrefix(URL, "http") && (strings.HasSuffix(URL, ".yaml") || strings.HasSuffix(URL, ".yml")) { - parsed, err := url.Parse(URL) + parsedURL, err := url.Parse(URL) if err != nil { - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Error: err, } return } - if !stringSliceContains(remoteTemplateDomainList, parsed.Host) { - w <- RemoteContentError{ - Error: errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsed.Host), + if !utils.StringSliceContains(remoteTemplateDomainList, parsedURL.Host) { + remoteContentChannel <- RemoteContent{ + Error: errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsedURL.Host), } return } - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Content: []string{URL}, Type: contentType, } @@ -79,14 +80,14 @@ func getRemoteContent(URL string, remoteTemplateDomainList []string, w chan<- Re } response, err := http.Get(URL) if err != nil { - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Error: err, } return } defer response.Body.Close() if response.StatusCode < 200 || response.StatusCode > 299 { - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Error: fmt.Errorf("get \"%s\": unexpect status %d", URL, response.StatusCode), } return @@ -103,23 +104,14 @@ func getRemoteContent(URL string, remoteTemplateDomainList []string, w chan<- Re } if err := scanner.Err(); err != nil { - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Error: errors.Wrap(err, "get \"%s\""), } return } - w <- RemoteContentError{ + remoteContentChannel <- RemoteContent{ Content: templateList, Type: contentType, } } - -func stringSliceContains(slice []string, item string) bool { - for _, i := range slice { - if strings.EqualFold(i, item) { - return true - } - } - return false -} diff --git a/v2/pkg/parsers/parser.go b/v2/pkg/parsers/parser.go index c060f679e..39bc405e3 100644 --- a/v2/pkg/parsers/parser.go +++ b/v2/pkg/parsers/parser.go @@ -2,9 +2,6 @@ package parsers import ( "fmt" - "io/ioutil" - "net/http" - "os" "regexp" "strings" @@ -130,7 +127,7 @@ func ParseTemplate(templatePath string) (*templates.Template, error) { if value, err := parsedTemplatesCache.Has(templatePath); value != nil { return value.(*templates.Template), err } - data, err := readFromTemplatePath(templatePath) + data, err := utils.ReadFromPathOrURL(templatePath) if err != nil { return nil, err } @@ -152,28 +149,3 @@ func ParseTemplate(templatePath string) (*templates.Template, error) { parsedTemplatesCache.Store(templatePath, template, nil) return template, nil } - -func readFromTemplatePath(templatePath string) (data []byte, err error) { - if utils.IsURL(templatePath) { - resp, err := http.Get(templatePath) - if err != nil { - return nil, err - } - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - } else { - f, err := os.Open(templatePath) - if err != nil { - return nil, err - } - defer f.Close() - data, err = ioutil.ReadAll(f) - if err != nil { - return nil, err - } - } - return -} diff --git a/v2/pkg/templates/compile.go b/v2/pkg/templates/compile.go index f605bd026..24e334edd 100644 --- a/v2/pkg/templates/compile.go +++ b/v2/pkg/templates/compile.go @@ -2,9 +2,6 @@ package templates import ( "fmt" - "io/ioutil" - "net/http" - "os" "reflect" "strings" @@ -39,7 +36,7 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Execute template := &Template{} - data, err := readFromTemplatePath(filePath) + data, err := utils.ReadFromPathOrURL(filePath) if err != nil { return nil, err } @@ -100,30 +97,6 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Execute parsedTemplatesCache.Store(filePath, template, err) return template, nil } -func readFromTemplatePath(templatePath string) (data []byte, err error) { - if utils.IsURL(templatePath) { - resp, err := http.Get(templatePath) - if err != nil { - return nil, err - } - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - } else { - f, err := os.Open(templatePath) - if err != nil { - return nil, err - } - defer f.Close() - data, err = ioutil.ReadAll(f) - if err != nil { - return nil, err - } - } - return -} // parseSelfContainedRequests parses the self contained template requests. func (template *Template) parseSelfContainedRequests() { diff --git a/v2/pkg/utils/utils.go b/v2/pkg/utils/utils.go index 57b092803..e7a1413f5 100644 --- a/v2/pkg/utils/utils.go +++ b/v2/pkg/utils/utils.go @@ -2,7 +2,10 @@ package utils import ( "errors" + "io/ioutil" + "net/http" "net/url" + "os" "strings" "github.com/projectdiscovery/fileutil" @@ -53,3 +56,39 @@ func IsURL(input string) bool { return true } + +// ReadFromPathOrURL reads and returns the contents of a file or url. +func ReadFromPathOrURL(templatePath string) (data []byte, err error) { + if IsURL(templatePath) { + resp, err := http.Get(templatePath) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + } else { + f, err := os.Open(templatePath) + if err != nil { + return nil, err + } + defer f.Close() + data, err = ioutil.ReadAll(f) + if err != nil { + return nil, err + } + } + return +} + +// StringSliceContains checks if a string slice contains a string. +func StringSliceContains(slice []string, item string) bool { + for _, i := range slice { + if strings.EqualFold(i, item) { + return true + } + } + return false +} From 670421a79d14fbf90e992868a6980ab1a36fa2b3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jan 2022 05:20:13 +0000 Subject: [PATCH 08/23] chore(deps): bump github.com/aws/aws-sdk-go in /v2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.42.37 to 1.42.41. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.42.37...v1.42.41) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- v2/go.mod | 2 +- v2/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index bccb2281d..9831b10b1 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -63,7 +63,7 @@ require ( moul.io/http2curl v1.0.0 ) -require github.com/aws/aws-sdk-go v1.42.37 +require github.com/aws/aws-sdk-go v1.42.41 require github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e diff --git a/v2/go.sum b/v2/go.sum index 9d1500640..ab8d2d229 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,8 +82,8 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.42.37 h1:EIziSq3REaoi1LgUBgxoQr29DQS7GYHnBbZPajtJmXM= -github.com/aws/aws-sdk-go v1.42.37/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.41 h1:gmHgDzSiLYfHx0ZedcXtHjMXbVxBtyOcl/sy0wg9o2o= +github.com/aws/aws-sdk-go v1.42.41/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= From 695404477e18582e575ed1fdc127eddda0372f87 Mon Sep 17 00:00:00 2001 From: mzack Date: Wed, 26 Jan 2022 14:20:23 +0100 Subject: [PATCH 09/23] Headless limited code refactor for better readability --- v2/pkg/protocols/headless/engine/rules.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/v2/pkg/protocols/headless/engine/rules.go b/v2/pkg/protocols/headless/engine/rules.go index fe6831e41..10dc7ef8e 100644 --- a/v2/pkg/protocols/headless/engine/rules.go +++ b/v2/pkg/protocols/headless/engine/rules.go @@ -64,9 +64,7 @@ func (p *Page) routingRuleHandler(ctx *rod.Hijack) { var rawResp strings.Builder respPayloads := ctx.Response.Payload() if respPayloads != nil { - rawResp.WriteString("HTTP/1.1 ") - rawResp.WriteString(fmt.Sprint(respPayloads.ResponseCode)) - rawResp.WriteString(" " + respPayloads.ResponsePhrase + "\n") + rawResp.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\n", respPayloads.ResponseCode, respPayloads.ResponsePhrase)) for _, header := range respPayloads.ResponseHeaders { rawResp.WriteString(header.Name + ": " + header.Value + "\n") } From 0605de707aed45d0111a3bc0aad2a11b0c59d7cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Jan 2022 05:24:11 +0000 Subject: [PATCH 10/23] chore(deps): bump github.com/aws/aws-sdk-go in /v2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.42.41 to 1.42.42. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.42.41...v1.42.42) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- v2/go.mod | 2 +- v2/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 7a3103550..4897836df 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -63,7 +63,7 @@ require ( moul.io/http2curl v1.0.0 ) -require github.com/aws/aws-sdk-go v1.42.41 +require github.com/aws/aws-sdk-go v1.42.42 require github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e diff --git a/v2/go.sum b/v2/go.sum index a25f13ea5..8d2a39b2e 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,8 +82,8 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.42.41 h1:gmHgDzSiLYfHx0ZedcXtHjMXbVxBtyOcl/sy0wg9o2o= -github.com/aws/aws-sdk-go v1.42.41/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.42 h1:2K61yu5BApC9ExAwC5Vk6ljWzBGbiRGRQYLW7adhP5U= +github.com/aws/aws-sdk-go v1.42.42/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= From 8dfa9cee39a5f2c4f158564b20cee96052c41d50 Mon Sep 17 00:00:00 2001 From: Ice3man Date: Thu, 27 Jan 2022 12:14:32 +0530 Subject: [PATCH 11/23] Added debug-req/resp support for interactsh interactions (#1491) * Added debug-req/resp support for interactsh interactions * Added format function for interact debug logs + misc fixes * Added function for interact debug header * Typo fix * Enable debug logging for req/resp debug flag --- v2/internal/runner/options.go | 2 +- v2/internal/runner/runner.go | 3 ++ .../protocols/common/interactsh/interactsh.go | 48 ++++++++++++++----- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/v2/internal/runner/options.go b/v2/internal/runner/options.go index 5650efa3c..5c666282a 100644 --- a/v2/internal/runner/options.go +++ b/v2/internal/runner/options.go @@ -152,7 +152,7 @@ func configureOutput(options *types.Options) { if options.Verbose || options.Validate { gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) } - if options.Debug { + if options.Debug || options.DebugRequests || options.DebugResponse { gologger.DefaultLogger.SetMaxLevel(levels.LevelDebug) } if options.NoColor { diff --git a/v2/internal/runner/runner.go b/v2/internal/runner/runner.go index 4d8c01482..8302ccad9 100644 --- a/v2/internal/runner/runner.go +++ b/v2/internal/runner/runner.go @@ -183,6 +183,9 @@ func New(options *types.Options) (*Runner, error) { opts.PollDuration = time.Duration(options.InteractionsPollDuration) * time.Second opts.NoInteractsh = runner.options.NoInteractsh opts.StopAtFirstMatch = runner.options.StopAtFirstMatch + opts.Debug = runner.options.Debug + opts.DebugRequest = runner.options.DebugRequests + opts.DebugResponse = runner.options.DebugResponse interactshClient, err := interactsh.New(opts) if err != nil { gologger.Error().Msgf("Could not create interactsh client: %s", err) diff --git a/v2/pkg/protocols/common/interactsh/interactsh.go b/v2/pkg/protocols/common/interactsh/interactsh.go index a61ec4cc1..650d485c5 100644 --- a/v2/pkg/protocols/common/interactsh/interactsh.go +++ b/v2/pkg/protocols/common/interactsh/interactsh.go @@ -75,7 +75,9 @@ type Options struct { // Progress is the nuclei progress bar implementation. Progress progress.Progress // Debug specifies whether debugging output should be shown for interactsh-client - Debug bool + Debug bool + DebugRequest bool + DebugResponse bool // DisableHttpFallback controls http retry in case of https failure for server url DisableHttpFallback bool // NoInteractsh disables the engine @@ -146,8 +148,8 @@ func (c *Client) firstTimeInitializeClient() error { c.hostname = interactDomain interactsh.StartPolling(c.pollDuration, func(interaction *server.Interaction) { - if c.options.Debug { - debugPrintInteraction(interaction) + if c.options.Debug || c.options.DebugRequest || c.options.DebugResponse { + c.debugPrintInteraction(interaction) } item := c.requests.Get(interaction.UniqueID) @@ -343,26 +345,48 @@ func HasMatchers(op *operators.Operators) bool { return false } -func debugPrintInteraction(interaction *server.Interaction) { +func (c *Client) debugPrintInteraction(interaction *server.Interaction) { builder := &bytes.Buffer{} switch interaction.Protocol { case "dns": - builder.WriteString(fmt.Sprintf("[%s] Received DNS interaction (%s) from %s at %s", interaction.FullId, interaction.QType, interaction.RemoteAddress, interaction.Timestamp.Format("2006-01-02 15:04:05"))) - builder.WriteString(fmt.Sprintf("\n-----------\nDNS Request\n-----------\n\n%s\n\n------------\nDNS Response\n------------\n\n%s\n\n", interaction.RawRequest, interaction.RawResponse)) + builder.WriteString(formatInteractionHeader("DNS", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp)) + if c.options.DebugRequest || c.options.Debug { + builder.WriteString(formatInteractionMessage("DNS Request", interaction.RawRequest)) + } + if c.options.DebugResponse || c.options.Debug { + builder.WriteString(formatInteractionMessage("DNS Response", interaction.RawResponse)) + } case "http": - builder.WriteString(fmt.Sprintf("[%s] Received HTTP interaction from %s at %s", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp.Format("2006-01-02 15:04:05"))) - builder.WriteString(fmt.Sprintf("\n------------\nHTTP Request\n------------\n\n%s\n\n-------------\nHTTP Response\n-------------\n\n%s\n\n", interaction.RawRequest, interaction.RawResponse)) + builder.WriteString(formatInteractionHeader("HTTP", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp)) + if c.options.DebugRequest || c.options.Debug { + builder.WriteString(formatInteractionMessage("HTTP Request", interaction.RawRequest)) + } + if c.options.DebugResponse || c.options.Debug { + builder.WriteString(formatInteractionMessage("HTTP Response", interaction.RawResponse)) + } case "smtp": - builder.WriteString(fmt.Sprintf("[%s] Received SMTP interaction from %s at %s", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp.Format("2006-01-02 15:04:05"))) - builder.WriteString(fmt.Sprintf("\n------------\nSMTP Interaction\n------------\n\n%s\n\n", interaction.RawRequest)) + builder.WriteString(formatInteractionHeader("SMTP", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp)) + if c.options.DebugRequest || c.options.Debug || c.options.DebugResponse { + builder.WriteString(formatInteractionMessage("SMTP Interaction", interaction.RawRequest)) + } case "ldap": - builder.WriteString(fmt.Sprintf("[%s] Received LDAP interaction from %s at %s", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp.Format("2006-01-02 15:04:05"))) - builder.WriteString(fmt.Sprintf("\n------------\nLDAP Interaction\n------------\n\n%s\n\n", interaction.RawRequest)) + builder.WriteString(formatInteractionHeader("LDAP", interaction.FullId, interaction.RemoteAddress, interaction.Timestamp)) + if c.options.DebugRequest || c.options.Debug || c.options.DebugResponse { + builder.WriteString(formatInteractionMessage("LDAP Interaction", interaction.RawRequest)) + } } fmt.Fprint(os.Stderr, builder.String()) } +func formatInteractionHeader(protocol, ID, address string, at time.Time) string { + return fmt.Sprintf("[%s] Received %s interaction from %s at %s", ID, protocol, address, at.Format("2006-01-02 15:04:05")) +} + +func formatInteractionMessage(key, value string) string { + return fmt.Sprintf("\n------------\n%s\n------------\n\n%s\n\n", key, value) +} + func hash(templateID, host string) string { h := sha1.New() h.Write([]byte(templateID)) From a47d4f8b8f49df7067bd36daa00bf15240e1a61d Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Thu, 27 Jan 2022 13:45:03 +0530 Subject: [PATCH 12/23] validate remote input list URL and it's contents against allowed remote template domain list --- v2/pkg/catalog/loader/remote_loader.go | 38 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/v2/pkg/catalog/loader/remote_loader.go b/v2/pkg/catalog/loader/remote_loader.go index 2aa334fc3..ac2164b33 100644 --- a/v2/pkg/catalog/loader/remote_loader.go +++ b/v2/pkg/catalog/loader/remote_loader.go @@ -58,20 +58,13 @@ func getRemoteTemplatesAndWorkflows(templateURLs, workflowURLs, remoteTemplateDo } func getRemoteContent(URL string, remoteTemplateDomainList []string, remoteContentChannel chan<- RemoteContent, contentType ContentType) { + if err := validateRemoteRemplateURL(URL, remoteTemplateDomainList); err != nil { + remoteContentChannel <- RemoteContent{ + Error: err, + } + return + } if strings.HasPrefix(URL, "http") && (strings.HasSuffix(URL, ".yaml") || strings.HasSuffix(URL, ".yml")) { - parsedURL, err := url.Parse(URL) - if err != nil { - remoteContentChannel <- RemoteContent{ - Error: err, - } - return - } - if !utils.StringSliceContains(remoteTemplateDomainList, parsedURL.Host) { - remoteContentChannel <- RemoteContent{ - Error: errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsedURL.Host), - } - return - } remoteContentChannel <- RemoteContent{ Content: []string{URL}, Type: contentType, @@ -100,6 +93,14 @@ func getRemoteContent(URL string, remoteTemplateDomainList []string, remoteConte if text == "" { continue } + if utils.IsURL(text) { + if err := validateRemoteRemplateURL(text, remoteTemplateDomainList); err != nil { + remoteContentChannel <- RemoteContent{ + Error: err, + } + return + } + } templateList = append(templateList, text) } @@ -115,3 +116,14 @@ func getRemoteContent(URL string, remoteTemplateDomainList []string, remoteConte Type: contentType, } } + +func validateRemoteRemplateURL(inputURL string, remoteTemplateDomainList []string) error { + parsedURL, err := url.Parse(inputURL) + if err != nil { + return err + } + if !utils.StringSliceContains(remoteTemplateDomainList, parsedURL.Host) { + return errors.Errorf("Remote template URL host (%s) is not present in the `remote-template-domain` list in nuclei config", parsedURL.Host) + } + return nil +} From 30850ced509e8557e7cea536003b1c47cec5afad Mon Sep 17 00:00:00 2001 From: Sajad Parra Date: Thu, 27 Jan 2022 17:19:23 +0530 Subject: [PATCH 13/23] fix remote template integration test cases --- v2/cmd/integration-test/loader.go | 55 +++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/v2/cmd/integration-test/loader.go b/v2/cmd/integration-test/loader.go index d118a6411..9c0439c91 100644 --- a/v2/cmd/integration-test/loader.go +++ b/v2/cmd/integration-test/loader.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "io/ioutil" "net/http" "net/http/httptest" "os" @@ -17,6 +18,7 @@ var loaderTestcases = map[string]testutils.TestCase{ "loader/workflow-list.yaml": &remoteWorkflowList{}, "loader/nonexistent-template-list.yaml": &nonExistentTemplateList{}, "loader/nonexistent-workflow-list.yaml": &nonExistentWorkflowList{}, + "loader/template-list-not-allowed.yaml": &remoteTemplateListNotAllowed{}, } type remoteTemplateList struct{} @@ -45,7 +47,14 @@ func (h *remoteTemplateList) Execute(templateList string) error { ts := httptest.NewServer(router) defer ts.Close() - results, err := testutils.RunNucleiBareArgsAndGetResults(debug, "-target", ts.URL, "-tu", ts.URL+"/template_list") + configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]` + err := ioutil.WriteFile("test-config.yaml", []byte(configFileData), os.ModePerm) + if err != nil { + return err + } + defer os.Remove("test-config.yaml") + + results, err := testutils.RunNucleiBareArgsAndGetResults(debug, "-target", ts.URL, "-tu", ts.URL+"/template_list", "-config", "test-config.yaml") if err != nil { return err } @@ -53,6 +62,41 @@ func (h *remoteTemplateList) Execute(templateList string) error { return expectResultsCount(results, 2) } +type remoteTemplateListNotAllowed struct{} + +// Execute executes a test case and returns an error if occurred +func (h *remoteTemplateListNotAllowed) Execute(templateList string) error { + router := httprouter.New() + + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + fmt.Fprintf(w, "This is test matcher text") + if strings.EqualFold(r.Header.Get("test"), "nuclei") { + fmt.Fprintf(w, "This is test headers matcher text") + } + }) + + router.GET("/template_list", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + file, err := os.ReadFile(templateList) + if err != nil { + w.WriteHeader(500) + } + _, err = w.Write(file) + if err != nil { + w.WriteHeader(500) + } + }) + ts := httptest.NewServer(router) + defer ts.Close() + + _, err := testutils.RunNucleiBareArgsAndGetResults(debug, "-target", ts.URL, "-tu", ts.URL+"/template_list") + if err == nil { + return fmt.Errorf("expected error for not allowed remote template list url") + } + + return nil + +} + type remoteWorkflowList struct{} // Execute executes a test case and returns an error if occurred @@ -79,7 +123,14 @@ func (h *remoteWorkflowList) Execute(workflowList string) error { ts := httptest.NewServer(router) defer ts.Close() - results, err := testutils.RunNucleiBareArgsAndGetResults(debug, "-target", ts.URL, "-wu", ts.URL+"/workflow_list") + configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]` + err := ioutil.WriteFile("test-config.yaml", []byte(configFileData), os.ModePerm) + if err != nil { + return err + } + defer os.Remove("test-config.yaml") + + results, err := testutils.RunNucleiBareArgsAndGetResults(debug, "-target", ts.URL, "-wu", ts.URL+"/workflow_list", "-config", "test-config.yaml") if err != nil { return err } From b6fd4ebbcc73f90e2ff5a257cc4b14a2b18c1fb2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Jan 2022 05:18:17 +0000 Subject: [PATCH 14/23] chore(deps): bump github.com/aws/aws-sdk-go in /v2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.42.42 to 1.42.43. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.42.42...v1.42.43) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- v2/go.mod | 2 +- v2/go.sum | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 05040269f..cb3da7c61 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -63,7 +63,7 @@ require ( moul.io/http2curl v1.0.0 ) -require github.com/aws/aws-sdk-go v1.42.42 +require github.com/aws/aws-sdk-go v1.42.43 require github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e diff --git a/v2/go.sum b/v2/go.sum index 4bcd8b27e..c7794ca6b 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,8 +82,8 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.42.42 h1:2K61yu5BApC9ExAwC5Vk6ljWzBGbiRGRQYLW7adhP5U= -github.com/aws/aws-sdk-go v1.42.42/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.43 h1:rLcxH9YgI3zN7TnjBn1Z6V62GPjOEW1IQd0m11Y/nXE= +github.com/aws/aws-sdk-go v1.42.43/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -418,10 +418,6 @@ github.com/projectdiscovery/fileutil v0.0.0-20210928100737-cab279c5d4b5/go.mod h github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e h1:RJJuYyuwskYtzZi2gziy6SE/b7saWEzyskaA252E0VY= github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e/go.mod h1:BMqXH4jNGByVdE2iLtKvc/6XStaiZRuCIaKv1vw9PnI= github.com/projectdiscovery/goflags v0.0.7/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= -github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a h1:EzwVm8i4zmzqZX55vrDtyfogwHh8AAZ3cWCJe4fEduk= -github.com/projectdiscovery/goflags v0.0.8-0.20211028121123-edf02bc05b1a/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= -github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994 h1:4EuhJ+YTGA1eqMnR7zI9BLV0Wu0BvHaDKE9LbxSUFb8= -github.com/projectdiscovery/goflags v0.0.8-0.20220117072744-aa5d42bd5994/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/goflags v0.0.8-0.20220121110825-48035ad3ffe0 h1:KtCp/dCsxXNdT8m0yyWc/4ou4YaKWVakAr3G03TjQCk= github.com/projectdiscovery/goflags v0.0.8-0.20220121110825-48035ad3ffe0/go.mod h1:Jjwsf4eEBPXDSQI2Y+6fd3dBumJv/J1U0nmpM+hy2YY= github.com/projectdiscovery/gologger v1.0.1/go.mod h1:Ok+axMqK53bWNwDSU1nTNwITLYMXMdZtRc8/y1c7sWE= From 4ff22a50d92f82cf1f1597936e449ddb50841a7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 05:31:11 +0000 Subject: [PATCH 15/23] chore(deps): bump github.com/aws/aws-sdk-go in /v2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.42.43 to 1.42.44. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.42.43...v1.42.44) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- v2/go.mod | 2 +- v2/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index cb3da7c61..cf004973e 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -63,7 +63,7 @@ require ( moul.io/http2curl v1.0.0 ) -require github.com/aws/aws-sdk-go v1.42.43 +require github.com/aws/aws-sdk-go v1.42.44 require github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e diff --git a/v2/go.sum b/v2/go.sum index c7794ca6b..f6c0ec227 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,8 +82,8 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.42.43 h1:rLcxH9YgI3zN7TnjBn1Z6V62GPjOEW1IQd0m11Y/nXE= -github.com/aws/aws-sdk-go v1.42.43/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.44 h1:vPlF4cUsdN5ETfvb7ewZFbFZyB6Rsfndt3kS2XqLXKo= +github.com/aws/aws-sdk-go v1.42.44/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= From 3807e648c705971d549d3f92037d32d659306e2e Mon Sep 17 00:00:00 2001 From: mzack Date: Tue, 1 Feb 2022 11:25:29 +0100 Subject: [PATCH 16/23] Fixing exit at first match with extractors --- .../stop-at-first-match-with-extractors.yaml | 18 ++++ .../http/stop-at-first-match.yaml | 17 ++++ v2/cmd/integration-test/http.go | 92 +++++++++++++------ v2/pkg/protocols/http/request.go | 6 +- 4 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 integration_tests/http/stop-at-first-match-with-extractors.yaml create mode 100644 integration_tests/http/stop-at-first-match.yaml diff --git a/integration_tests/http/stop-at-first-match-with-extractors.yaml b/integration_tests/http/stop-at-first-match-with-extractors.yaml new file mode 100644 index 000000000..b22e9f4ec --- /dev/null +++ b/integration_tests/http/stop-at-first-match-with-extractors.yaml @@ -0,0 +1,18 @@ +id: stop-at-first-match-with-extractors + +info: + name: Stop at first match Request with extractors + author: pdteam + severity: info + +requests: + - method: GET + path: + - "{{BaseURL}}?a=1" + - "{{BaseURL}}?a=2" + stop-at-first-match: true + extractors: + - type: kval + part: header + kval: + - "date" \ No newline at end of file diff --git a/integration_tests/http/stop-at-first-match.yaml b/integration_tests/http/stop-at-first-match.yaml new file mode 100644 index 000000000..a5a06da78 --- /dev/null +++ b/integration_tests/http/stop-at-first-match.yaml @@ -0,0 +1,17 @@ +id: stop-at-first-match + +info: + name: Stop at first match Request + author: pdteam + severity: info + +requests: + - method: GET + path: + - "{{BaseURL}}?a=1" + - "{{BaseURL}}?a=2" + matchers: + - type: word + words: + - "This is test" + stop-at-first-match: true \ No newline at end of file diff --git a/v2/cmd/integration-test/http.go b/v2/cmd/integration-test/http.go index 5f7f5ae8c..a9d578a73 100644 --- a/v2/cmd/integration-test/http.go +++ b/v2/cmd/integration-test/http.go @@ -18,32 +18,34 @@ import ( ) var httpTestcases = map[string]testutils.TestCase{ - "http/get-headers.yaml": &httpGetHeaders{}, - "http/get-query-string.yaml": &httpGetQueryString{}, - "http/get-redirects.yaml": &httpGetRedirects{}, - "http/get.yaml": &httpGet{}, - "http/post-body.yaml": &httpPostBody{}, - "http/post-json-body.yaml": &httpPostJSONBody{}, - "http/post-multipart-body.yaml": &httpPostMultipartBody{}, - "http/raw-cookie-reuse.yaml": &httpRawCookieReuse{}, - "http/raw-dynamic-extractor.yaml": &httpRawDynamicExtractor{}, - "http/raw-get-query.yaml": &httpRawGetQuery{}, - "http/raw-get.yaml": &httpRawGet{}, - "http/raw-payload.yaml": &httpRawPayload{}, - "http/raw-post-body.yaml": &httpRawPostBody{}, - "http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{}, - "http/request-condition.yaml": &httpRequestCondition{}, - "http/request-condition-new.yaml": &httpRequestCondition{}, - "http/interactsh.yaml": &httpInteractshRequest{}, - "http/interactsh-stop-at-first-match.yaml": &httpInteractshStopAtFirstMatchRequest{}, - "http/self-contained.yaml": &httpRequestSelContained{}, - "http/get-case-insensitive.yaml": &httpGetCaseInsensitive{}, - "http/get.yaml,http/get-case-insensitive.yaml": &httpGetCaseInsensitiveCluster{}, - "http/get-redirects-chain-headers.yaml": &httpGetRedirectsChainHeaders{}, - "http/dsl-matcher-variable.yaml": &httpDSLVariable{}, - "http/dsl-functions.yaml": &httpDSLFunctions{}, - "http/race-simple.yaml": &httpRaceSimple{}, - "http/race-multiple.yaml": &httpRaceMultiple{}, + "http/get-headers.yaml": &httpGetHeaders{}, + "http/get-query-string.yaml": &httpGetQueryString{}, + "http/get-redirects.yaml": &httpGetRedirects{}, + "http/get.yaml": &httpGet{}, + "http/post-body.yaml": &httpPostBody{}, + "http/post-json-body.yaml": &httpPostJSONBody{}, + "http/post-multipart-body.yaml": &httpPostMultipartBody{}, + "http/raw-cookie-reuse.yaml": &httpRawCookieReuse{}, + "http/raw-dynamic-extractor.yaml": &httpRawDynamicExtractor{}, + "http/raw-get-query.yaml": &httpRawGetQuery{}, + "http/raw-get.yaml": &httpRawGet{}, + "http/raw-payload.yaml": &httpRawPayload{}, + "http/raw-post-body.yaml": &httpRawPostBody{}, + "http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{}, + "http/request-condition.yaml": &httpRequestCondition{}, + "http/request-condition-new.yaml": &httpRequestCondition{}, + "http/interactsh.yaml": &httpInteractshRequest{}, + "http/interactsh-stop-at-first-match.yaml": &httpInteractshStopAtFirstMatchRequest{}, + "http/self-contained.yaml": &httpRequestSelContained{}, + "http/get-case-insensitive.yaml": &httpGetCaseInsensitive{}, + "http/get.yaml,http/get-case-insensitive.yaml": &httpGetCaseInsensitiveCluster{}, + "http/get-redirects-chain-headers.yaml": &httpGetRedirectsChainHeaders{}, + "http/dsl-matcher-variable.yaml": &httpDSLVariable{}, + "http/dsl-functions.yaml": &httpDSLFunctions{}, + "http/race-simple.yaml": &httpRaceSimple{}, + "http/race-multiple.yaml": &httpRaceMultiple{}, + "http/stop-at-first-match.yaml": &httpStopAtFirstMatch{}, + "http/stop-at-first-match-with-extractors.yaml": &httpStopAtFirstMatchWithExtractors{}, } type httpInteractshRequest struct{} @@ -727,3 +729,41 @@ func (h *httpRaceMultiple) Execute(filePath string) error { } return expectResultsCount(results, 5) } + +type httpStopAtFirstMatch struct{} + +// Execute executes a test case and returns an error if occurred +func (h *httpStopAtFirstMatch) Execute(filePath string) error { + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + fmt.Fprintf(w, "This is test") + }) + ts := httptest.NewServer(router) + defer ts.Close() + + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug) + if err != nil { + return err + } + + return expectResultsCount(results, 1) +} + +type httpStopAtFirstMatchWithExtractors struct{} + +// Execute executes a test case and returns an error if occurred +func (h *httpStopAtFirstMatchWithExtractors) Execute(filePath string) error { + router := httprouter.New() + router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + fmt.Fprintf(w, "This is test") + }) + ts := httptest.NewServer(router) + defer ts.Close() + + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug) + if err != nil { + return err + } + + return expectResultsCount(results, 2) +} diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go index c4dccaf9f..eefd33d8a 100644 --- a/v2/pkg/protocols/http/request.go +++ b/v2/pkg/protocols/http/request.go @@ -256,13 +256,13 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(reqURL) { return true, nil } - var gotOutput bool + var gotMatches bool request.options.RateLimiter.Take() err = request.executeRequest(reqURL, generatedHttpRequest, previous, hasInteractMarkers, func(event *output.InternalWrappedEvent) { // Add the extracts to the dynamic values if any. if event.OperatorsResult != nil { - gotOutput = true + gotMatches = event.OperatorsResult.Matched gotDynamicValues = generators.MergeMapsMany(event.OperatorsResult.DynamicValues, dynamicValues, gotDynamicValues) } if hasInteractMarkers && request.options.Interactsh != nil { @@ -292,7 +292,7 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou request.options.Progress.IncrementRequests() // If this was a match, and we want to stop at first match, skip all further requests. - if (generatedHttpRequest.original.options.Options.StopAtFirstMatch || generatedHttpRequest.original.options.StopAtFirstMatch || request.StopAtFirstMatch) && gotOutput { + if (generatedHttpRequest.original.options.Options.StopAtFirstMatch || generatedHttpRequest.original.options.StopAtFirstMatch || request.StopAtFirstMatch) && gotMatches { return true, nil } return false, nil From b3c61ee157b1332e004ce4615b7cd7b8b0e0e27a Mon Sep 17 00:00:00 2001 From: mzack Date: Tue, 1 Feb 2022 12:10:18 +0100 Subject: [PATCH 17/23] Differentiate between interact matchers and markers --- v2/pkg/protocols/common/interactsh/interactsh.go | 5 +++++ v2/pkg/protocols/http/request.go | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/v2/pkg/protocols/common/interactsh/interactsh.go b/v2/pkg/protocols/common/interactsh/interactsh.go index 650d485c5..fe3f7f218 100644 --- a/v2/pkg/protocols/common/interactsh/interactsh.go +++ b/v2/pkg/protocols/common/interactsh/interactsh.go @@ -345,6 +345,11 @@ func HasMatchers(op *operators.Operators) bool { return false } +// HasMarkers checks if the text contains interactsh markers +func HasMarkers(data string) bool { + return strings.Contains(data, interactshURLMarker) +} + func (c *Client) debugPrintInteraction(interaction *server.Interaction) { builder := &bytes.Buffer{} diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go index c4dccaf9f..90df53d85 100644 --- a/v2/pkg/protocols/http/request.go +++ b/v2/pkg/protocols/http/request.go @@ -239,7 +239,8 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou for { // returns two values, error and skip, which skips the execution for the request instance. executeFunc := func(data string, payloads, dynamicValue map[string]interface{}) (bool, error) { - hasInteractMarkers := interactsh.HasMatchers(request.CompiledOperators) + hasInteractMarkers := interactsh.HasMarkers(data) + hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators) generatedHttpRequest, err := generator.Make(reqURL, data, payloads, dynamicValue) if err != nil { @@ -259,13 +260,13 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou var gotOutput bool request.options.RateLimiter.Take() - err = request.executeRequest(reqURL, generatedHttpRequest, previous, hasInteractMarkers, func(event *output.InternalWrappedEvent) { + err = request.executeRequest(reqURL, generatedHttpRequest, previous, hasInteractMatchers, func(event *output.InternalWrappedEvent) { // Add the extracts to the dynamic values if any. if event.OperatorsResult != nil { gotOutput = true gotDynamicValues = generators.MergeMapsMany(event.OperatorsResult.DynamicValues, dynamicValues, gotDynamicValues) } - if hasInteractMarkers && request.options.Interactsh != nil { + if hasInteractMarkers && hasInteractMatchers && request.options.Interactsh != nil { request.options.Interactsh.RequestEvent(generatedHttpRequest.interactshURLs, &interactsh.RequestData{ MakeResultFunc: request.MakeResultEvent, Event: event, @@ -329,7 +330,7 @@ const drainReqSize = int64(8 * 1024) var errStopExecution = errors.New("stop execution due to unresolved variables") // executeRequest executes the actual generated request and returns error if occurred -func (request *Request) executeRequest(reqURL string, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMarkers bool, callback protocols.OutputEventCallback, requestCount int) error { +func (request *Request) executeRequest(reqURL string, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMatchers bool, callback protocols.OutputEventCallback, requestCount int) error { request.setCustomHeaders(generatedRequest) var ( @@ -434,7 +435,7 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate // If we have interactsh markers and request times out, still send // a callback event so in case we receive an interaction, correlation is possible. - if hasInteractMarkers { + if hasInteractMatchers { outputEvent := request.responseToDSLMap(&http.Response{}, reqURL, formedURL, tostring.UnsafeToString(dumpedRequest), "", "", "", 0, generatedRequest.meta) if i := strings.LastIndex(hostname, ":"); i != -1 { hostname = hostname[:i] @@ -558,7 +559,7 @@ func (request *Request) executeRequest(reqURL string, generatedRequest *generate event := eventcreator.CreateEventWithAdditionalOptions(request, generators.MergeMaps(generatedRequest.dynamicValues, finalEvent), request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) { internalWrappedEvent.OperatorsResult.PayloadValues = generatedRequest.meta }) - if hasInteractMarkers { + if hasInteractMatchers { event.UsesInteractsh = true } From 4370abf89b74708102d0e80e5dad27304ae75278 Mon Sep 17 00:00:00 2001 From: mzack Date: Tue, 1 Feb 2022 12:34:12 +0100 Subject: [PATCH 18/23] improving markers detection --- v2/pkg/protocols/http/request.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v2/pkg/protocols/http/request.go b/v2/pkg/protocols/http/request.go index 90df53d85..0da3dbe8d 100644 --- a/v2/pkg/protocols/http/request.go +++ b/v2/pkg/protocols/http/request.go @@ -239,9 +239,7 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou for { // returns two values, error and skip, which skips the execution for the request instance. executeFunc := func(data string, payloads, dynamicValue map[string]interface{}) (bool, error) { - hasInteractMarkers := interactsh.HasMarkers(data) hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators) - generatedHttpRequest, err := generator.Make(reqURL, data, payloads, dynamicValue) if err != nil { if err == io.EOF { @@ -250,6 +248,7 @@ func (request *Request) ExecuteWithResults(reqURL string, dynamicValues, previou request.options.Progress.IncrementFailedRequestsBy(int64(generator.Total())) return true, err } + hasInteractMarkers := interactsh.HasMarkers(data) || len(generatedHttpRequest.interactshURLs) > 0 if reqURL == "" { reqURL = generatedHttpRequest.URL() } From 5889a39fb487aa1825e530c3907222f779a87e77 Mon Sep 17 00:00:00 2001 From: mzack Date: Tue, 1 Feb 2022 14:46:40 +0100 Subject: [PATCH 19/23] Disable internal standard library logger --- v2/internal/runner/options.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/v2/internal/runner/options.go b/v2/internal/runner/options.go index 5c666282a..b95813033 100644 --- a/v2/internal/runner/options.go +++ b/v2/internal/runner/options.go @@ -2,6 +2,8 @@ package runner import ( "bufio" + "io" + "log" "os" "path/filepath" "strings" @@ -161,6 +163,10 @@ func configureOutput(options *types.Options) { if options.Silent { gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) } + + // disable standard logger (ref: https://github.com/golang/go/issues/19895) + log.SetFlags(0) + log.SetOutput(io.Discard) } // loadResolvers loads resolvers from both user provided flag and file From 2e6476eac2291df5caae852bd2daf3b6df628c76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Feb 2022 05:21:13 +0000 Subject: [PATCH 20/23] chore(deps): bump github.com/shirou/gopsutil/v3 in /v2 Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.21.12 to 3.22.1. - [Release notes](https://github.com/shirou/gopsutil/releases) - [Commits](https://github.com/shirou/gopsutil/compare/v3.21.12...v3.22.1) --- updated-dependencies: - dependency-name: github.com/shirou/gopsutil/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- v2/go.mod | 4 ++-- v2/go.sum | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index cf004973e..375159774 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -43,7 +43,7 @@ require ( github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.3.0 // indirect github.com/segmentio/ksuid v1.0.4 - github.com/shirou/gopsutil/v3 v3.21.12 + github.com/shirou/gopsutil/v3 v3.22.1 github.com/spaolacci/murmur3 v1.1.0 github.com/spf13/cast v1.4.1 github.com/syndtr/goleveldb v1.0.0 @@ -145,7 +145,7 @@ require ( goftp.io/server/v2 v2.0.0 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect + golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/v2/go.sum b/v2/go.sum index f6c0ec227..1f09f1ee1 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -221,8 +221,9 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -482,8 +483,8 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4= -github.com/shirou/gopsutil/v3 v3.21.12 h1:VoGxEW2hpmz0Vt3wUvHIl9fquzYLNpVpgNNB7pGJimA= -github.com/shirou/gopsutil/v3 v3.21.12/go.mod h1:BToYZVTlSVlfazpDDYFnsVZLaoRG+g8ufT6fPQLdJzA= +github.com/shirou/gopsutil/v3 v3.22.1 h1:33y31Q8J32+KstqPfscvFwBlNJ6xLaBy4xqBXzlYV5w= +github.com/shirou/gopsutil/v3 v3.22.1/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= @@ -775,10 +776,10 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 4d6a8a71a777882cd0a10bac82d84c1adca55701 Mon Sep 17 00:00:00 2001 From: mzack Date: Wed, 2 Feb 2022 07:54:15 +0100 Subject: [PATCH 21/23] Adding support for DNS CAA query --- integration_tests/dns/caa.yaml | 22 ++++++++++++++++++++++ v2/cmd/integration-test/dns.go | 17 +++++++++++++++++ v2/pkg/protocols/dns/dns.go | 2 ++ v2/pkg/protocols/dns/dns_types.go | 3 +++ 4 files changed, 44 insertions(+) create mode 100644 integration_tests/dns/caa.yaml diff --git a/integration_tests/dns/caa.yaml b/integration_tests/dns/caa.yaml new file mode 100644 index 000000000..9a2ffc987 --- /dev/null +++ b/integration_tests/dns/caa.yaml @@ -0,0 +1,22 @@ +id: caa-fingerprinting + +info: + name: CAA Fingerprint + author: pdteam + severity: info + tags: dns,caa + +dns: + - name: "{{FQDN}}" + type: CAA + + matchers: + - type: word + words: + - "IN\tCAA" + + extractors: + - type: regex + group: 1 + regex: + - "IN\tCAA\t(.+)" \ No newline at end of file diff --git a/v2/cmd/integration-test/dns.go b/v2/cmd/integration-test/dns.go index 8e3b7213a..e0068fa90 100644 --- a/v2/cmd/integration-test/dns.go +++ b/v2/cmd/integration-test/dns.go @@ -7,6 +7,7 @@ import ( var dnsTestCases = map[string]testutils.TestCase{ "dns/basic.yaml": &dnsBasic{}, "dns/ptr.yaml": &dnsPtr{}, + "dns/caa.yaml": &dnsCAA{}, } type dnsBasic struct{} @@ -40,3 +41,19 @@ func (h *dnsPtr) Execute(filePath string) error { } return expectResultsCount(results, 1) } + +type dnsCAA struct{} + +// Execute executes a test case and returns an error if occurred +func (h *dnsCAA) Execute(filePath string) error { + var routerErr error + + results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "google.com", debug) + if err != nil { + return err + } + if routerErr != nil { + return routerErr + } + return expectResultsCount(results, 1) +} diff --git a/v2/pkg/protocols/dns/dns.go b/v2/pkg/protocols/dns/dns.go index 31adee9e7..279e6eea6 100644 --- a/v2/pkg/protocols/dns/dns.go +++ b/v2/pkg/protocols/dns/dns.go @@ -234,6 +234,8 @@ func questionTypeToInt(questionType string) uint16 { question = dns.TypeDS case "AAAA": question = dns.TypeAAAA + case "CAA": + question = dns.TypeCAA } return question } diff --git a/v2/pkg/protocols/dns/dns_types.go b/v2/pkg/protocols/dns/dns_types.go index dc0a22d2c..34c8b7e16 100644 --- a/v2/pkg/protocols/dns/dns_types.go +++ b/v2/pkg/protocols/dns/dns_types.go @@ -31,6 +31,8 @@ const ( TXT // name:AAAA AAAA + // name:CAA + CAA limit ) @@ -45,6 +47,7 @@ var DNSRequestTypeMapping = map[DNSRequestType]string{ MX: "MX", TXT: "TXT", AAAA: "AAAA", + CAA: "CAA", } // GetSupportedDNSRequestTypes returns list of supported types From 7c47395780bc69f1e47331e556d109923387c877 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 05:25:54 +0000 Subject: [PATCH 22/23] chore(deps): bump github.com/aws/aws-sdk-go in /v2 Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.42.44 to 1.42.45. - [Release notes](https://github.com/aws/aws-sdk-go/releases) - [Changelog](https://github.com/aws/aws-sdk-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/aws-sdk-go/compare/v1.42.44...v1.42.45) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- v2/go.mod | 2 +- v2/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/go.mod b/v2/go.mod index 375159774..01829474b 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -63,7 +63,7 @@ require ( moul.io/http2curl v1.0.0 ) -require github.com/aws/aws-sdk-go v1.42.44 +require github.com/aws/aws-sdk-go v1.42.45 require github.com/projectdiscovery/folderutil v0.0.0-20211206150108-b4e7ea80f36e diff --git a/v2/go.sum b/v2/go.sum index 1f09f1ee1..85016d38a 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -82,8 +82,8 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.42.44 h1:vPlF4cUsdN5ETfvb7ewZFbFZyB6Rsfndt3kS2XqLXKo= -github.com/aws/aws-sdk-go v1.42.44/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= +github.com/aws/aws-sdk-go v1.42.45 h1:rzYlmOX2EqdsYKvo0WBBffuff3BuckL1UB2KyzWhXyQ= +github.com/aws/aws-sdk-go v1.42.45/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= From ddd652c3ed828aeb7f5ba14e00a76054f850b7a6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 3 Feb 2022 05:55:07 +0000 Subject: [PATCH 23/23] Auto Generate Syntax Docs + JSONSchema [Thu Feb 3 05:55:07 UTC 2022] :robot: --- SYNTAX-REFERENCE.md | 2 ++ nuclei-jsonschema.json | 3 ++- v2/pkg/templates/templates_doc.go | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/SYNTAX-REFERENCE.md b/SYNTAX-REFERENCE.md index e55cf986a..94e14315f 100755 --- a/SYNTAX-REFERENCE.md +++ b/SYNTAX-REFERENCE.md @@ -2506,6 +2506,8 @@ Enum Values: - TXT - AAAA + + - CAA
diff --git a/nuclei-jsonschema.json b/nuclei-jsonschema.json index 6e4148745..a0bed62d9 100755 --- a/nuclei-jsonschema.json +++ b/nuclei-jsonschema.json @@ -338,7 +338,8 @@ "PTR", "MX", "TXT", - "AAAA" + "AAAA", + "CAA" ], "type": "string", "title": "type of DNS request to make", diff --git a/v2/pkg/templates/templates_doc.go b/v2/pkg/templates/templates_doc.go index 024408d52..8cffb6778 100644 --- a/v2/pkg/templates/templates_doc.go +++ b/v2/pkg/templates/templates_doc.go @@ -1110,6 +1110,7 @@ func init() { "MX", "TXT", "AAAA", + "CAA", } FILERequestDoc.Type = "file.Request"