2020-04-03 18:47:57 +00:00
|
|
|
package requests
|
|
|
|
|
|
|
|
import (
|
2020-04-28 21:02:07 +00:00
|
|
|
"bufio"
|
2020-04-03 21:20:32 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2020-04-03 21:56:11 +00:00
|
|
|
"net/url"
|
2020-04-03 21:20:32 +00:00
|
|
|
"strings"
|
|
|
|
|
2020-04-05 19:14:45 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/pkg/extractors"
|
2020-04-03 18:47:57 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
2020-04-04 11:42:29 +00:00
|
|
|
retryablehttp "github.com/projectdiscovery/retryablehttp-go"
|
2020-04-03 21:56:11 +00:00
|
|
|
"github.com/valyala/fasttemplate"
|
2020-04-03 18:47:57 +00:00
|
|
|
)
|
|
|
|
|
2020-04-22 20:45:02 +00:00
|
|
|
// HTTPRequest contains a request to be made from a template
|
|
|
|
type HTTPRequest struct {
|
2020-04-03 18:47:57 +00:00
|
|
|
// Method is the request method, whether GET, POST, PUT, etc
|
|
|
|
Method string `yaml:"method"`
|
|
|
|
// Path contains the path/s for the request
|
|
|
|
Path []string `yaml:"path"`
|
|
|
|
// Headers contains headers to send with the request
|
|
|
|
Headers map[string]string `yaml:"headers,omitempty"`
|
|
|
|
// Body is an optional parameter which contains the request body for POST methods, etc
|
|
|
|
Body string `yaml:"body,omitempty"`
|
|
|
|
// Matchers contains the detection mechanism for the request to identify
|
|
|
|
// whether the request was successful
|
2020-04-05 18:28:22 +00:00
|
|
|
Matchers []*matchers.Matcher `yaml:"matchers,omitempty"`
|
2020-04-26 00:20:33 +00:00
|
|
|
// MatchersCondition is the condition of the matchers
|
|
|
|
// whether to use AND or OR. Default is OR.
|
|
|
|
MatchersCondition string `yaml:"matchers-condition,omitempty"`
|
2020-04-26 01:03:59 +00:00
|
|
|
// matchersCondition is internal condition for the matchers.
|
|
|
|
matchersCondition matchers.ConditionType
|
2020-04-05 18:28:22 +00:00
|
|
|
// Extractors contains the extraction mechanism for the request to identify
|
|
|
|
// and extract parts of the response.
|
2020-04-05 19:14:45 +00:00
|
|
|
Extractors []*extractors.Extractor `yaml:"extractors,omitempty"`
|
2020-04-22 22:26:41 +00:00
|
|
|
// Redirects specifies whether redirects should be followed.
|
|
|
|
Redirects bool `yaml:"redirects,omitempty"`
|
|
|
|
// MaxRedirects is the maximum number of redirects that should be followed.
|
|
|
|
MaxRedirects int `yaml:"max-redirects,omitempty"`
|
2020-04-28 21:02:07 +00:00
|
|
|
// Raw contains a raw request
|
|
|
|
Raw string `yaml:"raw,omitempty"`
|
2020-04-03 18:47:57 +00:00
|
|
|
}
|
2020-04-03 21:20:32 +00:00
|
|
|
|
2020-04-26 01:03:59 +00:00
|
|
|
// GetMatchersCondition returns the condition for the matcher
|
|
|
|
func (r *HTTPRequest) GetMatchersCondition() matchers.ConditionType {
|
|
|
|
return r.matchersCondition
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetMatchersCondition sets the condition for the matcher
|
|
|
|
func (r *HTTPRequest) SetMatchersCondition(condition matchers.ConditionType) {
|
|
|
|
r.matchersCondition = condition
|
|
|
|
}
|
|
|
|
|
2020-04-22 20:45:02 +00:00
|
|
|
// MakeHTTPRequest creates a *http.Request from a request template
|
|
|
|
func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request, error) {
|
2020-04-28 21:02:07 +00:00
|
|
|
if r.Raw != "" {
|
|
|
|
return r.makeHTTPRequestFromRaw(baseURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.makeHTTPRequestFromModel(baseURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeHTTPRequestFromModel creates a *http.Request from a request template
|
|
|
|
func (r *HTTPRequest) makeHTTPRequestFromModel(baseURL string) ([]*retryablehttp.Request, error) {
|
2020-04-03 21:56:11 +00:00
|
|
|
parsed, err := url.Parse(baseURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hostname := parsed.Hostname()
|
2020-04-03 21:20:32 +00:00
|
|
|
|
2020-04-04 11:42:29 +00:00
|
|
|
requests := make([]*retryablehttp.Request, 0, len(r.Path))
|
2020-04-03 21:20:32 +00:00
|
|
|
for _, path := range r.Path {
|
2020-04-03 21:56:11 +00:00
|
|
|
// Replace the dynamic variables in the URL if any
|
|
|
|
t := fasttemplate.New(path, "{{", "}}")
|
|
|
|
url := t.ExecuteString(map[string]interface{}{
|
|
|
|
"BaseURL": baseURL,
|
|
|
|
"Hostname": hostname,
|
|
|
|
})
|
2020-04-03 21:20:32 +00:00
|
|
|
|
2020-04-03 21:56:11 +00:00
|
|
|
// Build a request on the specified URL
|
2020-04-03 21:20:32 +00:00
|
|
|
req, err := http.NewRequest(r.Method, url, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the user requested a request body
|
|
|
|
if r.Body != "" {
|
|
|
|
req.Body = ioutil.NopCloser(strings.NewReader(r.Body))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the header values requested
|
|
|
|
for header, value := range r.Headers {
|
2020-04-27 18:10:21 +00:00
|
|
|
t := fasttemplate.New(value, "{{", "}}")
|
|
|
|
val := t.ExecuteString(map[string]interface{}{
|
|
|
|
"BaseURL": baseURL,
|
|
|
|
"Hostname": hostname,
|
|
|
|
})
|
|
|
|
req.Header.Set(header, val)
|
2020-04-03 21:20:32 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
|
|
|
|
// Set some headers only if the header wasn't supplied by the user
|
|
|
|
if _, ok := r.Headers["User-Agent"]; !ok {
|
|
|
|
req.Header.Set("User-Agent", "Nuclei (@pdiscoveryio)")
|
|
|
|
}
|
2020-04-05 18:28:22 +00:00
|
|
|
if _, ok := r.Headers["Accept"]; !ok {
|
|
|
|
req.Header.Set("Accept", "*/*")
|
|
|
|
}
|
|
|
|
if _, ok := r.Headers["Accept-Language"]; !ok {
|
|
|
|
req.Header.Set("Accept-Language", "en")
|
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
req.Header.Set("Connection", "close")
|
|
|
|
req.Close = true
|
|
|
|
|
2020-04-04 11:42:29 +00:00
|
|
|
request, err := retryablehttp.FromRequest(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
requests = append(requests, request)
|
2020-04-03 21:20:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return requests, nil
|
|
|
|
}
|
2020-04-28 21:02:07 +00:00
|
|
|
|
|
|
|
// makeHTTPRequestFromRaw creates a *http.Request from a raw request
|
|
|
|
func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string) ([]*retryablehttp.Request, error) {
|
|
|
|
parsed, err := url.Parse(baseURL)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
hostname := parsed.Hostname()
|
|
|
|
|
|
|
|
requests := make([]*retryablehttp.Request, 0, len(r.Path))
|
|
|
|
for _, path := range r.Path {
|
|
|
|
// Replace the dynamic variables in the URL if any
|
|
|
|
t := fasttemplate.New(path, "{{", "}}")
|
|
|
|
|
|
|
|
// Build a request from raw
|
|
|
|
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(r.Raw)))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overwrite the raw requests with template defined fields if defined
|
|
|
|
req.Method = r.Method
|
|
|
|
targetURL, err := url.Parse(t.ExecuteString(map[string]interface{}{
|
|
|
|
"BaseURL": baseURL,
|
|
|
|
"Hostname": hostname,
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
req.URL = targetURL
|
|
|
|
|
|
|
|
// Check if the user requested a request body
|
|
|
|
if r.Body != "" {
|
|
|
|
req.Body = ioutil.NopCloser(strings.NewReader(r.Body))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the header values requested
|
|
|
|
for header, value := range r.Headers {
|
|
|
|
t := fasttemplate.New(value, "{{", "}}")
|
|
|
|
val := t.ExecuteString(map[string]interface{}{
|
|
|
|
"BaseURL": baseURL,
|
|
|
|
"Hostname": hostname,
|
|
|
|
})
|
|
|
|
req.Header.Set(header, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set some headers only if the header wasn't supplied by the user
|
|
|
|
if _, ok := r.Headers["User-Agent"]; !ok {
|
|
|
|
req.Header.Set("User-Agent", "Nuclei (@pdiscoveryio)")
|
|
|
|
}
|
|
|
|
if _, ok := r.Headers["Accept"]; !ok {
|
|
|
|
req.Header.Set("Accept", "*/*")
|
|
|
|
}
|
|
|
|
if _, ok := r.Headers["Accept-Language"]; !ok {
|
|
|
|
req.Header.Set("Accept-Language", "en")
|
|
|
|
}
|
|
|
|
req.Header.Set("Connection", "close")
|
|
|
|
req.Close = true
|
|
|
|
|
|
|
|
request, err := retryablehttp.FromRequest(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
requests = append(requests, request)
|
|
|
|
}
|
|
|
|
|
|
|
|
return requests, nil
|
|
|
|
}
|