From 44821e6b77fed27661c71b7c1402113a7ab4b2a4 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 4 May 2020 23:24:59 +0200 Subject: [PATCH] begin of work on fuzzing - only working for raw requests with payload (TODO code cleanup) --- go.mod | 1 + go.sum | 2 + pkg/executor/executer_http.go | 2 +- pkg/generators/attack.go | 20 ++++ pkg/generators/clusterbomb.go | 50 ++++++++ pkg/generators/dsl.go | 103 ++++++++++++++++ pkg/generators/pitchfork.go | 38 ++++++ pkg/generators/sniper.go | 21 ++++ pkg/generators/util.go | 108 +++++++++++++++++ pkg/matchers/compile.go | 74 +----------- pkg/requests/http-request.go | 214 ++++++++++++++++++++++++++-------- pkg/templates/compile.go | 8 ++ 12 files changed, 519 insertions(+), 122 deletions(-) create mode 100644 pkg/generators/attack.go create mode 100644 pkg/generators/clusterbomb.go create mode 100644 pkg/generators/dsl.go create mode 100644 pkg/generators/pitchfork.go create mode 100644 pkg/generators/sniper.go create mode 100644 pkg/generators/util.go diff --git a/go.mod b/go.mod index ae54f776..ba73258d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/Knetic/govaluate v3.0.0+incompatible github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 + github.com/karrick/godirwalk v1.15.6 github.com/miekg/dns v1.1.29 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/gologger v1.0.0 diff --git a/go.sum b/go.sum index e34472c1..b2699167 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8L github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA= +github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= diff --git a/pkg/executor/executer_http.go b/pkg/executor/executer_http.go index f5698717..c09a4511 100644 --- a/pkg/executor/executer_http.go +++ b/pkg/executor/executer_http.go @@ -78,7 +78,7 @@ func (e *HTTPExecutor) ExecuteHTTP(URL string) error { // Send the request to the target servers mainLoop: - for _, req := range compiledRequest { + for req := range compiledRequest { resp, err := e.httpClient.Do(req) if err != nil { if resp != nil { diff --git a/pkg/generators/attack.go b/pkg/generators/attack.go new file mode 100644 index 00000000..cd901d5f --- /dev/null +++ b/pkg/generators/attack.go @@ -0,0 +1,20 @@ +package generators + +// Type is type of attack +type Type int + +const ( + // Sniper attack - each variable replaced with values at a time + Sniper Type = iota + 1 + // PitchFork attack - Each variable replaced with positional value in multiple wordlists + PitchFork + // ClusterBomb attack - Generate all possible combinations of values + ClusterBomb +) + +// AttackTypes is an table for conversion of attack type from string. +var AttackTypes = map[string]Type{ + "sniper": Sniper, + "pitchfork": PitchFork, + "clusterbomb": ClusterBomb, +} diff --git a/pkg/generators/clusterbomb.go b/pkg/generators/clusterbomb.go new file mode 100644 index 00000000..0cd54f74 --- /dev/null +++ b/pkg/generators/clusterbomb.go @@ -0,0 +1,50 @@ +package generators + +// ClusterbombGenerator Attack - Generate all possible combinations from an input map with all values listed +// as slices of the same size +func ClusterbombGenerator(payloads map[string][]string) (out chan map[string]interface{}) { + out = make(chan map[string]interface{}) + + // generator + go func() { + defer close(out) + var order []string + var parts [][]string + for name, wordlist := range payloads { + order = append(order, name) + parts = append(parts, wordlist) + } + + var n = 1 + for _, ar := range parts { + n *= len(ar) + } + + var at = make([]int, len(parts)) + loop: + for { + // increment position counters + for i := len(parts) - 1; i >= 0; i-- { + if at[i] > 0 && at[i] >= len(parts[i]) { + if i == 0 || (i == 1 && at[i-1] == len(parts[0])-1) { + break loop + } + at[i] = 0 + at[i-1]++ + } + } + // construct permutation + item := make(map[string]interface{}) + for i, ar := range parts { + var p = at[i] + if p >= 0 && p < len(ar) { + item[order[i]] = ar[p] + } + } + out <- item + at[len(parts)-1]++ + } + }() + + return out +} diff --git a/pkg/generators/dsl.go b/pkg/generators/dsl.go new file mode 100644 index 00000000..929a8506 --- /dev/null +++ b/pkg/generators/dsl.go @@ -0,0 +1,103 @@ +package generators + +import ( + "crypto/md5" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "html" + "net/url" + "regexp" + "strings" + + "github.com/Knetic/govaluate" +) + +// HelperFunctions contains the dsl functions +func HelperFunctions() (functions map[string]govaluate.ExpressionFunction) { + functions = make(map[string]govaluate.ExpressionFunction) + // strings + functions["len"] = func(args ...interface{}) (interface{}, error) { + length := len(args[0].(string)) + return (float64)(length), nil + } + functions["toupper"] = func(args ...interface{}) (interface{}, error) { + return strings.ToUpper(args[0].(string)), nil + } + functions["tolower"] = func(args ...interface{}) (interface{}, error) { + return strings.ToLower(args[0].(string)), nil + } + functions["replace"] = func(args ...interface{}) (interface{}, error) { + return strings.Replace(args[0].(string), args[1].(string), args[2].(string), -1), nil + } + functions["trim"] = func(args ...interface{}) (interface{}, error) { + return strings.Trim(args[0].(string), args[2].(string)), nil + } + functions["trimleft"] = func(args ...interface{}) (interface{}, error) { + return strings.TrimLeft(args[0].(string), args[1].(string)), nil + } + functions["trimright"] = func(args ...interface{}) (interface{}, error) { + return strings.TrimRight(args[0].(string), args[1].(string)), nil + } + functions["trimspace"] = func(args ...interface{}) (interface{}, error) { + return strings.TrimSpace(args[0].(string)), nil + } + functions["trimprefix"] = func(args ...interface{}) (interface{}, error) { + return strings.TrimPrefix(args[0].(string), args[1].(string)), nil + } + functions["trimsuffix"] = func(args ...interface{}) (interface{}, error) { + return strings.TrimSuffix(args[0].(string), args[1].(string)), nil + } + functions["reverse"] = func(args ...interface{}) (interface{}, error) { + return reverseString(args[0].(string)), nil + } + // encoding + functions["base64"] = func(args ...interface{}) (interface{}, error) { + sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string))) + return sEnc, nil + } + functions["base64_decode"] = func(args ...interface{}) (interface{}, error) { + sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string))) + return sEnc, nil + } + functions["url_encode"] = func(args ...interface{}) (interface{}, error) { + return url.PathEscape(args[0].(string)), nil + } + functions["url_decode"] = func(args ...interface{}) (interface{}, error) { + return url.PathUnescape(args[0].(string)) + } + functions["hex_encode"] = func(args ...interface{}) (interface{}, error) { + return hex.EncodeToString([]byte(args[0].(string))), nil + } + functions["hex_decode"] = func(args ...interface{}) (interface{}, error) { + hx, _ := hex.DecodeString(args[0].(string)) + return string(hx), nil + } + functions["html_escape"] = func(args ...interface{}) (interface{}, error) { + return html.EscapeString(args[0].(string)), nil + } + functions["html_unescape"] = func(args ...interface{}) (interface{}, error) { + return html.UnescapeString(args[0].(string)), nil + } + // hashing + functions["md5"] = func(args ...interface{}) (interface{}, error) { + hash := md5.Sum([]byte(args[0].(string))) + return hex.EncodeToString(hash[:]), nil + } + functions["sha256"] = func(args ...interface{}) (interface{}, error) { + return sha256.Sum256([]byte(args[0].(string))), nil + } + // search + functions["contains"] = func(args ...interface{}) (interface{}, error) { + return strings.Contains(args[0].(string), args[1].(string)), nil + } + functions["regex"] = func(args ...interface{}) (interface{}, error) { + compiled, err := regexp.Compile(args[0].(string)) + if err != nil { + return nil, err + } + return compiled.MatchString(args[1].(string)), nil + } + + return +} diff --git a/pkg/generators/pitchfork.go b/pkg/generators/pitchfork.go new file mode 100644 index 00000000..edce66f5 --- /dev/null +++ b/pkg/generators/pitchfork.go @@ -0,0 +1,38 @@ +package generators + +// PitchforkGenerator Attack - Generate positional combinations from an input map with all values listed +// as slices of the same size +func PitchforkGenerator(payloads map[string][]string) (out chan map[string]interface{}) { + out = make(chan map[string]interface{}) + + size := 0 + + // check if all wordlists have the same size + for _, wordlist := range payloads { + if size == 0 { + size = len(wordlist) + } + + if len(wordlist) != size { + //set size = 0 and exit the cycle + size = 0 + break + } + } + + // generator + go func() { + defer close(out) + + for i := 0; i < size; i++ { + element := make(map[string]interface{}) + for name, wordlist := range payloads { + element[name] = wordlist[i] + } + + out <- element + } + }() + + return out +} diff --git a/pkg/generators/sniper.go b/pkg/generators/sniper.go new file mode 100644 index 00000000..5d70c9fc --- /dev/null +++ b/pkg/generators/sniper.go @@ -0,0 +1,21 @@ +package generators + +// SniperGenerator Attack - Generate sequential combinations +func SniperGenerator(payloads map[string][]string) (out chan map[string]interface{}) { + out = make(chan map[string]interface{}) + + // generator + go func() { + defer close(out) + + for name, wordlist := range payloads { + for _, value := range wordlist { + element := CopyMapWithDefaultValue(payloads, "") + element[name] = value + out <- element + } + } + }() + + return out +} diff --git a/pkg/generators/util.go b/pkg/generators/util.go new file mode 100644 index 00000000..144f273d --- /dev/null +++ b/pkg/generators/util.go @@ -0,0 +1,108 @@ +package generators + +import ( + "bufio" + "os" + "strings" +) + +// LoadWordlists creating proper data structure +func LoadWordlists(payloads map[string]string) map[string][]string { + wordlists := make(map[string][]string) + // load all wordlists + for name, filepath := range payloads { + wordlists[name] = LoadFile(filepath) + } + + return wordlists +} + +// LoadFile into slice of strings +func LoadFile(filepath string) (lines []string) { + for line := range StreamFile(filepath) { + lines = append(lines, line) + } + + return +} + +// StreamFile content to a chan +func StreamFile(filepath string) (content chan string) { + content = make(chan string) + + go func() { + defer close(content) + file, err := os.Open(filepath) + if err != nil { + return + } + defer file.Close() + + // yql filter applied + scanner := bufio.NewScanner(file) + for scanner.Scan() { + content <- scanner.Text() + } + + if err := scanner.Err(); err != nil { + return + } + }() + + return +} + +// MergeMaps into a new one +func MergeMaps(m1, m2 map[string]interface{}) (m map[string]interface{}) { + m = make(map[string]interface{}) + for k, v := range m1 { + m[k] = v + } + for k, v := range m2 { + m[k] = v + } + + return m +} + +func reverseString(s string) string { + runes := []rune(s) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return string(runes) +} + +// CopyMap creates a new copy of an existing map +func CopyMap(originalMap map[string]interface{}) map[string]interface{} { + newMap := make(map[string]interface{}) + for key, value := range originalMap { + newMap[key] = value + } + return newMap +} + +// CopyMapWithDefaultValue creates a new copy of an existing map and set a default value +func CopyMapWithDefaultValue(originalMap map[string][]string, defaultValue interface{}) map[string]interface{} { + newMap := make(map[string]interface{}) + for key := range originalMap { + newMap[key] = defaultValue + } + return newMap +} + +// StringContainsAnyMapItem verifies is a string contains any value of a map +func StringContainsAnyMapItem(m map[string]interface{}, s string) bool { + for key := range m { + if strings.Contains(s, key) { + return true + } + } + + return false +} + +// TrimDelimiters removes trailing brackets +func TrimDelimiters(s string) string { + return strings.TrimSuffix(strings.TrimPrefix(s, "{{"), "}}") +} diff --git a/pkg/matchers/compile.go b/pkg/matchers/compile.go index d77ffb8c..b0ddfb8f 100644 --- a/pkg/matchers/compile.go +++ b/pkg/matchers/compile.go @@ -1,15 +1,11 @@ package matchers import ( - "crypto/md5" - "crypto/sha256" - "encoding/base64" - "encoding/hex" "fmt" "regexp" - "strings" "github.com/Knetic/govaluate" + "github.com/projectdiscovery/nuclei/pkg/generators" ) // CompileMatchers performs the initial setup operation on a matcher @@ -34,7 +30,7 @@ func (m *Matcher) CompileMatchers() error { // Compile the dsl expressions for _, dsl := range m.DSL { - compiled, err := govaluate.NewEvaluableExpressionWithFunctions(dsl, helperFunctions()) + compiled, err := govaluate.NewEvaluableExpressionWithFunctions(dsl, generators.HelperFunctions()) if err != nil { return fmt.Errorf("could not compile dsl: %s", dsl) } @@ -63,69 +59,3 @@ func (m *Matcher) CompileMatchers() error { } return nil } - -func helperFunctions() (functions map[string]govaluate.ExpressionFunction) { - functions = make(map[string]govaluate.ExpressionFunction) - // strings - functions["len"] = func(args ...interface{}) (interface{}, error) { - length := len(args[0].(string)) - return (float64)(length), nil - } - functions["toupper"] = func(args ...interface{}) (interface{}, error) { - return strings.ToUpper(args[0].(string)), nil - } - functions["tolower"] = func(args ...interface{}) (interface{}, error) { - return strings.ToLower(args[0].(string)), nil - } - functions["replace"] = func(args ...interface{}) (interface{}, error) { - return strings.Replace(args[0].(string), args[1].(string), args[2].(string), -1), nil - } - functions["trim"] = func(args ...interface{}) (interface{}, error) { - return strings.Trim(args[0].(string), args[2].(string)), nil - } - functions["trimleft"] = func(args ...interface{}) (interface{}, error) { - return strings.TrimLeft(args[0].(string), args[1].(string)), nil - } - functions["trimright"] = func(args ...interface{}) (interface{}, error) { - return strings.TrimRight(args[0].(string), args[1].(string)), nil - } - functions["trimspace"] = func(args ...interface{}) (interface{}, error) { - return strings.TrimSpace(args[0].(string)), nil - } - functions["trimprefix"] = func(args ...interface{}) (interface{}, error) { - return strings.TrimPrefix(args[0].(string), args[1].(string)), nil - } - functions["trimsuffix"] = func(args ...interface{}) (interface{}, error) { - return strings.TrimSuffix(args[0].(string), args[1].(string)), nil - } - // encoding - functions["base64"] = func(args ...interface{}) (interface{}, error) { - sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string))) - return sEnc, nil - } - functions["base64_decode"] = func(args ...interface{}) (interface{}, error) { - sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string))) - return sEnc, nil - } - // hashing - functions["md5"] = func(args ...interface{}) (interface{}, error) { - hash := md5.Sum([]byte(args[0].(string))) - return hex.EncodeToString(hash[:]), nil - } - functions["sha256"] = func(args ...interface{}) (interface{}, error) { - return sha256.Sum256([]byte(args[0].(string))), nil - } - // search - functions["contains"] = func(args ...interface{}) (interface{}, error) { - return strings.Contains(args[0].(string), args[1].(string)), nil - } - functions["regex"] = func(args ...interface{}) (interface{}, error) { - compiled, err := regexp.Compile(args[0].(string)) - if err != nil { - return nil, err - } - return compiled.MatchString(args[1].(string)), nil - } - - return -} diff --git a/pkg/requests/http-request.go b/pkg/requests/http-request.go index c986e077..c5326f9c 100644 --- a/pkg/requests/http-request.go +++ b/pkg/requests/http-request.go @@ -4,17 +4,28 @@ import ( "bufio" "fmt" "io/ioutil" + "log" "net/http" "net/url" + "regexp" "strings" + "github.com/Knetic/govaluate" "github.com/projectdiscovery/nuclei/pkg/extractors" + "github.com/projectdiscovery/nuclei/pkg/generators" "github.com/projectdiscovery/nuclei/pkg/matchers" retryablehttp "github.com/projectdiscovery/retryablehttp-go" ) // HTTPRequest contains a request to be made from a template type HTTPRequest struct { + // AttackType is the attack type + // Sniper, PitchFork and ClusterBomb. Default is Sniper + AttackType string `yaml:"attack,omitempty"` + // attackType is internal attack type + attackType generators.Type + // Path contains the path/s for the request variables + Payloads map[string]string `yaml:"payloads,omitempty"` // Method is the request method, whether GET, POST, PUT, etc Method string `yaml:"method"` // Path contains the path/s for the request @@ -52,8 +63,18 @@ func (r *HTTPRequest) SetMatchersCondition(condition matchers.ConditionType) { r.matchersCondition = condition } +// GetAttackType returns the attack +func (r *HTTPRequest) GetAttackType() generators.Type { + return r.attackType +} + +// SetAttackType sets the attack +func (r *HTTPRequest) SetAttackType(attack generators.Type) { + r.attackType = attack +} + // MakeHTTPRequest creates a *http.Request from a request configuration -func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request, error) { +func (r *HTTPRequest) MakeHTTPRequest(baseURL string) (chan *retryablehttp.Request, error) { parsed, err := url.Parse(baseURL) if err != nil { return nil, err @@ -73,64 +94,159 @@ func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request, } // MakeHTTPRequestFromModel creates a *http.Request from a request template -func (r *HTTPRequest) makeHTTPRequestFromModel(baseURL string, values map[string]interface{}) (requests []*retryablehttp.Request, err error) { - replacer := newReplacer(values) - for _, path := range r.Path { - // Replace the dynamic variables in the URL if any - URL := replacer.Replace(path) +func (r *HTTPRequest) makeHTTPRequestFromModel(baseURL string, values map[string]interface{}) (requests chan *retryablehttp.Request, err error) { + requests = make(chan *retryablehttp.Request) - // Build a request on the specified URL - req, err := http.NewRequest(r.Method, URL, nil) - if err != nil { - return nil, err + // request generator + go func() { + defer close(requests) + for _, path := range r.Path { + // process base request + replacer := newReplacer(values) + + // Replace the dynamic variables in the URL if any + URL := replacer.Replace(path) + + // Build a request on the specified URL + req, err := http.NewRequest(r.Method, URL, nil) + if err != nil { + // find a way to pass the error + return + } + + request, err := r.fillRequest(req, values) + if err != nil { + // find a way to pass the error + return + } + + requests <- request } - - request, err := r.fillRequest(req, values) - if err != nil { - return nil, err - } - - requests = append(requests, request) - } + }() return } // makeHTTPRequestFromRaw creates a *http.Request from a raw request -func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]interface{}) (requests []*retryablehttp.Request, err error) { - replacer := newReplacer(values) - for _, raw := range r.Raw { - // Add trailing line - raw += "\n" +func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]interface{}) (requests chan *retryablehttp.Request, err error) { + requests = make(chan *retryablehttp.Request) + // request generator + go func() { + defer close(requests) - // Replace the dynamic variables in the URL if any - raw = replacer.Replace(raw) + for _, raw := range r.Raw { + // Add trailing line + raw += "\n" - // Build a parsed request from raw - parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw))) - if err != nil { - return nil, err + if len(r.Payloads) > 0 { + basePayloads := generators.LoadWordlists(r.Payloads) + generatorFunc := generators.SniperGenerator + switch r.attackType { + case generators.PitchFork: + generatorFunc = generators.PitchforkGenerator + case generators.ClusterBomb: + generatorFunc = generators.ClusterbombGenerator + } + + for genValues := range generatorFunc(basePayloads) { + baseValues := generators.CopyMap(values) + finValues := generators.MergeMaps(baseValues, genValues) + + replacer := newReplacer(finValues) + + // Replace the dynamic variables in the URL if any + raw := replacer.Replace(raw) + + dynamicValues := make(map[string]interface{}) + // find all potentials tokens between {{}} + var re = regexp.MustCompile(`(?m)\{\{.+}}`) + for _, match := range re.FindAllString(raw, -1) { + // check if the match contains a dynamic variable + if generators.StringContainsAnyMapItem(finValues, match) { + expr := generators.TrimDelimiters(match) + compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, generators.HelperFunctions()) + if err != nil { + // Debug only - Remove + log.Fatal(err) + } + result, err := compiled.Evaluate(finValues) + if err != nil { + // Debug only - Remove + log.Fatal(err) + } + dynamicValues[expr] = result + } + } + + // replace dynamic values + dynamicReplacer := newReplacer(dynamicValues) + raw = dynamicReplacer.Replace(raw) + + // log.Println(raw) + + // Build a parsed request from raw + parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw))) + if err != nil { + return + } + + // requests generated from http.ReadRequest have incorrect RequestURI, so they + // cannot be used to perform another request directly, we need to generate a new one + // with the new target url + finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL) + req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body) + if err != nil { + return + } + + // copy headers + req.Header = parsedReq.Header.Clone() + + request, err := r.fillRequest(req, values) + if err != nil { + return + } + + requests <- request + } + } else { + // otherwise continue with normal flow + + // base request + replacer := newReplacer(values) + // Replace the dynamic variables in the request if any + raw = replacer.Replace(raw) + + // Build a parsed request from raw + parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw))) + if err != nil { + // find a way to pass the error + return + } + + // requests generated from http.ReadRequest have incorrect RequestURI, so they + // cannot be used to perform another request directly, we need to generate a new one + // with the new target url + finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL) + req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body) + if err != nil { + // find a way to pass the error + return + } + + // copy headers + req.Header = parsedReq.Header.Clone() + + request, err := r.fillRequest(req, values) + if err != nil { + // find a way to pass the error + return + } + + requests <- request + } } - - // requests generated from http.ReadRequest have incorrect RequestURI, so they - // cannot be used to perform another request directly, we need to generate a new one - // with the new target url - finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL) - req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body) - if err != nil { - return nil, err - } - - // copy headers - req.Header = parsedReq.Header.Clone() - - request, err := r.fillRequest(req, values) - if err != nil { - return nil, err - } - - requests = append(requests, request) - } + }() return requests, nil } diff --git a/pkg/templates/compile.go b/pkg/templates/compile.go index aa72a0c3..c3956801 100644 --- a/pkg/templates/compile.go +++ b/pkg/templates/compile.go @@ -3,6 +3,7 @@ package templates import ( "os" + "github.com/projectdiscovery/nuclei/pkg/generators" "github.com/projectdiscovery/nuclei/pkg/matchers" "gopkg.in/yaml.v2" ) @@ -33,6 +34,13 @@ func ParseTemplate(file string) (*Template, error) { request.SetMatchersCondition(condition) } + attack, ok := generators.AttackTypes[request.AttackType] + if !ok { + request.SetAttackType(generators.Sniper) + } else { + request.SetAttackType(attack) + } + for _, matcher := range request.Matchers { if err = matcher.CompileMatchers(); err != nil { return nil, err