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 }