nuclei/v2/pkg/protocols/http/http.go

169 lines
5.6 KiB
Go
Raw Normal View History

package http
2020-12-21 22:41:07 +00:00
import (
2021-01-11 21:14:51 +00:00
"strings"
2021-02-25 07:44:42 +00:00
"github.com/corpix/uarand"
2020-12-27 20:03:50 +00:00
"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"
2020-12-27 20:03:50 +00:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
"github.com/projectdiscovery/rawhttp"
"github.com/projectdiscovery/retryablehttp-go"
)
2020-12-21 22:41:07 +00:00
// Request contains a http request to be made from a template
type Request struct {
2021-01-16 08:40:24 +00:00
ID string `yaml:"id"`
// Name is the name of the request
2020-12-23 20:12:04 +00:00
Name string `yaml:"Name"`
// AttackType is the attack type
// Sniper, PitchFork and ClusterBomb. Default is Sniper
2020-12-23 20:12:04 +00:00
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
2020-12-23 20:12:04 +00:00
Body string `yaml:"body"`
// Path contains the path/s for the request
Path []string `yaml:"path"`
// Raw contains raw requests
2020-12-23 20:12:04 +00:00
Raw []string `yaml:"raw"`
// Path contains the path/s for the request variables
2020-12-23 20:12:04 +00:00
Payloads map[string]interface{} `yaml:"payloads"`
// Headers contains headers to send with the request
2020-12-23 20:12:04 +00:00
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"`
// Race determines if all the request have to be attempted at the same time
2021-02-04 16:39:32 +00:00
// The minimum number of requests is determined by threads
2020-12-23 20:12:04 +00:00
Race bool `yaml:"race"`
2021-02-04 16:39:32 +00:00
// MaxSize is the maximum size of http response body to read in bytes.
MaxSize int `yaml:"max-size"`
2020-12-23 20:12:04 +00:00
2020-12-27 20:03:50 +00:00
// Operators for the current request go here.
2020-12-30 07:56:55 +00:00
operators.Operators `yaml:",inline"`
CompiledOperators *operators.Operators
2020-12-27 20:03:50 +00:00
options *protocols.ExecuterOptions
attackType generators.Type
totalRequests int
2021-02-01 10:51:49 +00:00
customHeaders map[string]string
2020-12-27 20:03:50 +00:00
generator *generators.Generator // optional, only enabled when using payloads
httpClient *retryablehttp.Client
rawhttpClient *rawhttp.Client
}
2021-01-16 08:40:24 +00:00
// GetID returns the unique ID of the request if any.
func (r *Request) GetID() string {
return r.ID
}
2020-12-27 20:03:50 +00:00
// 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,
2021-01-15 08:47:34 +00:00
CookieReuse: r.CookieReuse,
2020-12-27 20:03:50 +00:00
})
if err != nil {
return errors.Wrap(err, "could not get dns client")
}
2021-02-01 10:51:49 +00:00
r.customHeaders = make(map[string]string)
2020-12-27 20:03:50 +00:00
r.httpClient = client
2020-12-29 14:16:52 +00:00
r.options = options
for _, option := range r.options.Options.CustomHeaders {
parts := strings.SplitN(option, ":", 2)
2021-02-01 10:51:49 +00:00
if len(parts) != 2 {
continue
}
r.customHeaders[parts[0]] = strings.TrimSpace(parts[1])
}
2021-02-25 10:55:55 +00:00
// Add User-Agent value randomly to the customHeaders slice if `random-agent` flag is given
if r.options.Options.RandomAgent {
r.customHeaders["User-Agent"] = uarand.GetRandom()
}
2020-12-27 20:03:50 +00:00
2021-02-04 16:59:36 +00:00
if r.Body != "" && !strings.Contains(r.Body, "\r\n") {
r.Body = strings.ReplaceAll(r.Body, "\n", "\r\n")
}
2020-12-27 20:03:50 +00:00
if len(r.Raw) > 0 {
2021-02-04 16:57:47 +00:00
for i, raw := range r.Raw {
if !strings.Contains(raw, "\r\n") {
r.Raw[i] = strings.ReplaceAll(raw, "\n", "\r\n")
}
}
2020-12-27 20:03:50 +00:00
r.rawhttpClient = httpclientpool.GetRawHTTP()
}
2020-12-30 07:56:55 +00:00
if len(r.Matchers) > 0 || len(r.Extractors) > 0 {
compiled := &r.Operators
if err := compiled.Compile(); err != nil {
2020-12-27 20:03:50 +00:00
return errors.Wrap(err, "could not compile operators")
}
2020-12-30 07:56:55 +00:00
r.CompiledOperators = compiled
2020-12-27 20:03:50 +00:00
}
if len(r.Payloads) > 0 {
2020-12-29 06:38:46 +00:00
attackType := r.AttackType
if attackType == "" {
attackType = "sniper"
}
r.attackType = generators.StringToType[attackType]
2021-01-11 21:14:51 +00:00
// Resolve payload paths if they are files.
for name, payload := range r.Payloads {
switch pt := payload.(type) {
case string:
final, err := options.Catalogue.ResolvePath(pt, options.TemplatePath)
if err != nil {
return errors.Wrap(err, "could not read payload file")
2021-01-11 21:14:51 +00:00
}
r.Payloads[name] = final
2021-01-11 21:14:51 +00:00
}
}
2020-12-29 06:38:46 +00:00
r.generator, err = generators.New(r.Payloads, r.attackType, r.options.TemplatePath)
2020-12-27 20:03:50 +00:00
if err != nil {
return errors.Wrap(err, "could not parse payloads")
}
}
r.options = options
r.totalRequests = r.Requests()
2020-12-27 20:03:50 +00:00
return nil
2020-12-21 22:41:07 +00:00
}
// Requests returns the total number of requests the YAML rule will perform
func (r *Request) Requests() int {
2020-12-29 14:05:16 +00:00
if r.generator != nil {
payloadRequests := r.generator.NewIterator().Total() * len(r.Raw)
return payloadRequests
}
2020-12-29 14:05:16 +00:00
if len(r.Raw) > 0 {
requests := len(r.Raw)
if requests == 1 && r.RaceNumberRequests != 0 {
requests = requests * r.RaceNumberRequests
}
return requests
}
return len(r.Path)
}