mirror of https://github.com/daffainfo/nuclei.git
Fixed rawhttp header formatting issues
parent
c2bea10a15
commit
d963fc0840
|
@ -76,16 +76,13 @@ func (e *Executer) Execute(input string) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
return results, err
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, callback protocols.OutputEventCallback) error {
|
func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEventCallback) error {
|
||||||
dynamicValues := make(map[string]interface{})
|
dynamicValues := make(map[string]interface{})
|
||||||
_ = e.requests.ExecuteWithResults(input, dynamicValues, nil, func(event *output.InternalWrappedEvent) {
|
err := e.requests.ExecuteWithResults(input, dynamicValues, nil, func(event *output.InternalWrappedEvent) {
|
||||||
for _, operator := range e.operators {
|
for _, operator := range e.operators {
|
||||||
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract)
|
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract)
|
||||||
if matched && result != nil {
|
if matched && result != nil {
|
||||||
|
@ -97,5 +94,5 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package executer
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/gologger"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||||
)
|
)
|
||||||
|
@ -69,7 +70,7 @@ func (e *Executer) Execute(input string) (bool, error) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
gologger.Warning().Msgf("Could not execute request for %s: %s\n", e.options.TemplateID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
|
@ -81,7 +82,7 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
|
||||||
previous := make(map[string]interface{})
|
previous := make(map[string]interface{})
|
||||||
|
|
||||||
for _, req := range e.requests {
|
for _, req := range e.requests {
|
||||||
_ = req.ExecuteWithResults(input, dynamicValues, previous, func(event *output.InternalWrappedEvent) {
|
err := req.ExecuteWithResults(input, dynamicValues, previous, func(event *output.InternalWrappedEvent) {
|
||||||
ID := req.GetID()
|
ID := req.GetID()
|
||||||
if ID != "" {
|
if ID != "" {
|
||||||
builder := &strings.Builder{}
|
builder := &strings.Builder{}
|
||||||
|
@ -98,6 +99,9 @@ func (e *Executer) ExecuteWithResults(input string, callback protocols.OutputEve
|
||||||
}
|
}
|
||||||
callback(event)
|
callback(event)
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
gologger.Warning().Msgf("Could not execute request for %s: %s\n", e.options.TemplateID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package interactsh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/karlseguin/ccache"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/projectdiscovery/interactsh/pkg/client"
|
||||||
|
"github.com/projectdiscovery/interactsh/pkg/server"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/internal/progress"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
|
||||||
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is a wrapped client for interactsh server.
|
||||||
|
type Client struct {
|
||||||
|
// interactsh is a client for interactsh server.
|
||||||
|
interactsh *client.Client
|
||||||
|
// requests is a stored cache for interactsh-url->request-event data.
|
||||||
|
requests *ccache.Cache
|
||||||
|
|
||||||
|
dotHostname string
|
||||||
|
eviction time.Duration
|
||||||
|
pollDuration time.Duration
|
||||||
|
cooldownDuration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options contains configuration options for interactsh nuclei integration.
|
||||||
|
type Options struct {
|
||||||
|
// ServerURL is the URL of the interactsh server.
|
||||||
|
ServerURL string
|
||||||
|
// CacheSize is the numbers of requests to keep track of at a time.
|
||||||
|
// Older items are discarded in LRU manner in favour of new requests.
|
||||||
|
CacheSize int64
|
||||||
|
// Eviction is the period of time after which to automatically discard
|
||||||
|
// interaction requests.
|
||||||
|
Eviction time.Duration
|
||||||
|
// CooldownPeriod is additional time to wait for interactions after closing
|
||||||
|
// of the poller.
|
||||||
|
ColldownPeriod time.Duration
|
||||||
|
// PollDuration is the time to wait before each poll to the server for interactions.
|
||||||
|
PollDuration time.Duration
|
||||||
|
// Output is the output writer for nuclei
|
||||||
|
Output output.Writer
|
||||||
|
// Progress is the nuclei progress bar implementation.
|
||||||
|
Progress *progress.Progress
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new interactsh server client
|
||||||
|
func New(options *Options) (*Client, error) {
|
||||||
|
parsed, err := url.Parse(options.ServerURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not parse server url")
|
||||||
|
}
|
||||||
|
|
||||||
|
interactsh, err := client.New(&client.Options{
|
||||||
|
ServerURL: options.ServerURL,
|
||||||
|
PersistentSession: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not create client")
|
||||||
|
}
|
||||||
|
configure := ccache.Configure()
|
||||||
|
configure = configure.MaxSize(options.CacheSize)
|
||||||
|
cache := ccache.New(configure)
|
||||||
|
|
||||||
|
client := &Client{
|
||||||
|
interactsh: interactsh,
|
||||||
|
eviction: options.Eviction,
|
||||||
|
dotHostname: "." + parsed.Host,
|
||||||
|
requests: cache,
|
||||||
|
pollDuration: options.PollDuration,
|
||||||
|
cooldownDuration: options.ColldownPeriod,
|
||||||
|
}
|
||||||
|
client.interactsh.StartPolling(client.pollDuration, func(interaction *server.Interaction) {
|
||||||
|
item := client.requests.Get(interaction.UniqueID)
|
||||||
|
if item == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, ok := item.Value().(*internalRequestEvent)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.requests.Delete(interaction.UniqueID)
|
||||||
|
|
||||||
|
data.event.OperatorsResult = &operators.Result{
|
||||||
|
Matches: map[string]struct{}{strings.ToLower(interaction.Protocol): {}},
|
||||||
|
}
|
||||||
|
data.event.Results = data.makeResultFunc(data.event)
|
||||||
|
for _, result := range data.event.Results {
|
||||||
|
result.Interaction = interaction
|
||||||
|
options.Output.Write(result)
|
||||||
|
options.Progress.IncrementMatched()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL returns a new URL that can be interacted with
|
||||||
|
func (c *Client) URL() string {
|
||||||
|
return c.interactsh.URL()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the interactsh clients after waiting for cooldown period.
|
||||||
|
func (c *Client) Close() {
|
||||||
|
if c.cooldownDuration > 0 {
|
||||||
|
time.Sleep(c.cooldownDuration)
|
||||||
|
}
|
||||||
|
c.interactsh.StopPolling()
|
||||||
|
c.interactsh.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeResultEventFunc is a result making function for nuclei
|
||||||
|
type MakeResultEventFunc func(wrapped *output.InternalWrappedEvent) []*output.ResultEvent
|
||||||
|
|
||||||
|
type internalRequestEvent struct {
|
||||||
|
makeResultFunc MakeResultEventFunc
|
||||||
|
event *output.InternalWrappedEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestEvent is the event for a network request sent by nuclei.
|
||||||
|
func (c *Client) RequestEvent(interactshURL string, event *output.InternalWrappedEvent, makeResult MakeResultEventFunc) {
|
||||||
|
id := strings.TrimSuffix(interactshURL, c.dotHostname)
|
||||||
|
c.requests.Set(id, &internalRequestEvent{makeResultFunc: makeResult, event: event}, c.eviction)
|
||||||
|
}
|
|
@ -67,7 +67,7 @@ type Request struct {
|
||||||
options *protocols.ExecuterOptions
|
options *protocols.ExecuterOptions
|
||||||
attackType generators.Type
|
attackType generators.Type
|
||||||
totalRequests int
|
totalRequests int
|
||||||
customHeaders []string
|
customHeaders map[string]string
|
||||||
generator *generators.Generator // optional, only enabled when using payloads
|
generator *generators.Generator // optional, only enabled when using payloads
|
||||||
httpClient *retryablehttp.Client
|
httpClient *retryablehttp.Client
|
||||||
rawhttpClient *rawhttp.Client
|
rawhttpClient *rawhttp.Client
|
||||||
|
@ -89,10 +89,15 @@ func (r *Request) Compile(options *protocols.ExecuterOptions) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "could not get dns client")
|
return errors.Wrap(err, "could not get dns client")
|
||||||
}
|
}
|
||||||
|
r.customHeaders = make(map[string]string)
|
||||||
r.httpClient = client
|
r.httpClient = client
|
||||||
r.options = options
|
r.options = options
|
||||||
for _, option := range r.options.Options.CustomHeaders {
|
for _, option := range r.options.Options.CustomHeaders {
|
||||||
r.customHeaders = append(r.customHeaders, option)
|
parts := strings.SplitN(option, ":", 1)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.customHeaders[parts[0]] = strings.TrimSpace(parts[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.Raw) > 0 {
|
if len(r.Raw) > 0 {
|
||||||
|
|
|
@ -59,7 +59,7 @@ func Parse(request, baseURL string, unsafe bool) (*Request, error) {
|
||||||
if unsafe && found {
|
if unsafe && found {
|
||||||
rawRequest.Headers[line] = ""
|
rawRequest.Headers[line] = ""
|
||||||
} else {
|
} else {
|
||||||
rawRequest.Headers[key] = value
|
rawRequest.Headers[strings.TrimSpace(key)] = strings.TrimSpace(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -209,10 +209,7 @@ func (r *Request) ExecuteWithResults(reqURL string, dynamicValues, previous outp
|
||||||
func (r *Request) executeRequest(reqURL string, request *generatedRequest, dynamicvalues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
func (r *Request) executeRequest(reqURL string, request *generatedRequest, dynamicvalues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||||
// Add User-Agent value randomly to the customHeaders slice if `random-agent` flag is given
|
// Add User-Agent value randomly to the customHeaders slice if `random-agent` flag is given
|
||||||
if r.options.Options.RandomAgent {
|
if r.options.Options.RandomAgent {
|
||||||
builder := &strings.Builder{}
|
r.customHeaders["User-Agent"] = uarand.GetRandom()
|
||||||
builder.WriteString("User-Agent: ")
|
|
||||||
builder.WriteString(uarand.GetRandom())
|
|
||||||
r.customHeaders = append(r.customHeaders, builder.String())
|
|
||||||
}
|
}
|
||||||
r.setCustomHeaders(request)
|
r.setCustomHeaders(request)
|
||||||
|
|
||||||
|
@ -356,23 +353,11 @@ const two = 2
|
||||||
|
|
||||||
// setCustomHeaders sets the custom headers for generated request
|
// setCustomHeaders sets the custom headers for generated request
|
||||||
func (e *Request) setCustomHeaders(r *generatedRequest) {
|
func (e *Request) setCustomHeaders(r *generatedRequest) {
|
||||||
for _, customHeader := range e.customHeaders {
|
for k, v := range e.customHeaders {
|
||||||
if customHeader == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should be pre-computed somewhere and done only once
|
|
||||||
tokens := strings.SplitN(customHeader, ":", two)
|
|
||||||
// if it's an invalid header skip it
|
|
||||||
if len(tokens) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
headerName, headerValue := tokens[0], strings.Join(tokens[1:], "")
|
|
||||||
if r.rawRequest != nil {
|
if r.rawRequest != nil {
|
||||||
r.rawRequest.Headers[headerName] = headerValue
|
r.rawRequest.Headers[k] = v
|
||||||
} else {
|
} else {
|
||||||
r.request.Header.Set(strings.TrimSpace(headerName), strings.TrimSpace(headerValue))
|
r.request.Header.Set(strings.TrimSpace(k), strings.TrimSpace(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue