github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/headless/headless.go (about)

     1  package headless
     2  
     3  import (
     4  	"github.com/corpix/uarand"
     5  	"github.com/pkg/errors"
     6  
     7  	useragent "github.com/projectdiscovery/nuclei/v2/pkg/model/types/userAgent"
     8  	"github.com/projectdiscovery/nuclei/v2/pkg/operators"
     9  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
    10  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/fuzz"
    11  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
    12  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
    13  	fileutil "github.com/projectdiscovery/utils/file"
    14  )
    15  
    16  // Request contains a Headless protocol request to be made from a template
    17  type Request struct {
    18  	// ID is the optional id of the request
    19  	ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the request,description=Optional ID of the headless request"`
    20  
    21  	// description: |
    22  	//   Attack is the type of payload combinations to perform.
    23  	//
    24  	//   Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
    25  	//   permutations and combinations for all payloads.
    26  	AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
    27  	// description: |
    28  	//   Payloads contains any payloads for the current request.
    29  	//
    30  	//   Payloads support both key-values combinations where a list
    31  	//   of payloads is provided, or optionally a single file can also
    32  	//   be provided as payload which will be read on run-time.
    33  	Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the headless request,description=Payloads contains any payloads for the current request"`
    34  
    35  	// description: |
    36  	//   Steps is the list of actions to run for headless request
    37  	Steps []*engine.Action `yaml:"steps,omitempty" json:"steps,omitempty" jsonschema:"title=list of actions for headless request,description=List of actions to run for headless request"`
    38  
    39  	// descriptions: |
    40  	// 	 User-Agent is the type of user-agent to use for the request.
    41  	UserAgent useragent.UserAgentHolder `yaml:"user_agent,omitempty" json:"user_agent,omitempty" jsonschema:"title=user agent for the headless request,description=User agent for the headless request"`
    42  
    43  	// description: |
    44  	// 	 If UserAgent is set to custom, customUserAgent is the custom user-agent to use for the request.
    45  	CustomUserAgent   string `yaml:"custom_user_agent,omitempty" json:"custom_user_agent,omitempty" jsonschema:"title=custom user agent for the headless request,description=Custom user agent for the headless request"`
    46  	compiledUserAgent string
    47  	// description: |
    48  	//   StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
    49  	StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
    50  
    51  	// Operators for the current request go here.
    52  	operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
    53  	CompiledOperators   *operators.Operators `yaml:"-" json:"-"`
    54  
    55  	// cache any variables that may be needed for operation.
    56  	options   *protocols.ExecutorOptions
    57  	generator *generators.PayloadGenerator
    58  
    59  	// Fuzzing describes schema to fuzz headless requests
    60  	Fuzzing []*fuzz.Rule `yaml:"fuzzing,omitempty" json:"fuzzing,omitempty" jsonschema:"title=fuzzin rules for http fuzzing,description=Fuzzing describes rule schema to fuzz headless requests"`
    61  
    62  	// description: |
    63  	//   CookieReuse is an optional setting that enables cookie reuse
    64  	CookieReuse bool `yaml:"cookie-reuse,omitempty" json:"cookie-reuse,omitempty" jsonschema:"title=optional cookie reuse enable,description=Optional setting that enables cookie reuse"`
    65  }
    66  
    67  // RequestPartDefinitions contains a mapping of request part definitions and their
    68  // description. Multiple definitions are separated by commas.
    69  // Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
    70  var RequestPartDefinitions = map[string]string{
    71  	"template-id":    "ID of the template executed",
    72  	"template-info":  "Info Block of the template executed",
    73  	"template-path":  "Path of the template executed",
    74  	"host":           "Host is the input to the template",
    75  	"matched":        "Matched is the input which was matched upon",
    76  	"type":           "Type is the type of request made",
    77  	"req":            "Headless request made from the client",
    78  	"resp,body,data": "Headless response received from client (default)",
    79  }
    80  
    81  // Step is a headless protocol request step.
    82  type Step struct {
    83  	// Action is the headless action to execute for the script
    84  	Action string `yaml:"action"`
    85  }
    86  
    87  // GetID returns the unique ID of the request if any.
    88  func (request *Request) GetID() string {
    89  	return request.ID
    90  }
    91  
    92  // Compile compiles the protocol request for further execution.
    93  func (request *Request) Compile(options *protocols.ExecutorOptions) error {
    94  	// TODO: logic similar to network + http => probably can be refactored
    95  	// Resolve payload paths from vars if they exists
    96  	for name, payload := range options.Options.Vars.AsMap() {
    97  		payloadStr, ok := payload.(string)
    98  		// check if inputs contains the payload
    99  		if ok && fileutil.FileExists(payloadStr) {
   100  			if request.Payloads == nil {
   101  				request.Payloads = make(map[string]interface{})
   102  			}
   103  			request.Payloads[name] = payloadStr
   104  		}
   105  	}
   106  
   107  	if len(request.Payloads) > 0 {
   108  		var err error
   109  		request.generator, err = generators.New(request.Payloads, request.AttackType.Value, options.TemplatePath, options.Options.AllowLocalFileAccess, options.Catalog, options.Options.AttackType)
   110  		if err != nil {
   111  			return errors.Wrap(err, "could not parse payloads")
   112  		}
   113  	}
   114  
   115  	// Compile User-Agent
   116  	switch request.UserAgent.Value {
   117  	case useragent.Off:
   118  		request.compiledUserAgent = " "
   119  	case useragent.Default:
   120  		request.compiledUserAgent = ""
   121  	case useragent.Custom:
   122  		if request.CustomUserAgent == "" {
   123  			return errors.New("please set custom_user_agent in the template")
   124  		}
   125  		request.compiledUserAgent = request.CustomUserAgent
   126  	case useragent.Random:
   127  		request.compiledUserAgent = uarand.GetRandom()
   128  	}
   129  
   130  	if len(request.Matchers) > 0 || len(request.Extractors) > 0 {
   131  		compiled := &request.Operators
   132  		compiled.ExcludeMatchers = options.ExcludeMatchers
   133  		compiled.TemplateID = options.TemplateID
   134  		if err := compiled.Compile(); err != nil {
   135  			return errors.Wrap(err, "could not compile operators")
   136  		}
   137  		request.CompiledOperators = compiled
   138  	}
   139  	request.options = options
   140  
   141  	if len(request.Fuzzing) > 0 {
   142  		for _, rule := range request.Fuzzing {
   143  			if fuzzingMode := options.Options.FuzzingMode; fuzzingMode != "" {
   144  				rule.Mode = fuzzingMode
   145  			}
   146  			if fuzzingType := options.Options.FuzzingType; fuzzingType != "" {
   147  				rule.Type = fuzzingType
   148  			}
   149  			if err := rule.Compile(request.generator, request.options); err != nil {
   150  				return errors.Wrap(err, "could not compile fuzzing rule")
   151  			}
   152  		}
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  // Requests returns the total number of requests the YAML rule will perform
   159  func (request *Request) Requests() int {
   160  	return 1
   161  }