Skip to content

Commit

Permalink
feat: added configurable fuzzing aggression level for payloads
Browse files Browse the repository at this point in the history
  • Loading branch information
Ice3man543 committed May 7, 2024
1 parent 7a62bba commit 12d116b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
1 change: 1 addition & 0 deletions cmd/nuclei/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.BoolVar(&options.DAST, "dast", false, "enable / run dast (fuzz) nuclei templates"),
flagSet.BoolVarP(&options.DisplayFuzzPoints, "display-fuzz-points", "dfp", false, "display fuzz points in the output for debugging"),
flagSet.IntVar(&options.FuzzParamFrequency, "fuzz-param-frequency", 10, "frequency of uninteresting parameters for fuzzing before skipping"),
flagSet.StringVarP(&options.FuzzAggressionLevel, "fuzz-aggression", "fa", "low", "fuzzing aggression level controls payload count for fuzz (low, medium, high)"),
)

flagSet.CreateGroup("uncover", "Uncover",
Expand Down
69 changes: 67 additions & 2 deletions pkg/protocols/common/generators/generators.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st

// Resolve payload paths if they are files.
payloadsFinal := make(map[string]interface{})
for name, payload := range payloads {
payloadsFinal[name] = payload
for payloadName, v := range payloads {
switch value := v.(type) {
case map[interface{}]interface{}:
values, err := parsePayloadsWithAggression(payloadName, value, opts.FuzzAggressionLevel)
if err != nil {
return nil, errors.Wrap(err, "could not parse payloads with aggression")
}
for k, v := range values {
payloadsFinal[k] = v
}
default:
payloadsFinal[payloadName] = v
}
}

generator := &PayloadGenerator{catalog: catalog, options: opts}
Expand Down Expand Up @@ -57,6 +68,60 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
return generator, nil
}

type aggressionLevelToPayloads struct {
Low []interface{}
Medium []interface{}
High []interface{}
}

// parsePayloadsWithAggression parses the payloads with the aggression level
//
// Three agression are supported -
// - low
// - medium
// - high
//
// low is the default level. If medium is specified, all templates from
// low and medium are executed. Similarly with high, including all templates
// from low, medium, high.
func parsePayloadsWithAggression(name string, v map[interface{}]interface{}, agression string) (map[string]interface{}, error) {
payloadsLevels := &aggressionLevelToPayloads{}

for k, v := range v {
if _, ok := v.([]interface{}); !ok {
return nil, errors.Errorf("only lists are supported for aggression levels payloads")
}
var ok bool
switch k {
case "low":
payloadsLevels.Low, ok = v.([]interface{})
case "medium":
payloadsLevels.Medium, ok = v.([]interface{})
case "high":
payloadsLevels.High, ok = v.([]interface{})
default:
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
}
if !ok {
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
}
}

payloads := make(map[string]interface{})
switch agression {
case "low":
payloads[name] = payloadsLevels.Low
case "medium":
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
case "high":
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
payloads[name] = append(payloads[name].([]interface{}), payloadsLevels.High...)
default:
return nil, errors.Errorf("invalid aggression level %s specified for %s", agression, name)
}
return payloads, nil
}

// Iterator is a single instance of an iterator for a generator structure
type Iterator struct {
Type AttackType
Expand Down
48 changes: 48 additions & 0 deletions pkg/protocols/common/generators/generators_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package generators

import (
"strings"
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"

"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
Expand Down Expand Up @@ -90,3 +92,49 @@ func getOptions(allowLocalFileAccess bool) *types.Options {
opts.AllowLocalFileAccess = allowLocalFileAccess
return opts
}

func TestParsePayloadsWithAggression(t *testing.T) {
testPayload := `linux_path:
low:
- /etc/passwd
medium:
- ../etc/passwd
- ../../etc/passwd
high:
- ../../../etc/passwd
- ../../../../etc/passwd
- ../../../../../etc/passwd`

var payloads map[string]interface{}
err := yaml.NewDecoder(strings.NewReader(testPayload)).Decode(&payloads)
require.Nil(t, err, "could not unmarshal yaml")

aggressionsToValues := map[string][]string{
"low": {
"/etc/passwd",
},
"medium": {
"/etc/passwd",
"../etc/passwd",
"../../etc/passwd",
},
"high": {
"/etc/passwd",
"../etc/passwd",
"../../etc/passwd",
"../../../etc/passwd",
"../../../../etc/passwd",
"../../../../../etc/passwd",
},
}

for k, v := range payloads {
for aggression, values := range aggressionsToValues {
parsed, err := parsePayloadsWithAggression(k, v.(map[interface{}]interface{}), aggression)
require.Nil(t, err, "could not parse payloads with aggression")

gotValues := parsed[k].([]interface{})
require.Equal(t, len(values), len(gotValues), "could not get correct number of values")
}
}
}
2 changes: 2 additions & 0 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ type Options struct {
TlsImpersonate bool
// DisplayFuzzPoints enables display of fuzz points for fuzzing
DisplayFuzzPoints bool
// FuzzAggressionLevel is the level of fuzzing aggression (low, medium, high.)
FuzzAggressionLevel string
// FuzzParamFrequency is the frequency of fuzzing parameters
FuzzParamFrequency int
// CodeTemplateSignaturePublicKey is the custom public key used to verify the template signature (algorithm is automatically inferred from the length)
Expand Down

0 comments on commit 12d116b

Please sign in to comment.