mirror of https://github.com/daffainfo/nuclei.git
MIsc
parent
5bd3438b4f
commit
164a67353b
|
@ -1,42 +0,0 @@
|
||||||
package atomicboolean
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AtomBool struct {
|
|
||||||
sync.RWMutex
|
|
||||||
flag bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *AtomBool {
|
|
||||||
return &AtomBool{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *AtomBool) Or(value bool) {
|
|
||||||
b.Lock()
|
|
||||||
defer b.Unlock()
|
|
||||||
|
|
||||||
b.flag = b.flag || value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *AtomBool) And(value bool) {
|
|
||||||
b.Lock()
|
|
||||||
defer b.Unlock()
|
|
||||||
|
|
||||||
b.flag = b.flag && value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *AtomBool) Set(value bool) {
|
|
||||||
b.Lock()
|
|
||||||
defer b.Unlock()
|
|
||||||
|
|
||||||
b.flag = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *AtomBool) Get() bool {
|
|
||||||
b.RLock()
|
|
||||||
defer b.RUnlock() //nolint
|
|
||||||
|
|
||||||
return b.flag
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package executer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/internal/progress"
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/requests"
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DNSExecuter is a client for performing a DNS request
|
|
||||||
// for a template.
|
|
||||||
type DNSExecuter struct {
|
|
||||||
template *templates.Template
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNSOptions contains configuration options for the DNS executer.
|
|
||||||
type DNSOptions struct {
|
|
||||||
Template *templates.Template
|
|
||||||
DNSRequest *requests.DNSRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDNSExecuter creates a new DNS executer from a template
|
|
||||||
// and a DNS request query.
|
|
||||||
func NewDNSExecuter(options *DNSOptions) *DNSExecuter {
|
|
||||||
|
|
||||||
executer := &DNSExecuter{
|
|
||||||
debug: options.Debug,
|
|
||||||
noMeta: options.NoMeta,
|
|
||||||
jsonOutput: options.JSON,
|
|
||||||
traceLog: options.TraceLog,
|
|
||||||
jsonRequest: options.JSONRequests,
|
|
||||||
dnsClient: dnsClient,
|
|
||||||
vhost: options.VHost,
|
|
||||||
template: options.Template,
|
|
||||||
dnsRequest: options.DNSRequest,
|
|
||||||
writer: options.Writer,
|
|
||||||
coloredOutput: options.ColoredOutput,
|
|
||||||
colorizer: options.Colorizer,
|
|
||||||
decolorizer: options.Decolorizer,
|
|
||||||
ratelimiter: options.RateLimiter,
|
|
||||||
}
|
|
||||||
return executer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteDNS executes the DNS request on a URL
|
|
||||||
func (e *DNSExecuter) ExecuteDNS(p *progress.Progress, reqURL string) *Result {
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the dns executer for a template.
|
|
||||||
func (e *DNSExecuter) Close() {}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package executer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
"github.com/projectdiscovery/gologger"
|
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/matchers"
|
|
||||||
)
|
|
||||||
|
|
||||||
// writeOutputDNS writes dns output to streams
|
|
||||||
// nolint:interfacer // dns.Msg is out of current scope
|
|
||||||
func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher *matchers.Matcher, extractorResults []string) {
|
|
||||||
if e.jsonOutput {
|
|
||||||
output := make(jsonOutput)
|
|
||||||
output["matched"] = domain
|
|
||||||
|
|
||||||
if !e.noMeta {
|
|
||||||
output["template"] = e.template.ID
|
|
||||||
output["type"] = "dns"
|
|
||||||
output["host"] = domain
|
|
||||||
for k, v := range e.template.Info {
|
|
||||||
output[k] = v
|
|
||||||
}
|
|
||||||
if matcher != nil && len(matcher.Name) > 0 {
|
|
||||||
output["matcher_name"] = matcher.Name
|
|
||||||
}
|
|
||||||
if len(extractorResults) > 0 {
|
|
||||||
output["extracted_results"] = extractorResults
|
|
||||||
}
|
|
||||||
if e.jsonRequest {
|
|
||||||
output["request"] = req.String()
|
|
||||||
output["response"] = resp.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := jsoniter.Marshal(output)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Warningf("Could not marshal json output: %s\n", err)
|
|
||||||
}
|
|
||||||
gologger.Silentf("%s", string(data))
|
|
||||||
if e.writer != nil {
|
|
||||||
if err := e.writer.Write(data); err != nil {
|
|
||||||
gologger.Errorf("Could not write output data: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
colorizer := e.colorizer
|
|
||||||
|
|
||||||
if !e.noMeta {
|
|
||||||
builder.WriteRune('[')
|
|
||||||
builder.WriteString(colorizer.Colorizer.BrightGreen(e.template.ID).String())
|
|
||||||
|
|
||||||
if matcher != nil && len(matcher.Name) > 0 {
|
|
||||||
builder.WriteString(":")
|
|
||||||
builder.WriteString(colorizer.Colorizer.BrightGreen(matcher.Name).Bold().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString("] [")
|
|
||||||
builder.WriteString(colorizer.Colorizer.BrightBlue("dns").String())
|
|
||||||
builder.WriteString("] ")
|
|
||||||
|
|
||||||
if e.template.Info["severity"] != "" {
|
|
||||||
builder.WriteString("[")
|
|
||||||
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info["severity"]))
|
|
||||||
builder.WriteString("] ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.WriteString(domain)
|
|
||||||
|
|
||||||
// If any extractors, write the results
|
|
||||||
if len(extractorResults) > 0 && !e.noMeta {
|
|
||||||
builder.WriteString(" [")
|
|
||||||
|
|
||||||
for i, result := range extractorResults {
|
|
||||||
builder.WriteString(colorizer.Colorizer.BrightCyan(result).String())
|
|
||||||
|
|
||||||
if i != len(extractorResults)-1 {
|
|
||||||
builder.WriteRune(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.WriteString("]")
|
|
||||||
}
|
|
||||||
builder.WriteRune('\n')
|
|
||||||
|
|
||||||
// Write output to screen as well as any output file
|
|
||||||
message := builder.String()
|
|
||||||
gologger.Silentf("%s", message)
|
|
||||||
|
|
||||||
if e.writer != nil {
|
|
||||||
if e.coloredOutput {
|
|
||||||
message = e.decolorizer.ReplaceAllString(message, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := e.writer.WriteString(message); err != nil {
|
|
||||||
gologger.Errorf("Could not write output data: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,6 +48,7 @@ type InternalEvent map[string]interface{}
|
||||||
// InternalWrappedEvent is a wrapped event with operators result added to it.
|
// InternalWrappedEvent is a wrapped event with operators result added to it.
|
||||||
type InternalWrappedEvent struct {
|
type InternalWrappedEvent struct {
|
||||||
InternalEvent InternalEvent
|
InternalEvent InternalEvent
|
||||||
|
Results []*ResultEvent
|
||||||
OperatorsResult *operators.Result
|
OperatorsResult *operators.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ func NewExecuter(requests []*Request, options *protocols.ExecuterOptions) *Execu
|
||||||
// Compile compiles the execution generators preparing any requests possible.
|
// Compile compiles the execution generators preparing any requests possible.
|
||||||
func (e *Executer) Compile() error {
|
func (e *Executer) Compile() error {
|
||||||
for _, request := range e.requests {
|
for _, request := range e.requests {
|
||||||
if err := request.Compile(e.options); err != nil {
|
err := request.Compile(e.options)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +47,9 @@ func (e *Executer) Execute(input string) (bool, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
if events == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a result field, we should add a result to slice.
|
// If we have a result field, we should add a result to slice.
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
|
@ -63,20 +67,24 @@ func (e *Executer) Execute(input string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
||||||
func (e *Executer) ExecuteWithResults(input string) ([]*output.ResultEvent, error) {
|
func (e *Executer) ExecuteWithResults(input string) ([]*output.InternalWrappedEvent, error) {
|
||||||
var results []*output.ResultEvent
|
var results []*output.InternalWrappedEvent
|
||||||
|
|
||||||
for _, req := range e.requests {
|
for _, req := range e.requests {
|
||||||
events, err := req.ExecuteWithResults(input, nil)
|
events, err := req.ExecuteWithResults(input, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if events == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
if event.OperatorsResult == nil {
|
if event.OperatorsResult == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
results = append(results, req.makeResultEvent(event)...)
|
event.Results = req.makeResultEvent(event)
|
||||||
}
|
}
|
||||||
|
results = append(results, events...)
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,13 +79,16 @@ func (r *Request) Extract(data map[string]interface{}, extractor *extractors.Ext
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseToDSLMap converts a DNS response to a map for use in DSL matching
|
// responseToDSLMap converts a DNS response to a map for use in DSL matching
|
||||||
func responseToDSLMap(req, resp *dns.Msg, host, matched string) output.InternalEvent {
|
func (r *Request) responseToDSLMap(req, resp *dns.Msg, host, matched string) output.InternalEvent {
|
||||||
data := make(output.InternalEvent, 8)
|
data := make(output.InternalEvent, 8)
|
||||||
|
|
||||||
// Some data regarding the request metadata
|
// Some data regarding the request metadata
|
||||||
data["host"] = host
|
data["host"] = host
|
||||||
data["matched"] = matched
|
data["matched"] = matched
|
||||||
data["request"] = req.String()
|
|
||||||
|
if r.options.Options.JSONRequests {
|
||||||
|
data["request"] = req.String()
|
||||||
|
}
|
||||||
|
|
||||||
data["rcode"] = resp.Rcode
|
data["rcode"] = resp.Rcode
|
||||||
buffer := &bytes.Buffer{}
|
buffer := &bytes.Buffer{}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (r *Request) ExecuteWithResults(input string, metadata output.InternalEvent
|
||||||
gologger.Debug().Msgf("[%s] Dumped DNS response for %s", r.options.TemplateID, domain)
|
gologger.Debug().Msgf("[%s] Dumped DNS response for %s", r.options.TemplateID, domain)
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", resp.String())
|
fmt.Fprintf(os.Stderr, "%s\n", resp.String())
|
||||||
}
|
}
|
||||||
ouputEvent := responseToDSLMap(compiledRequest, resp, input, input)
|
ouputEvent := r.responseToDSLMap(compiledRequest, resp, input, input)
|
||||||
|
|
||||||
event := []*output.InternalWrappedEvent{{InternalEvent: ouputEvent}}
|
event := []*output.InternalWrappedEvent{{InternalEvent: ouputEvent}}
|
||||||
if r.Operators != nil {
|
if r.Operators != nil {
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Knetic/govaluate"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/generators"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/syncedreadcloser"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MakeHTTPRequest makes the HTTP request
|
||||||
|
func (r *Request) MakeHTTPRequest(baseURL string, dynamicValues map[string]interface{}, data string) (*HTTPRequest, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
parsed, err := url.Parse(baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := parsed.Host
|
||||||
|
|
||||||
|
values := generators.MergeMaps(dynamicValues, map[string]interface{}{
|
||||||
|
"BaseURL": baseURLWithTemplatePrefs(data, parsed),
|
||||||
|
"Hostname": hostname,
|
||||||
|
})
|
||||||
|
|
||||||
|
// if data contains \n it's a raw request
|
||||||
|
if strings.Contains(data, "\n") {
|
||||||
|
return r.makeHTTPRequestFromRaw(ctx, baseURL, data, values)
|
||||||
|
}
|
||||||
|
return r.makeHTTPRequestFromModel(ctx, data, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeHTTPRequestFromModel creates a *http.Request from a request template
|
||||||
|
func (r *Request) makeHTTPRequestFromModel(ctx context.Context, data string, values map[string]interface{}) (*HTTPRequest, error) {
|
||||||
|
replacer := newReplacer(values)
|
||||||
|
URL := replacer.Replace(data)
|
||||||
|
|
||||||
|
// Build a request on the specified URL
|
||||||
|
req, err := http.NewRequestWithContext(ctx, r.Method, URL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := r.fillRequest(req, values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &HTTPRequest{Request: request}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitGenerator initializes the generator
|
||||||
|
func (r *Request) InitGenerator() {
|
||||||
|
r.gsfm = NewGeneratorFSM(r.attackType, r.Payloads, r.Path, r.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateGenerator creates the generator
|
||||||
|
func (r *Request) CreateGenerator(reqURL string) {
|
||||||
|
r.gsfm.Add(reqURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasGenerator check if an URL has a generator
|
||||||
|
func (r *Request) HasGenerator(reqURL string) bool {
|
||||||
|
return r.gsfm.Has(reqURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOne reads and return a generator by URL
|
||||||
|
func (r *Request) ReadOne(reqURL string) {
|
||||||
|
r.gsfm.ReadOne(reqURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeHTTPRequestFromRaw creates a *http.Request from a raw request
|
||||||
|
func (r *Request) makeHTTPRequestFromRaw(ctx context.Context, baseURL, data string, values map[string]interface{}) (*HTTPRequest, error) {
|
||||||
|
// Add trailing line
|
||||||
|
data += "\n"
|
||||||
|
|
||||||
|
if len(r.Payloads) > 0 {
|
||||||
|
r.gsfm.InitOrSkip(baseURL)
|
||||||
|
r.ReadOne(baseURL)
|
||||||
|
|
||||||
|
payloads, err := r.GetPayloadsValues(baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.handleRawWithPaylods(ctx, data, baseURL, values, payloads)
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise continue with normal flow
|
||||||
|
return r.handleRawWithPaylods(ctx, data, baseURL, values, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) handleRawWithPaylods(ctx context.Context, raw, baseURL string, values, genValues map[string]interface{}) (*HTTPRequest, error) {
|
||||||
|
baseValues := generators.CopyMap(values)
|
||||||
|
finValues := generators.MergeMaps(baseValues, genValues)
|
||||||
|
|
||||||
|
replacer := newReplacer(finValues)
|
||||||
|
|
||||||
|
// Replace the dynamic variables in the URL if any
|
||||||
|
raw = replacer.Replace(raw)
|
||||||
|
|
||||||
|
dynamicValues := make(map[string]interface{})
|
||||||
|
// find all potentials tokens between {{}}
|
||||||
|
var re = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`)
|
||||||
|
for _, match := range re.FindAllString(raw, -1) {
|
||||||
|
// check if the match contains a dynamic variable
|
||||||
|
expr := generators.TrimDelimiters(match)
|
||||||
|
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, generators.HelperFunctions())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := compiled.Evaluate(finValues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dynamicValues[expr] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace dynamic values
|
||||||
|
dynamicReplacer := newReplacer(dynamicValues)
|
||||||
|
raw = dynamicReplacer.Replace(raw)
|
||||||
|
|
||||||
|
rawRequest, err := r.parseRawRequest(raw, baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawhttp
|
||||||
|
if r.Unsafe {
|
||||||
|
unsafeReq := &HTTPRequest{
|
||||||
|
RawRequest: rawRequest,
|
||||||
|
Meta: genValues,
|
||||||
|
AutomaticHostHeader: !r.DisableAutoHostname,
|
||||||
|
AutomaticContentLengthHeader: !r.DisableAutoContentLength,
|
||||||
|
Unsafe: true,
|
||||||
|
FollowRedirects: r.Redirects,
|
||||||
|
}
|
||||||
|
return unsafeReq, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryablehttp
|
||||||
|
var body io.ReadCloser
|
||||||
|
body = ioutil.NopCloser(strings.NewReader(rawRequest.Data))
|
||||||
|
if r.Race {
|
||||||
|
// More or less this ensures that all requests hit the endpoint at the same approximated time
|
||||||
|
// Todo: sync internally upon writing latest request byte
|
||||||
|
body = syncedreadcloser.NewOpenGateWithTimeout(body, time.Duration(two)*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, rawRequest.Method, rawRequest.FullURL, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy headers
|
||||||
|
for key, value := range rawRequest.Headers {
|
||||||
|
req.Header[key] = []string{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := r.fillRequest(req, values)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTTPRequest{Request: request, Meta: genValues}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Request) fillRequest(req *http.Request, values map[string]interface{}) (*retryablehttp.Request, error) {
|
||||||
|
replacer := replacer.New(values)
|
||||||
|
// Set the header values requested
|
||||||
|
for header, value := range r.Headers {
|
||||||
|
req.Header[header] = []string{replacer.Replace(value)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case of multiple threads the underlying connection should remain open to allow reuse
|
||||||
|
if r.Threads <= 0 && req.Header.Get("Connection") == "" {
|
||||||
|
req.Close = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user requested a request body
|
||||||
|
if r.Body != "" {
|
||||||
|
req.Body = ioutil.NopCloser(strings.NewReader(r.Body))
|
||||||
|
}
|
||||||
|
setHeader(req, "User-Agent", "Nuclei - Open-source project (github.com/projectdiscovery/nuclei)")
|
||||||
|
|
||||||
|
// raw requests are left untouched
|
||||||
|
if len(r.Raw) > 0 {
|
||||||
|
return retryablehttp.FromRequest(req)
|
||||||
|
}
|
||||||
|
setHeader(req, "Accept", "*/*")
|
||||||
|
setHeader(req, "Accept-Language", "en")
|
||||||
|
|
||||||
|
return retryablehttp.FromRequest(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setHeader sets some headers only if the header wasn't supplied by the user
|
||||||
|
func setHeader(req *http.Request, name, value string) {
|
||||||
|
if _, ok := req.Header[name]; !ok {
|
||||||
|
req.Header.Set(name, value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ type Executer interface {
|
||||||
// Execute executes the protocol group and returns true or false if results were found.
|
// Execute executes the protocol group and returns true or false if results were found.
|
||||||
Execute(input string) (bool, error)
|
Execute(input string) (bool, error)
|
||||||
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
||||||
ExecuteWithResults(input string) ([]*output.ResultEvent, error)
|
ExecuteWithResults(input string) ([]*output.InternalWrappedEvent, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuterOptions contains the configuration options for executer clients
|
// ExecuterOptions contains the configuration options for executer clients
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
package requests
|
package requests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Knetic/govaluate"
|
"github.com/Knetic/govaluate"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/generators"
|
"github.com/projectdiscovery/nuclei/v2/pkg/generators"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/matchers"
|
"github.com/projectdiscovery/nuclei/v2/pkg/matchers"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/syncedreadcloser"
|
|
||||||
"github.com/projectdiscovery/rawhttp"
|
"github.com/projectdiscovery/rawhttp"
|
||||||
retryablehttp "github.com/projectdiscovery/retryablehttp-go"
|
retryablehttp "github.com/projectdiscovery/retryablehttp-go"
|
||||||
)
|
)
|
||||||
|
@ -52,196 +46,6 @@ func (r *BulkHTTPRequest) GetRequestCount() int64 {
|
||||||
return int64(r.gsfm.Total())
|
return int64(r.gsfm.Total())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeHTTPRequest makes the HTTP request
|
|
||||||
func (r *BulkHTTPRequest) MakeHTTPRequest(baseURL string, dynamicValues map[string]interface{}, data string) (*HTTPRequest, error) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
parsed, err := url.Parse(baseURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname := parsed.Host
|
|
||||||
|
|
||||||
values := generators.MergeMaps(dynamicValues, map[string]interface{}{
|
|
||||||
"BaseURL": baseURLWithTemplatePrefs(data, parsed),
|
|
||||||
"Hostname": hostname,
|
|
||||||
})
|
|
||||||
|
|
||||||
// if data contains \n it's a raw request
|
|
||||||
if strings.Contains(data, "\n") {
|
|
||||||
return r.makeHTTPRequestFromRaw(ctx, baseURL, data, values)
|
|
||||||
}
|
|
||||||
return r.makeHTTPRequestFromModel(ctx, data, values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeHTTPRequestFromModel creates a *http.Request from a request template
|
|
||||||
func (r *BulkHTTPRequest) makeHTTPRequestFromModel(ctx context.Context, data string, values map[string]interface{}) (*HTTPRequest, error) {
|
|
||||||
replacer := newReplacer(values)
|
|
||||||
URL := replacer.Replace(data)
|
|
||||||
|
|
||||||
// Build a request on the specified URL
|
|
||||||
req, err := http.NewRequestWithContext(ctx, r.Method, URL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
request, err := r.fillRequest(req, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &HTTPRequest{Request: request}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitGenerator initializes the generator
|
|
||||||
func (r *BulkHTTPRequest) InitGenerator() {
|
|
||||||
r.gsfm = NewGeneratorFSM(r.attackType, r.Payloads, r.Path, r.Raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateGenerator creates the generator
|
|
||||||
func (r *BulkHTTPRequest) CreateGenerator(reqURL string) {
|
|
||||||
r.gsfm.Add(reqURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasGenerator check if an URL has a generator
|
|
||||||
func (r *BulkHTTPRequest) HasGenerator(reqURL string) bool {
|
|
||||||
return r.gsfm.Has(reqURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOne reads and return a generator by URL
|
|
||||||
func (r *BulkHTTPRequest) ReadOne(reqURL string) {
|
|
||||||
r.gsfm.ReadOne(reqURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeHTTPRequestFromRaw creates a *http.Request from a raw request
|
|
||||||
func (r *BulkHTTPRequest) makeHTTPRequestFromRaw(ctx context.Context, baseURL, data string, values map[string]interface{}) (*HTTPRequest, error) {
|
|
||||||
// Add trailing line
|
|
||||||
data += "\n"
|
|
||||||
|
|
||||||
if len(r.Payloads) > 0 {
|
|
||||||
r.gsfm.InitOrSkip(baseURL)
|
|
||||||
r.ReadOne(baseURL)
|
|
||||||
|
|
||||||
payloads, err := r.GetPayloadsValues(baseURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.handleRawWithPaylods(ctx, data, baseURL, values, payloads)
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise continue with normal flow
|
|
||||||
return r.handleRawWithPaylods(ctx, data, baseURL, values, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BulkHTTPRequest) handleRawWithPaylods(ctx context.Context, raw, baseURL string, values, genValues map[string]interface{}) (*HTTPRequest, error) {
|
|
||||||
baseValues := generators.CopyMap(values)
|
|
||||||
finValues := generators.MergeMaps(baseValues, genValues)
|
|
||||||
|
|
||||||
replacer := newReplacer(finValues)
|
|
||||||
|
|
||||||
// Replace the dynamic variables in the URL if any
|
|
||||||
raw = replacer.Replace(raw)
|
|
||||||
|
|
||||||
dynamicValues := make(map[string]interface{})
|
|
||||||
// find all potentials tokens between {{}}
|
|
||||||
var re = regexp.MustCompile(`(?m)\{\{[^}]+\}\}`)
|
|
||||||
for _, match := range re.FindAllString(raw, -1) {
|
|
||||||
// check if the match contains a dynamic variable
|
|
||||||
expr := generators.TrimDelimiters(match)
|
|
||||||
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, generators.HelperFunctions())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := compiled.Evaluate(finValues)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamicValues[expr] = result
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace dynamic values
|
|
||||||
dynamicReplacer := newReplacer(dynamicValues)
|
|
||||||
raw = dynamicReplacer.Replace(raw)
|
|
||||||
|
|
||||||
rawRequest, err := r.parseRawRequest(raw, baseURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// rawhttp
|
|
||||||
if r.Unsafe {
|
|
||||||
unsafeReq := &HTTPRequest{
|
|
||||||
RawRequest: rawRequest,
|
|
||||||
Meta: genValues,
|
|
||||||
AutomaticHostHeader: !r.DisableAutoHostname,
|
|
||||||
AutomaticContentLengthHeader: !r.DisableAutoContentLength,
|
|
||||||
Unsafe: true,
|
|
||||||
FollowRedirects: r.Redirects,
|
|
||||||
}
|
|
||||||
return unsafeReq, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// retryablehttp
|
|
||||||
var body io.ReadCloser
|
|
||||||
body = ioutil.NopCloser(strings.NewReader(rawRequest.Data))
|
|
||||||
if r.Race {
|
|
||||||
// More or less this ensures that all requests hit the endpoint at the same approximated time
|
|
||||||
// Todo: sync internally upon writing latest request byte
|
|
||||||
body = syncedreadcloser.NewOpenGateWithTimeout(body, time.Duration(two)*time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, rawRequest.Method, rawRequest.FullURL, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy headers
|
|
||||||
for key, value := range rawRequest.Headers {
|
|
||||||
req.Header[key] = []string{value}
|
|
||||||
}
|
|
||||||
|
|
||||||
request, err := r.fillRequest(req, values)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &HTTPRequest{Request: request, Meta: genValues}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BulkHTTPRequest) fillRequest(req *http.Request, values map[string]interface{}) (*retryablehttp.Request, error) {
|
|
||||||
replacer := newReplacer(values)
|
|
||||||
// Set the header values requested
|
|
||||||
for header, value := range r.Headers {
|
|
||||||
req.Header[header] = []string{replacer.Replace(value)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case of multiple threads the underlying connection should remain open to allow reuse
|
|
||||||
if r.Threads <= 0 && req.Header.Get("Connection") == "" {
|
|
||||||
req.Close = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the user requested a request body
|
|
||||||
if r.Body != "" {
|
|
||||||
req.Body = ioutil.NopCloser(strings.NewReader(r.Body))
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeader(req, "User-Agent", "Nuclei - Open-source project (github.com/projectdiscovery/nuclei)")
|
|
||||||
|
|
||||||
// raw requests are left untouched
|
|
||||||
if len(r.Raw) > 0 {
|
|
||||||
return retryablehttp.FromRequest(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeader(req, "Accept", "*/*")
|
|
||||||
setHeader(req, "Accept-Language", "en")
|
|
||||||
|
|
||||||
return retryablehttp.FromRequest(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRequest is the basic HTTP request
|
// HTTPRequest is the basic HTTP request
|
||||||
type HTTPRequest struct {
|
type HTTPRequest struct {
|
||||||
Request *retryablehttp.Request
|
Request *retryablehttp.Request
|
||||||
|
|
Loading…
Reference in New Issue