Working interactsh integration

This commit is contained in:
Ice3man543 2021-04-18 16:10:10 +05:30
parent 4c594627a9
commit 33bf306f28
5 changed files with 127 additions and 26 deletions

View File

@ -372,7 +372,10 @@ func (r *Runner) RunEnumeration() {
wgtemplates.Wait() wgtemplates.Wait()
if r.interactsh != nil { if r.interactsh != nil {
r.interactsh.Close() matched := r.interactsh.Close()
if matched {
results.CAS(false, true)
}
} }
r.progress.Stop() r.progress.Stop()

View File

@ -65,6 +65,32 @@ type Result struct {
PayloadValues map[string]interface{} PayloadValues map[string]interface{}
} }
// Merge merges a result structure into the other.
func (r *Result) Merge(result *Result) {
if !r.Matched && result.Matched {
r.Matched = result.Matched
}
if !r.Extracted && result.Extracted {
r.Extracted = result.Extracted
}
for k, v := range result.Matches {
r.Matches[k] = v
}
for k, v := range result.Extracts {
r.Extracts[k] = v
}
for _, v := range result.OutputExtracts {
r.OutputExtracts = append(r.OutputExtracts, v)
}
for k, v := range result.DynamicValues {
r.DynamicValues[k] = v
}
for k, v := range result.PayloadValues {
r.PayloadValues[k] = v
}
}
// MatchFunc performs matching operation for a matcher on model and returns true or false. // MatchFunc performs matching operation for a matcher on model and returns true or false.
type MatchFunc func(data map[string]interface{}, matcher *matchers.Matcher) bool type MatchFunc func(data map[string]interface{}, matcher *matchers.Matcher) bool
@ -81,6 +107,7 @@ func (r *Operators) Execute(data map[string]interface{}, match MatchFunc, extrac
Extracts: make(map[string][]string), Extracts: make(map[string][]string),
DynamicValues: make(map[string]interface{}), DynamicValues: make(map[string]interface{}),
} }
// Start with the extractors first and evaluate them. // Start with the extractors first and evaluate them.
for _, extractor := range r.Extractors { for _, extractor := range r.Extractors {
var extractorResults []string var extractorResults []string

View File

@ -22,6 +22,7 @@ type Client struct {
// requests is a stored cache for interactsh-url->request-event data. // requests is a stored cache for interactsh-url->request-event data.
requests *ccache.Cache requests *ccache.Cache
matched bool
dotHostname string dotHostname string
eviction time.Duration eviction time.Duration
pollDuration time.Duration pollDuration time.Duration
@ -77,24 +78,38 @@ func New(options *Options) (*Client, error) {
pollDuration: options.PollDuration, pollDuration: options.PollDuration,
cooldownDuration: options.ColldownPeriod, cooldownDuration: options.ColldownPeriod,
} }
interactClient.interactsh.StartPolling(interactClient.pollDuration, func(interaction *server.Interaction) { interactClient.interactsh.StartPolling(interactClient.pollDuration, func(interaction *server.Interaction) {
item := interactClient.requests.Get(interaction.UniqueID) item := interactClient.requests.Get(interaction.UniqueID)
if item == nil { if item == nil {
return return
} }
data, ok := item.Value().(*internalRequestEvent) data, ok := item.Value().(*RequestData)
if !ok { if !ok {
return return
} }
data.Event.InternalEvent["interactsh-protocol"] = interaction.Protocol
data.Event.InternalEvent["interactsh-request"] = interaction.RawRequest
data.Event.InternalEvent["interactsh-response"] = interaction.RawResponse
result, matched := data.Operators.Execute(data.Event.InternalEvent, data.MatchFunc, data.ExtractFunc)
if !matched || result == nil {
return // if we don't match, return
}
interactClient.requests.Delete(interaction.UniqueID) interactClient.requests.Delete(interaction.UniqueID)
data.event.OperatorsResult = &operators.Result{ if data.Event.OperatorsResult != nil {
Matches: map[string]struct{}{strings.ToLower(interaction.Protocol): {}}, data.Event.OperatorsResult.Merge(result)
} else {
data.Event.OperatorsResult = result
} }
data.event.Results = data.makeResultFunc(data.event) data.Event.Results = data.MakeResultFunc(data.Event)
for _, result := range data.event.Results { for _, result := range data.Event.Results {
result.Interaction = interaction result.Interaction = interaction
_ = options.Output.Write(result) _ = options.Output.Write(result)
if !interactClient.matched {
interactClient.matched = true
}
options.Progress.IncrementMatched() options.Progress.IncrementMatched()
} }
}) })
@ -107,12 +122,13 @@ func (c *Client) URL() string {
} }
// Close closes the interactsh clients after waiting for cooldown period. // Close closes the interactsh clients after waiting for cooldown period.
func (c *Client) Close() { func (c *Client) Close() bool {
if c.cooldownDuration > 0 { if c.cooldownDuration > 0 {
time.Sleep(c.cooldownDuration) time.Sleep(c.cooldownDuration)
} }
c.interactsh.StopPolling() c.interactsh.StopPolling()
c.interactsh.Close() c.interactsh.Close()
return c.matched
} }
// ReplaceMarkers replaces the {{interactsh-url}} placeholders to actual // ReplaceMarkers replaces the {{interactsh-url}} placeholders to actual
@ -133,13 +149,36 @@ func (c *Client) ReplaceMarkers(data, interactshURL string) string {
// MakeResultEventFunc is a result making function for nuclei // MakeResultEventFunc is a result making function for nuclei
type MakeResultEventFunc func(wrapped *output.InternalWrappedEvent) []*output.ResultEvent type MakeResultEventFunc func(wrapped *output.InternalWrappedEvent) []*output.ResultEvent
type internalRequestEvent struct { // RequestData contains data for a request event
makeResultFunc MakeResultEventFunc type RequestData struct {
event *output.InternalWrappedEvent MakeResultFunc MakeResultEventFunc
Event *output.InternalWrappedEvent
Operators *operators.Operators
MatchFunc operators.MatchFunc
ExtractFunc operators.ExtractFunc
} }
// RequestEvent is the event for a network request sent by nuclei. // RequestEvent is the event for a network request sent by nuclei.
func (c *Client) RequestEvent(interactshURL string, event *output.InternalWrappedEvent, makeResult MakeResultEventFunc) { func (c *Client) RequestEvent(interactshURL string, data *RequestData) {
id := strings.TrimSuffix(interactshURL, c.dotHostname) id := strings.TrimSuffix(interactshURL, c.dotHostname)
c.requests.Set(id, &internalRequestEvent{makeResultFunc: makeResult, event: event}, c.eviction) c.requests.Set(id, data, c.eviction)
}
// HasMatchers returns true if an operator has interactsh part
// matchers or extractors.
//
// Used by requests to show result or not depending on presence of interact.sh
// data part matchers.
func HasMatchers(operators *operators.Operators) bool {
for _, matcher := range operators.Matchers {
if strings.HasPrefix(matcher.Part, "interactsh-") {
return true
}
}
for _, matcher := range operators.Extractors {
if strings.HasPrefix(matcher.Part, "interactsh-") {
return true
}
}
return false
} }

View File

@ -17,6 +17,7 @@ import (
"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"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/tostring" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/tostring"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
"github.com/projectdiscovery/rawhttp" "github.com/projectdiscovery/rawhttp"
@ -219,8 +220,16 @@ func (r *Request) ExecuteWithResults(reqURL string, dynamicValues, previous outp
gotOutput = true gotOutput = true
dynamicValues = generators.MergeMaps(dynamicValues, event.OperatorsResult.DynamicValues) dynamicValues = generators.MergeMaps(dynamicValues, event.OperatorsResult.DynamicValues)
} }
if r.options.Interactsh != nil { if interactsh.HasMatchers(r.CompiledOperators) {
r.options.Interactsh.RequestEvent(interactURL, event, r.MakeResultEvent) if r.options.Interactsh != nil {
r.options.Interactsh.RequestEvent(interactURL, &interactsh.RequestData{
MakeResultFunc: r.MakeResultEvent,
Event: event,
Operators: r.CompiledOperators,
MatchFunc: r.Match,
ExtractFunc: r.Extract,
})
}
} }
callback(event) callback(event)
}, requestCount) }, requestCount)
@ -398,14 +407,16 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, previ
} }
event := &output.InternalWrappedEvent{InternalEvent: outputEvent} event := &output.InternalWrappedEvent{InternalEvent: outputEvent}
if r.CompiledOperators != nil { if !interactsh.HasMatchers(r.CompiledOperators) {
var ok bool if r.CompiledOperators != nil {
event.OperatorsResult, ok = r.CompiledOperators.Execute(finalEvent, r.Match, r.Extract) var ok bool
if ok && event.OperatorsResult != nil { event.OperatorsResult, ok = r.CompiledOperators.Execute(finalEvent, r.Match, r.Extract)
event.OperatorsResult.PayloadValues = request.meta if ok && event.OperatorsResult != nil {
event.Results = r.MakeResultEvent(event) event.OperatorsResult.PayloadValues = request.meta
event.Results = r.MakeResultEvent(event)
}
event.InternalEvent = outputEvent
} }
event.InternalEvent = outputEvent
} }
callback(event) callback(event)
return nil return nil

View File

@ -13,6 +13,7 @@ import (
"github.com/projectdiscovery/gologger" "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"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer" "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
) )
@ -77,6 +78,11 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
defer conn.Close() defer conn.Close()
_ = conn.SetReadDeadline(time.Now().Add(time.Duration(r.options.Options.Timeout) * time.Second)) _ = conn.SetReadDeadline(time.Now().Add(time.Duration(r.options.Options.Timeout) * time.Second))
var interactURL string
if r.options.Interactsh != nil {
interactURL = r.options.Interactsh.URL()
}
responseBuilder := &strings.Builder{} responseBuilder := &strings.Builder{}
reqBuilder := &strings.Builder{} reqBuilder := &strings.Builder{}
@ -88,6 +94,9 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
case "hex": case "hex":
data, err = hex.DecodeString(input.Data) data, err = hex.DecodeString(input.Data)
default: default:
if r.options.Interactsh != nil {
input.Data = r.options.Interactsh.ReplaceMarkers(input.Data, interactURL)
}
data = []byte(input.Data) data = []byte(input.Data)
} }
if err != nil { if err != nil {
@ -150,11 +159,23 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
} }
event := &output.InternalWrappedEvent{InternalEvent: outputEvent} event := &output.InternalWrappedEvent{InternalEvent: outputEvent}
if r.CompiledOperators != nil { if !interactsh.HasMatchers(r.CompiledOperators) {
result, ok := r.CompiledOperators.Execute(outputEvent, r.Match, r.Extract) if r.CompiledOperators != nil {
if ok && result != nil { result, ok := r.CompiledOperators.Execute(outputEvent, r.Match, r.Extract)
event.OperatorsResult = result if ok && result != nil {
event.Results = r.MakeResultEvent(event) event.OperatorsResult = result
event.Results = r.MakeResultEvent(event)
}
}
} else {
if r.options.Interactsh != nil {
r.options.Interactsh.RequestEvent(interactURL, &interactsh.RequestData{
MakeResultFunc: r.MakeResultEvent,
Event: event,
Operators: r.CompiledOperators,
MatchFunc: r.Match,
ExtractFunc: r.Extract,
})
} }
} }
callback(event) callback(event)