mirror of https://github.com/daffainfo/nuclei.git
add `-dast` flag and multiple bug fixes for dast templates (#4941)
* add default get method * remove residual payload logic from old implementation * fuzz: clone current state of component * fuzz: bug fix stacking of payloads in multiple mode * improve stdout template loading stats * stdout: force display warnings if no templates are loaded * update flags in README.md * quote non-ascii chars in extractor output * aws request signature can only be used in signed & verified tmpls * deprecate request signature * remove logic related to deprecated fuzzing input * update test to use ordered params * fix interactsh-url lazy eval: #4946 * output: skip unnecessary updates when unescaping * updates as per requested changesdev
parent
78300e3250
commit
e88889b263
|
@ -221,7 +221,8 @@ INTERACTSH:
|
||||||
FUZZING:
|
FUZZING:
|
||||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
-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)
|
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||||
-fuzz enable loading fuzzing templates
|
-fuzz enable loading fuzzing templates (Deprecated: use -dast instead)
|
||||||
|
-dast only run DAST templates
|
||||||
|
|
||||||
UNCOVER:
|
UNCOVER:
|
||||||
-uc, -uncover enable uncover engine
|
-uc, -uncover enable uncover engine
|
||||||
|
|
|
@ -21,8 +21,6 @@ var fuzzingTestCases = []TestCaseInfo{
|
||||||
{Path: "fuzz/fuzz-type.yaml", TestCase: &fuzzTypeOverride{}},
|
{Path: "fuzz/fuzz-type.yaml", TestCase: &fuzzTypeOverride{}},
|
||||||
{Path: "fuzz/fuzz-query.yaml", TestCase: &httpFuzzQuery{}},
|
{Path: "fuzz/fuzz-query.yaml", TestCase: &httpFuzzQuery{}},
|
||||||
{Path: "fuzz/fuzz-headless.yaml", TestCase: &HeadlessFuzzingQuery{}},
|
{Path: "fuzz/fuzz-headless.yaml", TestCase: &HeadlessFuzzingQuery{}},
|
||||||
{Path: "fuzz/fuzz-header-basic.yaml", TestCase: &FuzzHeaderBasic{}},
|
|
||||||
{Path: "fuzz/fuzz-header-multiple.yaml", TestCase: &FuzzHeaderMultiple{}},
|
|
||||||
// for fuzzing we should prioritize adding test case related backend
|
// for fuzzing we should prioritize adding test case related backend
|
||||||
// logic in fuzz playground server instead of adding them here
|
// logic in fuzz playground server instead of adding them here
|
||||||
{Path: "fuzz/fuzz-query-num-replace.yaml", TestCase: &genericFuzzTestCase{expectedResults: 2}},
|
{Path: "fuzz/fuzz-query-num-replace.yaml", TestCase: &genericFuzzTestCase{expectedResults: 2}},
|
||||||
|
@ -176,52 +174,3 @@ func (h *HeadlessFuzzingQuery) Execute(filePath string) error {
|
||||||
}
|
}
|
||||||
return expectResultsCount(got, 2)
|
return expectResultsCount(got, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FuzzHeaderBasic struct{}
|
|
||||||
|
|
||||||
// Execute executes a test case and returns an error if occurred
|
|
||||||
func (h *FuzzHeaderBasic) Execute(filePath string) error {
|
|
||||||
router := httprouter.New()
|
|
||||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
||||||
host := r.Header.Get("Origin")
|
|
||||||
// redirect to different domain
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintf(w, "<html><body><a href="+host+">Click Here</a></body></html>")
|
|
||||||
})
|
|
||||||
ts := httptest.NewTLSServer(router)
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-fuzz")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return expectResultsCount(got, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
type FuzzHeaderMultiple struct{}
|
|
||||||
|
|
||||||
// Execute executes a test case and returns an error if occurred
|
|
||||||
func (h *FuzzHeaderMultiple) Execute(filePath string) error {
|
|
||||||
router := httprouter.New()
|
|
||||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
|
||||||
host1 := r.Header.Get("Origin")
|
|
||||||
host2 := r.Header.Get("X-Forwared-For")
|
|
||||||
|
|
||||||
fmt.Printf("host1: %s, host2: %s\n", host1, host2)
|
|
||||||
if host1 == host2 && host2 == "secret.local" {
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
fmt.Fprintf(w, "welcome! to secret admin panel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// redirect to different domain
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
})
|
|
||||||
ts := httptest.NewTLSServer(router)
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-fuzz")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return expectResultsCount(got, 1)
|
|
||||||
}
|
|
||||||
|
|
|
@ -186,6 +186,7 @@ func readConfig() *goflags.FlagSet {
|
||||||
// when true updates nuclei binary to latest version
|
// when true updates nuclei binary to latest version
|
||||||
var updateNucleiBinary bool
|
var updateNucleiBinary bool
|
||||||
var pdcpauth string
|
var pdcpauth string
|
||||||
|
var fuzzFlag bool
|
||||||
|
|
||||||
flagSet := goflags.NewFlagSet()
|
flagSet := goflags.NewFlagSet()
|
||||||
flagSet.CaseSensitive = true
|
flagSet.CaseSensitive = true
|
||||||
|
@ -313,7 +314,8 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||||
flagSet.CreateGroup("fuzzing", "Fuzzing",
|
flagSet.CreateGroup("fuzzing", "Fuzzing",
|
||||||
flagSet.StringVarP(&options.FuzzingType, "fuzzing-type", "ft", "", "overrides fuzzing type set in template (replace, prefix, postfix, infix)"),
|
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.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"),
|
||||||
flagSet.BoolVar(&options.FuzzTemplates, "fuzz", false, "enable loading fuzzing templates"),
|
flagSet.BoolVar(&fuzzFlag, "fuzz", false, "enable loading fuzzing templates (Deprecated: use -dast instead)"),
|
||||||
|
flagSet.BoolVar(&options.DAST, "dast", false, "only run DAST templates"),
|
||||||
)
|
)
|
||||||
|
|
||||||
flagSet.CreateGroup("uncover", "Uncover",
|
flagSet.CreateGroup("uncover", "Uncover",
|
||||||
|
@ -436,6 +438,12 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||||
goflags.DisableAutoConfigMigration = true
|
goflags.DisableAutoConfigMigration = true
|
||||||
_ = flagSet.Parse()
|
_ = flagSet.Parse()
|
||||||
|
|
||||||
|
// when fuzz flag is enabled, set the dast flag to true
|
||||||
|
if fuzzFlag {
|
||||||
|
// backwards compatibility for fuzz flag
|
||||||
|
options.DAST = true
|
||||||
|
}
|
||||||
|
|
||||||
// api key hierarchy: cli flag > env var > .pdcp/credential file
|
// api key hierarchy: cli flag > env var > .pdcp/credential file
|
||||||
if pdcpauth == "true" {
|
if pdcpauth == "true" {
|
||||||
runner.AuthWithPDCP()
|
runner.AuthWithPDCP()
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
id: fuzz-header-basic
|
|
||||||
|
|
||||||
info:
|
|
||||||
name: fuzz header basic
|
|
||||||
author: pdteam
|
|
||||||
severity: info
|
|
||||||
description: |
|
|
||||||
In this template we check for any reflection when fuzzing Origin header
|
|
||||||
|
|
||||||
variables:
|
|
||||||
first: "{{rand_int(10000, 99999)}}"
|
|
||||||
|
|
||||||
http:
|
|
||||||
- raw:
|
|
||||||
- |
|
|
||||||
GET /?x=aaa&y=bbb HTTP/1.1
|
|
||||||
Host: {{Hostname}}
|
|
||||||
Origin: https://example.com
|
|
||||||
X-Fuzz-Header: 1337
|
|
||||||
Cookie: z=aaa; bb=aaa
|
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
|
||||||
Accept-Language: en-US,en;q=0.9
|
|
||||||
Connection: close
|
|
||||||
|
|
||||||
payloads:
|
|
||||||
reflection:
|
|
||||||
- "'\"><{{first}}"
|
|
||||||
|
|
||||||
fuzzing:
|
|
||||||
- part: header
|
|
||||||
type: replace
|
|
||||||
mode: single
|
|
||||||
keys: ["Origin"]
|
|
||||||
fuzz:
|
|
||||||
- "{{reflection}}"
|
|
||||||
|
|
||||||
stop-at-first-match: true
|
|
||||||
matchers-condition: and
|
|
||||||
matchers:
|
|
||||||
- type: word
|
|
||||||
part: body
|
|
||||||
words:
|
|
||||||
- "{{reflection}}"
|
|
||||||
|
|
||||||
- type: word
|
|
||||||
part: header
|
|
||||||
words:
|
|
||||||
- "text/html"
|
|
|
@ -1,41 +0,0 @@
|
||||||
id: fuzz-header-multiple
|
|
||||||
|
|
||||||
info:
|
|
||||||
name: fuzz header multiple
|
|
||||||
author: pdteam
|
|
||||||
severity: info
|
|
||||||
description: |
|
|
||||||
In this template we fuzz multiple headers with single payload
|
|
||||||
|
|
||||||
http:
|
|
||||||
- raw:
|
|
||||||
- |
|
|
||||||
GET /?x=aaa&y=bbb HTTP/1.1
|
|
||||||
Host: {{Hostname}}
|
|
||||||
Origin: https://example.com
|
|
||||||
X-Forwared-For: 1337
|
|
||||||
Cookie: z=aaa; bb=aaa
|
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
|
||||||
Accept-Language: en-US,en;q=0.9
|
|
||||||
Connection: close
|
|
||||||
|
|
||||||
payloads:
|
|
||||||
reflection:
|
|
||||||
- "secret.local"
|
|
||||||
|
|
||||||
fuzzing:
|
|
||||||
- part: header
|
|
||||||
type: replace
|
|
||||||
mode: multiple
|
|
||||||
keys: ["Origin", "X-Forwared-For"]
|
|
||||||
fuzz:
|
|
||||||
- "{{reflection}}"
|
|
||||||
|
|
||||||
stop-at-first-match: true
|
|
||||||
matchers-condition: and
|
|
||||||
matchers:
|
|
||||||
- type: word
|
|
||||||
part: body
|
|
||||||
words:
|
|
||||||
- "admin"
|
|
|
@ -475,10 +475,9 @@ func (r *Runner) RunEnumeration() error {
|
||||||
|
|
||||||
// If using input-file flags, only load http fuzzing based templates.
|
// If using input-file flags, only load http fuzzing based templates.
|
||||||
loaderConfig := loader.NewConfig(r.options, r.catalog, executorOpts)
|
loaderConfig := loader.NewConfig(r.options, r.catalog, executorOpts)
|
||||||
if !strings.EqualFold(r.options.InputFileMode, "list") || r.options.FuzzTemplates {
|
if !strings.EqualFold(r.options.InputFileMode, "list") || r.options.DAST {
|
||||||
// if input type is not list (implicitly enable fuzzing)
|
// if input type is not list (implicitly enable fuzzing)
|
||||||
r.options.FuzzTemplates = true
|
r.options.DAST = true
|
||||||
loaderConfig.OnlyLoadHTTPFuzzing = true
|
|
||||||
}
|
}
|
||||||
store, err := loader.New(loaderConfig)
|
store, err := loader.New(loaderConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -640,19 +639,27 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
||||||
stats.Display(templates.SyntaxWarningStats)
|
stats.Display(templates.SyntaxWarningStats)
|
||||||
stats.Display(templates.SyntaxErrorStats)
|
stats.Display(templates.SyntaxErrorStats)
|
||||||
stats.Display(templates.RuntimeWarningsStats)
|
stats.Display(templates.RuntimeWarningsStats)
|
||||||
if r.options.Verbose {
|
tmplCount := len(store.Templates())
|
||||||
|
workflowCount := len(store.Workflows())
|
||||||
|
if r.options.Verbose || (tmplCount == 0 && workflowCount == 0) {
|
||||||
// only print these stats in verbose mode
|
// only print these stats in verbose mode
|
||||||
stats.DisplayAsWarning(templates.HeadlessFlagWarningStats)
|
stats.ForceDisplayWarning(templates.ExcludedHeadlessTmplStats)
|
||||||
stats.DisplayAsWarning(templates.CodeFlagWarningStats)
|
stats.ForceDisplayWarning(templates.ExcludedCodeTmplStats)
|
||||||
stats.DisplayAsWarning(templates.TemplatesExecutedStats)
|
stats.ForceDisplayWarning(templates.ExludedDastTmplStats)
|
||||||
stats.DisplayAsWarning(templates.HeadlessFlagWarningStats)
|
stats.ForceDisplayWarning(templates.TemplatesExcludedStats)
|
||||||
stats.DisplayAsWarning(templates.CodeFlagWarningStats)
|
|
||||||
stats.DisplayAsWarning(templates.FuzzFlagWarningStats)
|
|
||||||
stats.DisplayAsWarning(templates.TemplatesExecutedStats)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.DisplayAsWarning(templates.UnsignedCodeWarning)
|
if tmplCount == 0 && workflowCount == 0 {
|
||||||
|
// if dast flag is used print explicit warning
|
||||||
|
if r.options.DAST {
|
||||||
|
gologger.DefaultLogger.Print().Msgf("[%v] No DAST templates found", aurora.BrightYellow("WRN"))
|
||||||
|
}
|
||||||
|
stats.ForceDisplayWarning(templates.SkippedCodeTmplTamperedStats)
|
||||||
|
} else {
|
||||||
|
stats.DisplayAsWarning(templates.SkippedCodeTmplTamperedStats)
|
||||||
|
}
|
||||||
stats.ForceDisplayWarning(templates.SkippedUnsignedStats)
|
stats.ForceDisplayWarning(templates.SkippedUnsignedStats)
|
||||||
|
stats.ForceDisplayWarning(templates.SkippedRequestSignatureStats)
|
||||||
|
|
||||||
cfg := config.DefaultConfig
|
cfg := config.DefaultConfig
|
||||||
|
|
||||||
|
@ -666,25 +673,27 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(store.Templates()) > 0 {
|
if tmplCount > 0 || workflowCount > 0 {
|
||||||
gologger.Info().Msgf("New templates added in latest release: %d", len(config.DefaultConfig.GetNewAdditions()))
|
if len(store.Templates()) > 0 {
|
||||||
gologger.Info().Msgf("Templates loaded for current scan: %d", len(store.Templates()))
|
gologger.Info().Msgf("New templates added in latest release: %d", len(config.DefaultConfig.GetNewAdditions()))
|
||||||
}
|
gologger.Info().Msgf("Templates loaded for current scan: %d", len(store.Templates()))
|
||||||
if len(store.Workflows()) > 0 {
|
|
||||||
gologger.Info().Msgf("Workflows loaded for current scan: %d", len(store.Workflows()))
|
|
||||||
}
|
|
||||||
for k, v := range templates.SignatureStats {
|
|
||||||
value := v.Load()
|
|
||||||
if k == templates.Unsigned && value > 0 {
|
|
||||||
// adjust skipped unsigned templates via code or -dut flag
|
|
||||||
value = value - uint64(stats.GetValue(templates.SkippedUnsignedStats))
|
|
||||||
value = value - uint64(stats.GetValue(templates.CodeFlagWarningStats))
|
|
||||||
}
|
}
|
||||||
if value > 0 {
|
if len(store.Workflows()) > 0 {
|
||||||
if k != templates.Unsigned {
|
gologger.Info().Msgf("Workflows loaded for current scan: %d", len(store.Workflows()))
|
||||||
gologger.Info().Msgf("Executing %d signed templates from %s", value, k)
|
}
|
||||||
} else if !r.options.Silent && !config.DefaultConfig.HideTemplateSigWarning {
|
for k, v := range templates.SignatureStats {
|
||||||
gologger.Print().Msgf("[%v] Loaded %d unsigned templates for scan. Use with caution.", aurora.BrightYellow("WRN"), value)
|
value := v.Load()
|
||||||
|
if k == templates.Unsigned && value > 0 {
|
||||||
|
// adjust skipped unsigned templates via code or -dut flag
|
||||||
|
value = value - uint64(stats.GetValue(templates.SkippedUnsignedStats))
|
||||||
|
value = value - uint64(stats.GetValue(templates.ExcludedCodeTmplStats))
|
||||||
|
}
|
||||||
|
if value > 0 {
|
||||||
|
if k == templates.Unsigned && !r.options.Silent && !config.DefaultConfig.HideTemplateSigWarning {
|
||||||
|
gologger.Print().Msgf("[%v] Loading %d unsigned templates for scan. Use with caution.", aurora.BrightYellow("WRN"), value)
|
||||||
|
} else {
|
||||||
|
gologger.Info().Msgf("Executing %d signed templates from %s", value, k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,10 +376,18 @@ func LoadSecretsFromFile(files []string, prefetch bool) NucleiSDKOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableFuzzTemplates allows enabling template fuzzing
|
// DASTMode only run DAST templates
|
||||||
func EnableFuzzTemplates() NucleiSDKOptions {
|
func DASTMode() NucleiSDKOptions {
|
||||||
return func(e *NucleiEngine) error {
|
return func(e *NucleiEngine) error {
|
||||||
e.opts.FuzzTemplates = true
|
e.opts.DAST = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedTemplatesOnly only run signed templates and disabled loading all unsigned templates
|
||||||
|
func SignedTemplatesOnly() NucleiSDKOptions {
|
||||||
|
return func(e *NucleiEngine) error {
|
||||||
|
e.opts.DisableUnsignedTemplates = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,6 @@ type Config struct {
|
||||||
|
|
||||||
Catalog catalog.Catalog
|
Catalog catalog.Catalog
|
||||||
ExecutorOptions protocols.ExecutorOptions
|
ExecutorOptions protocols.ExecutorOptions
|
||||||
|
|
||||||
OnlyLoadHTTPFuzzing bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store is a storage for loaded nuclei templates
|
// Store is a storage for loaded nuclei templates
|
||||||
|
@ -405,33 +403,42 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||||
stats.Increment(templates.SkippedUnsignedStats)
|
stats.Increment(templates.SkippedUnsignedStats)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
// if template has request signature like aws then only signed and verified templates are allowed
|
||||||
|
if parsed.UsesRequestSignature() && !parsed.Verified {
|
||||||
|
stats.Increment(templates.SkippedRequestSignatureStats)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// DAST only templates
|
||||||
|
if store.config.ExecutorOptions.Options.DAST {
|
||||||
|
// check if the template is a DAST template
|
||||||
|
if parsed.IsFuzzing() {
|
||||||
|
loadedTemplates = append(loadedTemplates, parsed)
|
||||||
|
}
|
||||||
|
} else if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||||
// donot include headless template in final list if headless flag is not set
|
// donot include headless template in final list if headless flag is not set
|
||||||
stats.Increment(templates.HeadlessFlagWarningStats)
|
stats.Increment(templates.ExcludedHeadlessTmplStats)
|
||||||
if config.DefaultConfig.LogAllEvents {
|
if config.DefaultConfig.LogAllEvents {
|
||||||
gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||||
}
|
}
|
||||||
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||||
// donot include 'Code' protocol custom template in final list if code flag is not set
|
// donot include 'Code' protocol custom template in final list if code flag is not set
|
||||||
stats.Increment(templates.CodeFlagWarningStats)
|
stats.Increment(templates.ExcludedCodeTmplStats)
|
||||||
if config.DefaultConfig.LogAllEvents {
|
if config.DefaultConfig.LogAllEvents {
|
||||||
gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||||
}
|
}
|
||||||
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
||||||
// donot include unverified 'Code' protocol custom template in final list
|
// donot include unverified 'Code' protocol custom template in final list
|
||||||
stats.Increment(templates.UnsignedCodeWarning)
|
stats.Increment(templates.SkippedCodeTmplTamperedStats)
|
||||||
// these will be skipped so increment skip counter
|
// these will be skipped so increment skip counter
|
||||||
stats.Increment(templates.SkippedUnsignedStats)
|
stats.Increment(templates.SkippedUnsignedStats)
|
||||||
if config.DefaultConfig.LogAllEvents {
|
if config.DefaultConfig.LogAllEvents {
|
||||||
gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
|
gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||||
}
|
}
|
||||||
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.FuzzTemplates {
|
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||||
stats.Increment(templates.FuzzFlagWarningStats)
|
stats.Increment(templates.ExludedDastTmplStats)
|
||||||
if config.DefaultConfig.LogAllEvents {
|
if config.DefaultConfig.LogAllEvents {
|
||||||
gologger.Print().Msgf("[%v] Fuzz flag is required for fuzzing template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
gologger.Print().Msgf("[%v] -dast flag is required for DAST template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||||
}
|
}
|
||||||
} else if store.config.OnlyLoadHTTPFuzzing && !parsed.IsFuzzing() {
|
|
||||||
gologger.Warning().Msgf("Non-Fuzzing template '%s' can only be run on list input mode targets\n", templatePath)
|
|
||||||
} else {
|
} else {
|
||||||
loadedTemplates = append(loadedTemplates, parsed)
|
loadedTemplates = append(loadedTemplates, parsed)
|
||||||
}
|
}
|
||||||
|
@ -439,7 +446,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
||||||
stats.Increment(templates.TemplatesExecutedStats)
|
stats.Increment(templates.TemplatesExcludedStats)
|
||||||
if cfg.DefaultConfig.LogAllEvents {
|
if cfg.DefaultConfig.LogAllEvents {
|
||||||
gologger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
|
gologger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,3 +141,10 @@ func (b *Body) Rebuild() (*retryablehttp.Request, error) {
|
||||||
cloned.Header.Set("Content-Length", strconv.Itoa(len(encoded)))
|
cloned.Header.Set("Content-Length", strconv.Itoa(len(encoded)))
|
||||||
return cloned, nil
|
return cloned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Body) Clone() Component {
|
||||||
|
return &Body{
|
||||||
|
value: b.value.Clone(),
|
||||||
|
req: b.req.Clone(context.Background()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/projectdiscovery/retryablehttp-go"
|
"github.com/projectdiscovery/retryablehttp-go"
|
||||||
|
urlutil "github.com/projectdiscovery/utils/url"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ func TestBodyXMLComponent(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBodyFormComponent(t *testing.T) {
|
func TestBodyFormComponent(t *testing.T) {
|
||||||
formData := url.Values{}
|
formData := urlutil.NewOrderedParams()
|
||||||
formData.Set("key1", "value1")
|
formData.Set("key1", "value1")
|
||||||
formData.Set("key2", "value2")
|
formData.Set("key2", "value2")
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ type Component interface {
|
||||||
// Rebuild returns a new request with the
|
// Rebuild returns a new request with the
|
||||||
// component rebuilt
|
// component rebuilt
|
||||||
Rebuild() (*retryablehttp.Request, error)
|
Rebuild() (*retryablehttp.Request, error)
|
||||||
|
// Clones current state of this component
|
||||||
|
Clone() Component
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -99,6 +99,14 @@ func (c *Cookie) Rebuild() (*retryablehttp.Request, error) {
|
||||||
return cloned, nil
|
return cloned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone clones current state of this component
|
||||||
|
func (c *Cookie) Clone() Component {
|
||||||
|
return &Cookie{
|
||||||
|
value: c.value.Clone(),
|
||||||
|
req: c.req.Clone(context.Background()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A list of cookies that are essential to the request and
|
// A list of cookies that are essential to the request and
|
||||||
// must not be fuzzed.
|
// must not be fuzzed.
|
||||||
var defaultIgnoredCookieKeys = map[string]struct{}{
|
var defaultIgnoredCookieKeys = map[string]struct{}{
|
||||||
|
|
|
@ -102,6 +102,14 @@ func (q *Header) Rebuild() (*retryablehttp.Request, error) {
|
||||||
return cloned, nil
|
return cloned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clones current state of this component
|
||||||
|
func (q *Header) Clone() Component {
|
||||||
|
return &Header{
|
||||||
|
value: q.value.Clone(),
|
||||||
|
req: q.req.Clone(context.Background()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A list of headers that are essential to the request and
|
// A list of headers that are essential to the request and
|
||||||
// must not be fuzzed.
|
// must not be fuzzed.
|
||||||
var defaultIgnoredHeaderKeys = map[string]struct{}{
|
var defaultIgnoredHeaderKeys = map[string]struct{}{
|
||||||
|
|
|
@ -83,3 +83,11 @@ func (q *Path) Rebuild() (*retryablehttp.Request, error) {
|
||||||
}
|
}
|
||||||
return cloned, nil
|
return cloned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clones current state to a new component
|
||||||
|
func (q *Path) Clone() Component {
|
||||||
|
return &Path{
|
||||||
|
value: q.value.Clone(),
|
||||||
|
req: q.req.Clone(context.Background()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -92,3 +92,11 @@ func (q *Query) Rebuild() (*retryablehttp.Request, error) {
|
||||||
cloned.Update()
|
cloned.Update()
|
||||||
return cloned, nil
|
return cloned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clones current state to a new component
|
||||||
|
func (q *Query) Clone() Component {
|
||||||
|
return &Query{
|
||||||
|
value: q.value.Clone(),
|
||||||
|
req: q.req.Clone(context.Background()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,15 @@ func NewValue(data string) *Value {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clones current state of this value
|
||||||
|
func (v *Value) Clone() *Value {
|
||||||
|
return &Value{
|
||||||
|
data: v.data,
|
||||||
|
parsed: v.parsed.Clone(),
|
||||||
|
dataFormat: v.dataFormat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the string representation of the value
|
// String returns the string representation of the value
|
||||||
func (v *Value) String() string {
|
func (v *Value) String() string {
|
||||||
return v.data
|
return v.data
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package dataformat
|
package dataformat
|
||||||
|
|
||||||
import mapsutil "github.com/projectdiscovery/utils/maps"
|
import (
|
||||||
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
// KV is a key-value struct
|
// KV is a key-value struct
|
||||||
// that is implemented or used by fuzzing package
|
// that is implemented or used by fuzzing package
|
||||||
|
@ -14,6 +17,18 @@ type KV struct {
|
||||||
OrderedMap *mapsutil.OrderedMap[string, any]
|
OrderedMap *mapsutil.OrderedMap[string, any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clones the current state of the KV struct
|
||||||
|
func (kv *KV) Clone() KV {
|
||||||
|
newKV := KV{}
|
||||||
|
if kv.OrderedMap == nil {
|
||||||
|
newKV.Map = maps.Clone(kv.Map)
|
||||||
|
return newKV
|
||||||
|
}
|
||||||
|
clonedOrderedMap := kv.OrderedMap.Clone()
|
||||||
|
newKV.OrderedMap = &clonedOrderedMap
|
||||||
|
return newKV
|
||||||
|
}
|
||||||
|
|
||||||
// IsNIL returns true if the KV struct is nil
|
// IsNIL returns true if the KV struct is nil
|
||||||
func (kv *KV) IsNIL() bool {
|
func (kv *KV) IsNIL() bool {
|
||||||
return kv.Map == nil && kv.OrderedMap == nil
|
return kv.Map == nil && kv.OrderedMap == nil
|
||||||
|
|
|
@ -34,12 +34,13 @@ func (rule *Rule) checkRuleApplicableOnComponent(component component.Component)
|
||||||
|
|
||||||
// executePartComponent executes this rule on a given component and payload
|
// executePartComponent executes this rule on a given component and payload
|
||||||
func (rule *Rule) executePartComponent(input *ExecuteRuleInput, payload ValueOrKeyValue, ruleComponent component.Component) error {
|
func (rule *Rule) executePartComponent(input *ExecuteRuleInput, payload ValueOrKeyValue, ruleComponent component.Component) error {
|
||||||
|
// Note: component needs to be cloned because they contain values copied by reference
|
||||||
if payload.IsKV() {
|
if payload.IsKV() {
|
||||||
// for kv fuzzing
|
// for kv fuzzing
|
||||||
return rule.executePartComponentOnKV(input, payload, ruleComponent)
|
return rule.executePartComponentOnKV(input, payload, ruleComponent.Clone())
|
||||||
} else {
|
} else {
|
||||||
// for value only fuzzing
|
// for value only fuzzing
|
||||||
return rule.executePartComponentOnValues(input, payload.Value, ruleComponent)
|
return rule.executePartComponentOnValues(input, payload.Value, ruleComponent.Clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,8 +59,12 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
|
||||||
|
|
||||||
for i, item := range output.ExtractedResults {
|
for i, item := range output.ExtractedResults {
|
||||||
// trim trailing space
|
// trim trailing space
|
||||||
|
// quote non-ascii and non printable characters and then
|
||||||
|
// unquote quotes (`"`) for readability
|
||||||
item = strings.TrimSpace(item)
|
item = strings.TrimSpace(item)
|
||||||
item = strings.ReplaceAll(item, "\n", "\\n") // only replace newlines
|
item = strconv.QuoteToASCII(item)
|
||||||
|
item = strings.ReplaceAll(item, `\"`, `"`)
|
||||||
|
|
||||||
builder.WriteString(w.aurora.BrightCyan(item).String())
|
builder.WriteString(w.aurora.BrightCyan(item).String())
|
||||||
|
|
||||||
if i != len(output.ExtractedResults)-1 {
|
if i != len(output.ExtractedResults)-1 {
|
||||||
|
|
|
@ -135,6 +135,12 @@ func (variables *Variable) checkForLazyEval() bool {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// this is a hotfix and not the best way to do it
|
||||||
|
// will be refactored once we move scan state to scanContext (see: https://github.com/projectdiscovery/nuclei/issues/4631)
|
||||||
|
if strings.Contains(types.ToString(value), "interactsh-url") {
|
||||||
|
variables.LazyEval = true
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return variables.LazyEval
|
return variables.LazyEval
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,7 +283,6 @@ func (r *Request) ApplyAuthStrategy(strategy authx.AuthStrategy) {
|
||||||
gologger.Error().Msgf("auth strategy failed to parse url: %s got %v", r.FullURL, err)
|
gologger.Error().Msgf("auth strategy failed to parse url: %s got %v", r.FullURL, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = parsed
|
|
||||||
for _, p := range s.Data.Params {
|
for _, p := range s.Data.Params {
|
||||||
parsed.Params.Add(p.Key, p.Value)
|
parsed.Params.Add(p.Key, p.Value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package http
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -23,13 +24,14 @@ import (
|
||||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||||
"github.com/projectdiscovery/retryablehttp-go"
|
"github.com/projectdiscovery/retryablehttp-go"
|
||||||
|
urlutil "github.com/projectdiscovery/utils/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// executeFuzzingRule executes fuzzing request for a URL
|
// executeFuzzingRule executes fuzzing request for a URL
|
||||||
// TODO:
|
// TODO:
|
||||||
// 1. use SPMHandler and rewrite stop at first match logic here
|
// 1. use SPMHandler and rewrite stop at first match logic here
|
||||||
// 2. use scanContext instead of contextargs.Context
|
// 2. use scanContext instead of contextargs.Context
|
||||||
func (request *Request) executeFuzzingRule(input *contextargs.Context, _ output.InternalEvent, callback protocols.OutputEventCallback) error {
|
func (request *Request) executeFuzzingRule(input *contextargs.Context, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||||
// methdology:
|
// methdology:
|
||||||
// to check applicablity of rule, we first try to execute it with one value
|
// to check applicablity of rule, we first try to execute it with one value
|
||||||
// if it is applicable, we execute all requests
|
// if it is applicable, we execute all requests
|
||||||
|
@ -46,29 +48,20 @@ func (request *Request) executeFuzzingRule(input *contextargs.Context, _ output.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate through all requests for template and queue them for fuzzing
|
if input.MetaInput.Input == "" && input.MetaInput.ReqResp == nil {
|
||||||
generator := request.newGenerator(true)
|
return errors.New("empty input provided for fuzzing")
|
||||||
|
|
||||||
// this will generate next value along with request it is meant to be used with
|
|
||||||
currRequest, payloads, result := generator.nextValue()
|
|
||||||
if !result && input.MetaInput.ReqResp == nil {
|
|
||||||
// this case is only true if input is not a full http request
|
|
||||||
return fmt.Errorf("no values to generate requests")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it is a full http request obtained from target file
|
// ==== fuzzing when full HTTP request is provided =====
|
||||||
|
|
||||||
if input.MetaInput.ReqResp != nil {
|
if input.MetaInput.ReqResp != nil {
|
||||||
// Note: in case of full http request, we only need to build it once
|
baseRequest, err := input.MetaInput.ReqResp.BuildRequest()
|
||||||
// and then reuse it for all requests and completely abandon the request
|
|
||||||
// returned by generator
|
|
||||||
_ = currRequest
|
|
||||||
generated, err := input.MetaInput.ReqResp.BuildRequest()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "fuzz: could not build request obtained from target file")
|
return errors.Wrap(err, "fuzz: could not build request obtained from target file")
|
||||||
}
|
}
|
||||||
input.MetaInput.Input = generated.URL.String()
|
input.MetaInput.Input = baseRequest.URL.String()
|
||||||
// execute with one value first to checks its applicability
|
// execute with one value first to checks its applicability
|
||||||
err = request.executePayloadUsingRules(input, payloads, generated, callback)
|
err = request.executeAllFuzzingRules(input, previous, baseRequest, callback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// in case of any error, return it
|
// in case of any error, return it
|
||||||
if fuzz.IsErrRuleNotApplicable(err) {
|
if fuzz.IsErrRuleNotApplicable(err) {
|
||||||
|
@ -79,36 +72,25 @@ func (request *Request) executeFuzzingRule(input *contextargs.Context, _ output.
|
||||||
if errors.Is(err, errStopExecution) {
|
if errors.Is(err, errStopExecution) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gologger.Verbose().Msgf("[%s] fuzz: inital payload request execution failed : %s\n", request.options.TemplateID, err)
|
gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err)
|
||||||
}
|
|
||||||
|
|
||||||
// if it is applicable, execute all requests
|
|
||||||
for {
|
|
||||||
_, payloads, result := generator.nextValue()
|
|
||||||
if !result {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = request.executePayloadUsingRules(input, payloads, generated, callback)
|
|
||||||
if err != nil {
|
|
||||||
// continue to next request since this is payload specific
|
|
||||||
gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== fuzzing when only URL is provided =====
|
// ==== fuzzing when only URL is provided =====
|
||||||
|
|
||||||
generated, err := generator.Make(context.Background(), input, currRequest, payloads, nil)
|
// we need to use this url instead of input
|
||||||
|
inputx := input.Clone()
|
||||||
|
parsed, err := urlutil.ParseAbsoluteURL(input.MetaInput.Input, true)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "fuzz: could not parse input url")
|
||||||
|
}
|
||||||
|
baseRequest, err := retryablehttp.NewRequestFromURL(http.MethodGet, parsed, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "fuzz: could not build request from url")
|
return errors.Wrap(err, "fuzz: could not build request from url")
|
||||||
}
|
}
|
||||||
// we need to use this url instead of input
|
|
||||||
inputx := input.Clone()
|
|
||||||
inputx.MetaInput.Input = generated.request.URL.String()
|
|
||||||
// execute with one value first to checks its applicability
|
// execute with one value first to checks its applicability
|
||||||
err = request.executePayloadUsingRules(inputx, generated.dynamicValues, generated.request, callback)
|
err = request.executeAllFuzzingRules(inputx, previous, baseRequest, callback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// in case of any error, return it
|
// in case of any error, return it
|
||||||
if fuzz.IsErrRuleNotApplicable(err) {
|
if fuzz.IsErrRuleNotApplicable(err) {
|
||||||
|
@ -119,34 +101,13 @@ func (request *Request) executeFuzzingRule(input *contextargs.Context, _ output.
|
||||||
if errors.Is(err, errStopExecution) {
|
if errors.Is(err, errStopExecution) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gologger.Verbose().Msgf("[%s] fuzz: inital payload request execution failed : %s\n", request.options.TemplateID, err)
|
gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err)
|
||||||
}
|
|
||||||
|
|
||||||
// continue to next request since this is payload specific
|
|
||||||
for {
|
|
||||||
currRequest, payloads, result = generator.nextValue()
|
|
||||||
if !result {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
generated, err := generator.Make(context.Background(), input, currRequest, payloads, nil)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "fuzz: could not build request from url")
|
|
||||||
}
|
|
||||||
// we need to use this url instead of input
|
|
||||||
inputx := input.Clone()
|
|
||||||
inputx.MetaInput.Input = generated.request.URL.String()
|
|
||||||
// execute with one value first to checks its applicability
|
|
||||||
err = request.executePayloadUsingRules(inputx, generated.dynamicValues, generated.request, callback)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Verbose().Msgf("[%s] fuzz: payload request execution failed : %s\n", request.options.TemplateID, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// executePayloadUsingRules executes a payload using rules with given payload i.e values
|
// executeAllFuzzingRules executes all fuzzing rules defined in template for a given base request
|
||||||
func (request *Request) executePayloadUsingRules(input *contextargs.Context, values map[string]interface{}, baseRequest *retryablehttp.Request, callback protocols.OutputEventCallback) error {
|
func (request *Request) executeAllFuzzingRules(input *contextargs.Context, values map[string]interface{}, baseRequest *retryablehttp.Request, callback protocols.OutputEventCallback) error {
|
||||||
applicable := false
|
applicable := false
|
||||||
for _, rule := range request.Fuzzing {
|
for _, rule := range request.Fuzzing {
|
||||||
err := rule.Execute(&fuzz.ExecuteRuleInput{
|
err := rule.Execute(&fuzz.ExecuteRuleInput{
|
||||||
|
@ -156,7 +117,7 @@ func (request *Request) executePayloadUsingRules(input *contextargs.Context, val
|
||||||
return request.executeGeneratedFuzzingRequest(gr, input, callback)
|
return request.executeGeneratedFuzzingRequest(gr, input, callback)
|
||||||
},
|
},
|
||||||
Values: values,
|
Values: values,
|
||||||
BaseRequest: baseRequest,
|
BaseRequest: baseRequest.Clone(context.TODO()),
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
applicable = true
|
applicable = true
|
||||||
|
@ -295,6 +256,9 @@ func (request *Request) filterDataMap(input *contextargs.Context) map[string]int
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
m["header"] = sb.String()
|
m["header"] = sb.String()
|
||||||
|
} else {
|
||||||
|
// add default method value
|
||||||
|
m["method"] = http.MethodGet
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump if svd is enabled
|
// dump if svd is enabled
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package templates
|
package templates
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SyntaxWarningStats = "syntax-warnings"
|
SyntaxWarningStats = "syntax-warnings"
|
||||||
SyntaxErrorStats = "syntax-errors"
|
SyntaxErrorStats = "syntax-errors"
|
||||||
RuntimeWarningsStats = "runtime-warnings"
|
RuntimeWarningsStats = "runtime-warnings"
|
||||||
UnsignedCodeWarning = "unsigned-warnings"
|
SkippedCodeTmplTamperedStats = "unsigned-warnings"
|
||||||
HeadlessFlagWarningStats = "headless-flag-missing-warnings"
|
ExcludedHeadlessTmplStats = "headless-flag-missing-warnings"
|
||||||
TemplatesExecutedStats = "templates-executed"
|
TemplatesExcludedStats = "templates-executed"
|
||||||
CodeFlagWarningStats = "code-flag-missing-warnings"
|
ExcludedCodeTmplStats = "code-flag-missing-warnings"
|
||||||
FuzzFlagWarningStats = "fuzz-flag-missing-warnings"
|
ExludedDastTmplStats = "fuzz-flag-missing-warnings"
|
||||||
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
||||||
|
SkippedRequestSignatureStats = "skipped-request-signature-stats"
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,10 +6,11 @@ func init() {
|
||||||
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
stats.NewEntry(SyntaxWarningStats, "Found %d templates with syntax warning (use -validate flag for further examination)")
|
||||||
stats.NewEntry(SyntaxErrorStats, "Found %d templates with syntax error (use -validate flag for further examination)")
|
stats.NewEntry(SyntaxErrorStats, "Found %d templates with syntax error (use -validate flag for further examination)")
|
||||||
stats.NewEntry(RuntimeWarningsStats, "Found %d templates with runtime error (use -validate flag for further examination)")
|
stats.NewEntry(RuntimeWarningsStats, "Found %d templates with runtime error (use -validate flag for further examination)")
|
||||||
stats.NewEntry(UnsignedCodeWarning, "Found %d unsigned or tampered code template (carefully examine before using it & use -sign flag to sign them)")
|
stats.NewEntry(SkippedCodeTmplTamperedStats, "Found %d unsigned or tampered code template (carefully examine before using it & use -sign flag to sign them)")
|
||||||
stats.NewEntry(HeadlessFlagWarningStats, "Excluded %d headless template[s] (disabled as default), use -headless option to run headless templates.")
|
stats.NewEntry(ExcludedHeadlessTmplStats, "Excluded %d headless template[s] (disabled as default), use -headless option to run headless templates.")
|
||||||
stats.NewEntry(CodeFlagWarningStats, "Excluded %d code template[s] (disabled as default), use -code option to run code templates.")
|
stats.NewEntry(ExcludedCodeTmplStats, "Excluded %d code template[s] (disabled as default), use -code option to run code templates.")
|
||||||
stats.NewEntry(TemplatesExecutedStats, "Excluded %d template[s] with known weak matchers / tags excluded from default run using .nuclei-ignore")
|
stats.NewEntry(TemplatesExcludedStats, "Excluded %d template[s] with known weak matchers / tags excluded from default run using .nuclei-ignore")
|
||||||
stats.NewEntry(FuzzFlagWarningStats, "Excluded %d fuzz template[s] (disabled as default), use -fuzz option to run fuzz templates.")
|
stats.NewEntry(ExludedDastTmplStats, "Excluded %d dast template[s] (disabled as default), use -dast option to run dast templates.")
|
||||||
stats.NewEntry(SkippedUnsignedStats, "Skipping %d unsigned template[s]")
|
stats.NewEntry(SkippedUnsignedStats, "Skipping %d unsigned template[s]")
|
||||||
|
stats.NewEntry(SkippedRequestSignatureStats, "Skipping %d templates, HTTP Request signatures can only be used in Signed & Verified templates.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ type Template struct {
|
||||||
|
|
||||||
// description: |
|
// description: |
|
||||||
// Signature is the request signature method
|
// Signature is the request signature method
|
||||||
|
// WARNING: 'signature' will be deprecated and will be removed in a future release. Prefer using 'code' protocol for writing cloud checks
|
||||||
// values:
|
// values:
|
||||||
// - "AWS"
|
// - "AWS"
|
||||||
Signature http.SignatureTypeHolder `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS,deprecated=true"`
|
Signature http.SignatureTypeHolder `yaml:"signature,omitempty" json:"signature,omitempty" jsonschema:"title=signature is the http request signature method,description=Signature is the HTTP Request signature Method,enum=AWS,deprecated=true"`
|
||||||
|
@ -214,6 +215,11 @@ func (template *Template) IsFuzzing() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UsesRequestSignature returns true if the template uses a request signature like AWS
|
||||||
|
func (template *Template) UsesRequestSignature() bool {
|
||||||
|
return template.Signature.Value.String() != ""
|
||||||
|
}
|
||||||
|
|
||||||
// HasCodeProtocol returns true if the template has a code protocol section
|
// HasCodeProtocol returns true if the template has a code protocol section
|
||||||
func (template *Template) HasCodeProtocol() bool {
|
func (template *Template) HasCodeProtocol() bool {
|
||||||
return len(template.RequestsCode) > 0
|
return len(template.RequestsCode) > 0
|
||||||
|
|
|
@ -86,6 +86,10 @@ func parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, preprocessor Pr
|
||||||
stats.Increment(SkippedUnsignedStats)
|
stats.Increment(SkippedUnsignedStats)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if template.UsesRequestSignature() && !template.Verified {
|
||||||
|
stats.Increment(SkippedRequestSignatureStats)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if len(template.RequestsCode) > 0 {
|
if len(template.RequestsCode) > 0 {
|
||||||
if !options.Options.EnableCodeTemplates {
|
if !options.Options.EnableCodeTemplates {
|
||||||
|
|
|
@ -372,9 +372,6 @@ type Options struct {
|
||||||
ScanID string
|
ScanID string
|
||||||
// JsConcurrency is the number of concurrent js routines to run
|
// JsConcurrency is the number of concurrent js routines to run
|
||||||
JsConcurrency int
|
JsConcurrency int
|
||||||
// Fuzz enabled execution of fuzzing templates
|
|
||||||
// Note: when Fuzz is enabled other templates will not be executed
|
|
||||||
FuzzTemplates bool
|
|
||||||
// SecretsFile is file containing secrets for nuclei
|
// SecretsFile is file containing secrets for nuclei
|
||||||
SecretsFile goflags.StringSlice
|
SecretsFile goflags.StringSlice
|
||||||
// PreFetchSecrets pre-fetches the secrets from the auth provider
|
// PreFetchSecrets pre-fetches the secrets from the auth provider
|
||||||
|
@ -385,6 +382,8 @@ type Options struct {
|
||||||
SkipFormatValidation bool
|
SkipFormatValidation bool
|
||||||
// PayloadConcurrency is the number of concurrent payloads to run per template
|
// PayloadConcurrency is the number of concurrent payloads to run per template
|
||||||
PayloadConcurrency int
|
PayloadConcurrency int
|
||||||
|
// Dast only runs DAST templates
|
||||||
|
DAST bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldLoadResume resume file
|
// ShouldLoadResume resume file
|
||||||
|
|
Loading…
Reference in New Issue