mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 04:55:28 +00:00
Issue 2987 fuzz options (#3355)
* Add override fuzzing type and mode flags * Update english readme * Fix failing tests * Add the integration tests - validate the command line overriding type and mode for fuzzing
This commit is contained in:
parent
011d61eb39
commit
572c8eb780
@ -200,6 +200,10 @@ INTERACTSH:
|
||||
-interactions-cooldown-period int extra time for interaction polling before exiting (default 5)
|
||||
-ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
|
||||
|
||||
FUZZING:
|
||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover enable uncover engine
|
||||
-uq, -uncover-query string[] uncover search query
|
||||
|
||||
27
integration_tests/fuzz/fuzz-mode.yaml
Normal file
27
integration_tests/fuzz/fuzz-mode.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
id: fuzz-query
|
||||
|
||||
info:
|
||||
name: Basic Fuzz URL Query
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
fuzzing:
|
||||
- part: query
|
||||
type: postfix
|
||||
mode: multiple
|
||||
keys: ["id","name"]
|
||||
fuzz: ["fuzz-word"]
|
||||
matchers-condition: and
|
||||
matchers:
|
||||
- type: word
|
||||
part: body
|
||||
words:
|
||||
- "fuzz-word"
|
||||
- type: word
|
||||
part: header
|
||||
words:
|
||||
- "text/html"
|
||||
27
integration_tests/fuzz/fuzz-type.yaml
Normal file
27
integration_tests/fuzz/fuzz-type.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
id: fuzz-type
|
||||
|
||||
info:
|
||||
name: Basic Fuzz URL Query
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
requests:
|
||||
- method: GET
|
||||
path:
|
||||
- "{{BaseURL}}"
|
||||
fuzzing:
|
||||
- part: query
|
||||
type: postfix
|
||||
mode: single
|
||||
keys: ["id"]
|
||||
fuzz: ["fuzz-word"]
|
||||
matchers-condition: and
|
||||
matchers:
|
||||
- type: word
|
||||
part: body
|
||||
words:
|
||||
- "fuzz-word"
|
||||
- type: word
|
||||
part: header
|
||||
words:
|
||||
- "text/html"
|
||||
128
v2/cmd/integration-test/fuzz.go
Normal file
128
v2/cmd/integration-test/fuzz.go
Normal file
@ -0,0 +1,128 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
|
||||
)
|
||||
|
||||
var fuzzingTestCases = map[string]testutils.TestCase{
|
||||
"fuzz/fuzz-mode.yaml": &fuzzModeOverride{},
|
||||
"fuzz/fuzz-type.yaml": &fuzzTypeOverride{},
|
||||
"fuzz/fuzz-query.yaml": &httpFuzzQuery{},
|
||||
}
|
||||
|
||||
type httpFuzzQuery struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *httpFuzzQuery) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type fuzzModeOverride struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *fuzzModeOverride) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example&name=nuclei", false, "-fuzzing-mode", "single", "-json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
var event output.ResultEvent
|
||||
err = json.Unmarshal([]byte(results[0]), &event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal event: %s", err)
|
||||
}
|
||||
|
||||
// Check whether the matched value url query params are correct
|
||||
// default fuzzing mode is multiple in template, so all query params should be fuzzed
|
||||
// but using -fm flag we are overriding fuzzing mode to single,
|
||||
// so only one query param should be fuzzed, and the other should be the same
|
||||
|
||||
//parse url to get query params
|
||||
matchedURL, err := url.Parse(event.Matched)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values, err := url.ParseQuery(matchedURL.RawQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if values.Get("name") != "nuclei" {
|
||||
return fmt.Errorf("expected fuzzing should not override the name nuclei got %s", values.Get("name"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type fuzzTypeOverride struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *fuzzTypeOverride) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?id=example", false, "-fuzzing-type", "replace", "-json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
var event output.ResultEvent
|
||||
err = json.Unmarshal([]byte(results[0]), &event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal event: %s", err)
|
||||
}
|
||||
|
||||
// check whether the matched url query params are fuzzed
|
||||
// default fuzzing type in template is postfix but we are overriding it to replace
|
||||
// so the matched url query param should be replaced with fuzz-word
|
||||
|
||||
//parse url to get query params
|
||||
matchedURL, err := url.Parse(event.Matched)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values, err := url.ParseQuery(matchedURL.RawQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if values.Get("id") != "fuzz-word" {
|
||||
return fmt.Errorf("expected id to be fuzz-word, got %s", values.Get("id"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -33,7 +33,6 @@ var httpTestcases = map[string]testutils.TestCase{
|
||||
"http/get-host-redirects.yaml": &httpGetHostRedirects{},
|
||||
"http/disable-redirects.yaml": &httpDisableRedirects{},
|
||||
"http/get.yaml": &httpGet{},
|
||||
"http/fuzz-query.yaml": &httpFuzzQuery{},
|
||||
"http/post-body.yaml": &httpPostBody{},
|
||||
"http/post-json-body.yaml": &httpPostJSONBody{},
|
||||
"http/post-multipart-body.yaml": &httpPostMultipartBody{},
|
||||
@ -1128,26 +1127,6 @@ func (h *annotationTimeout) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type httpFuzzQuery struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *httpFuzzQuery) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type customAttackType struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
|
||||
@ -35,6 +35,7 @@ var (
|
||||
"file": fileTestcases,
|
||||
"offlineHttp": offlineHttpTestcases,
|
||||
"customConfigDir": customConfigDirTestCases,
|
||||
"fuzzing": fuzzingTestCases,
|
||||
}
|
||||
|
||||
// For debug purposes
|
||||
|
||||
@ -221,6 +221,11 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.NoInteractsh, "no-interactsh", "ni", false, "disable interactsh server for OAST testing, exclude OAST based templates"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("fuzzing", "Fuzzing",
|
||||
flagSet.StringVarP(&options.FuzzingType, "fuzzing-type", "ft", "", "overrides fuzzing type set in template (replace, prefix, postfix, infix)"),
|
||||
flagSet.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("uncover", "Uncover",
|
||||
flagSet.BoolVarP(&options.Uncover, "uncover", "uc", false, "enable uncover engine"),
|
||||
flagSet.StringSliceVarP(&options.UncoverQuery, "uncover-query", "uq", nil, "uncover search query", goflags.FileStringSliceOptions),
|
||||
|
||||
@ -107,16 +107,17 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
}
|
||||
if rule.Part != "" {
|
||||
if valueType, ok := stringToPartType[rule.Part]; !ok {
|
||||
return errors.Errorf("invalid part value specified: %s", rule.Mode)
|
||||
return errors.Errorf("invalid part value specified: %s", rule.Part)
|
||||
} else {
|
||||
rule.partType = valueType
|
||||
}
|
||||
} else {
|
||||
rule.partType = queryPartType
|
||||
}
|
||||
|
||||
if rule.Type != "" {
|
||||
if valueType, ok := stringToRuleType[rule.Type]; !ok {
|
||||
return errors.Errorf("invalid type value specified: %s", rule.Mode)
|
||||
return errors.Errorf("invalid type value specified: %s", rule.Type)
|
||||
} else {
|
||||
rule.ruleType = valueType
|
||||
}
|
||||
|
||||
@ -363,6 +363,12 @@ func (request *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||
return errors.New("cannot use unsafe with http fuzzing templates")
|
||||
}
|
||||
for _, rule := range request.Fuzzing {
|
||||
if fuzzingMode := options.Options.FuzzingMode; fuzzingMode != "" {
|
||||
rule.Mode = fuzzingMode
|
||||
}
|
||||
if fuzzingType := options.Options.FuzzingType; fuzzingType != "" {
|
||||
rule.Type = fuzzingType
|
||||
}
|
||||
if err := rule.Compile(request.generator, request.options); err != nil {
|
||||
return errors.Wrap(err, "could not compile fuzzing rule")
|
||||
}
|
||||
|
||||
@ -344,6 +344,10 @@ type Options struct {
|
||||
AwsRegion string
|
||||
// Scan Strategy (auto,hosts-spray,templates-spray)
|
||||
ScanStrategy string
|
||||
// Fuzzing Type overrides template level fuzzing-type configuration
|
||||
FuzzingType string
|
||||
// Fuzzing Mode overrides template level fuzzing-mode configuration
|
||||
FuzzingMode string
|
||||
}
|
||||
|
||||
// ShouldLoadResume resume file
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user