github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/fuzz/execute.go (about)

     1  package fuzz
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
     9  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
    10  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
    11  	"github.com/projectdiscovery/retryablehttp-go"
    12  	urlutil "github.com/projectdiscovery/utils/url"
    13  )
    14  
    15  // ExecuteRuleInput is the input for rule Execute function
    16  type ExecuteRuleInput struct {
    17  	// Input is the context args input
    18  	Input *contextargs.Context
    19  	// Callback is the callback for generated rule requests
    20  	Callback func(GeneratedRequest) bool
    21  	// InteractURLs contains interact urls for execute call
    22  	InteractURLs []string
    23  	// Values contains dynamic values for the rule
    24  	Values map[string]interface{}
    25  	// BaseRequest is the base http request for fuzzing rule
    26  	BaseRequest *retryablehttp.Request
    27  }
    28  
    29  // GeneratedRequest is a single generated request for rule
    30  type GeneratedRequest struct {
    31  	// Request is the http request for rule
    32  	Request *retryablehttp.Request
    33  	// InteractURLs is the list of interactsh urls
    34  	InteractURLs []string
    35  	// DynamicValues contains dynamic values map
    36  	DynamicValues map[string]interface{}
    37  }
    38  
    39  // Execute executes a fuzzing rule accepting a callback on which
    40  // generated requests are returned.
    41  //
    42  // Input is not thread safe and should not be shared between concurrent
    43  // goroutines.
    44  func (rule *Rule) Execute(input *ExecuteRuleInput) error {
    45  	if !rule.isExecutable(input.Input) {
    46  		return nil
    47  	}
    48  	baseValues := input.Values
    49  	if rule.generator == nil {
    50  		evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(baseValues, rule.options.Interactsh)
    51  		input.Values = generators.MergeMaps(evaluatedValues, baseValues, rule.options.Constants)
    52  		input.InteractURLs = interactURLs
    53  		err := rule.executeRuleValues(input)
    54  		return err
    55  	}
    56  	iterator := rule.generator.NewIterator()
    57  	for {
    58  		values, next := iterator.Value()
    59  		if !next {
    60  			return nil
    61  		}
    62  		evaluatedValues, interactURLs := rule.options.Variables.EvaluateWithInteractsh(generators.MergeMaps(values, baseValues), rule.options.Interactsh)
    63  		input.InteractURLs = interactURLs
    64  		input.Values = generators.MergeMaps(values, evaluatedValues, baseValues, rule.options.Constants)
    65  
    66  		if err := rule.executeRuleValues(input); err != nil {
    67  			return err
    68  		}
    69  	}
    70  }
    71  
    72  // isExecutable returns true if the rule can be executed based on provided input
    73  func (rule *Rule) isExecutable(input *contextargs.Context) bool {
    74  	parsed, err := urlutil.Parse(input.MetaInput.Input)
    75  	if err != nil {
    76  		return false
    77  	}
    78  	if !parsed.Query().IsEmpty() && rule.partType == queryPartType {
    79  		return true
    80  	}
    81  	return false
    82  }
    83  
    84  // executeRuleValues executes a rule with a set of values
    85  func (rule *Rule) executeRuleValues(input *ExecuteRuleInput) error {
    86  	for _, payload := range rule.Fuzz {
    87  		if err := rule.executePartRule(input, payload); err != nil {
    88  			return err
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  // Compile compiles a fuzzing rule and initializes it for operation
    95  func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *protocols.ExecutorOptions) error {
    96  	// If a payload generator is specified from base request, use it
    97  	// for payload values.
    98  	if generator != nil {
    99  		rule.generator = generator
   100  	}
   101  	rule.options = options
   102  
   103  	// Resolve the default enums
   104  	if rule.Mode != "" {
   105  		if valueType, ok := stringToModeType[rule.Mode]; !ok {
   106  			return errors.Errorf("invalid mode value specified: %s", rule.Mode)
   107  		} else {
   108  			rule.modeType = valueType
   109  		}
   110  	} else {
   111  		rule.modeType = multipleModeType
   112  	}
   113  	if rule.Part != "" {
   114  		if valueType, ok := stringToPartType[rule.Part]; !ok {
   115  			return errors.Errorf("invalid part value specified: %s", rule.Part)
   116  		} else {
   117  			rule.partType = valueType
   118  		}
   119  	} else {
   120  		rule.partType = queryPartType
   121  	}
   122  
   123  	if rule.Type != "" {
   124  		if valueType, ok := stringToRuleType[rule.Type]; !ok {
   125  			return errors.Errorf("invalid type value specified: %s", rule.Type)
   126  		} else {
   127  			rule.ruleType = valueType
   128  		}
   129  	} else {
   130  		rule.ruleType = replaceRuleType
   131  	}
   132  
   133  	// Initialize other required regexes and maps
   134  	if len(rule.Keys) > 0 {
   135  		rule.keysMap = make(map[string]struct{})
   136  	}
   137  	for _, key := range rule.Keys {
   138  		rule.keysMap[strings.ToLower(key)] = struct{}{}
   139  	}
   140  	for _, value := range rule.ValuesRegex {
   141  		compiled, err := regexp.Compile(value)
   142  		if err != nil {
   143  			return errors.Wrap(err, "could not compile value regex")
   144  		}
   145  		rule.valuesRegex = append(rule.valuesRegex, compiled)
   146  	}
   147  	for _, value := range rule.KeysRegex {
   148  		compiled, err := regexp.Compile(value)
   149  		if err != nil {
   150  			return errors.Wrap(err, "could not compile key regex")
   151  		}
   152  		rule.keysRegex = append(rule.keysRegex, compiled)
   153  	}
   154  	return nil
   155  }