package http import ( "github.com/pkg/errors" "github.com/projectdiscovery/nuclei/v2/pkg/operators" "github.com/projectdiscovery/nuclei/v2/pkg/protocols" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool" "github.com/projectdiscovery/nuclei/v2/pkg/types" "github.com/projectdiscovery/rawhttp" "github.com/projectdiscovery/retryablehttp-go" ) // Request contains a http request to be made from a template type Request struct { // Name is the name of the request Name string `yaml:"Name"` // AttackType is the attack type // Sniper, PitchFork and ClusterBomb. Default is Sniper AttackType string `yaml:"attack"` // Method is the request method, whether GET, POST, PUT, etc Method string `yaml:"method"` // Body is an optional parameter which contains the request body for POST methods, etc Body string `yaml:"body"` // Path contains the path/s for the request Path []string `yaml:"path"` // Raw contains raw requests Raw []string `yaml:"raw"` // Path contains the path/s for the request variables Payloads map[string]interface{} `yaml:"payloads"` // Headers contains headers to send with the request Headers map[string]string `yaml:"headers"` // RaceNumberRequests is the number of same request to send in race condition attack RaceNumberRequests int `yaml:"race_count"` // MaxRedirects is the maximum number of redirects that should be followed. MaxRedirects int `yaml:"max-redirects"` // PipelineConcurrentConnections is number of connections in pipelining PipelineConcurrentConnections int `yaml:"pipeline-concurrent-connections"` // PipelineRequestsPerConnection is number of requests in pipelining PipelineRequestsPerConnection int `yaml:"pipeline-requests-per-connection"` // Threads specifies number of threads for sending requests Threads int `yaml:"threads"` // CookieReuse is an optional setting that makes cookies shared within requests CookieReuse bool `yaml:"cookie-reuse"` // Redirects specifies whether redirects should be followed. Redirects bool `yaml:"redirects"` // Pipeline defines if the attack should be performed with HTTP 1.1 Pipelining (race conditions/billions requests) // All requests must be indempotent (GET/POST) Pipeline bool `yaml:"pipeline"` // Specify in order to skip request RFC normalization Unsafe bool `yaml:"unsafe"` // DisableAutoHostname Enable/Disable Host header for unsafe raw requests DisableAutoHostname bool `yaml:"disable-automatic-host-header"` // DisableAutoContentLength Enable/Disable Content-Length header for unsafe raw requests DisableAutoContentLength bool `yaml:"disable-automatic-content-length-header"` // Race determines if all the request have to be attempted at the same time // The minimum number fof requests is determined by threads Race bool `yaml:"race"` // Operators for the current request go here. *operators.Operators `yaml:",omitempty,inline"` options *protocols.ExecuterOptions attackType generators.Type totalRequests int customHeaders types.StringSlice generator *generators.Generator // optional, only enabled when using payloads httpClient *retryablehttp.Client rawhttpClient *rawhttp.Client } // Compile compiles the protocol request for further execution. func (r *Request) Compile(options *protocols.ExecuterOptions) error { client, err := httpclientpool.Get(options.Options, &httpclientpool.Configuration{ Threads: r.Threads, MaxRedirects: r.MaxRedirects, FollowRedirects: r.Redirects, }) if err != nil { return errors.Wrap(err, "could not get dns client") } r.httpClient = client if len(r.Raw) > 0 { r.rawhttpClient = httpclientpool.GetRawHTTP() } if r.Operators != nil { if err := r.Operators.Compile(); err != nil { return errors.Wrap(err, "could not compile operators") } } if len(r.Payloads) > 0 { attackType := r.AttackType if attackType == "" { attackType = "sniper" } r.attackType = generators.StringToType[attackType] r.generator, err = generators.New(r.Payloads, r.attackType, r.options.TemplatePath) if err != nil { return errors.Wrap(err, "could not parse payloads") } } r.options = options r.totalRequests = r.Requests() return nil } // Requests returns the total number of requests the YAML rule will perform func (r *Request) Requests() int { if r.generator != nil { payloadRequests := r.generator.NewIterator().Total() return len(r.Raw) * payloadRequests } if len(r.Raw) > 0 { return len(r.Raw) } return len(r.Path) }