mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 20:25:27 +00:00
Working interactsh integration
This commit is contained in:
parent
4c594627a9
commit
33bf306f28
@ -372,7 +372,10 @@ func (r *Runner) RunEnumeration() {
|
||||
wgtemplates.Wait()
|
||||
|
||||
if r.interactsh != nil {
|
||||
r.interactsh.Close()
|
||||
matched := r.interactsh.Close()
|
||||
if matched {
|
||||
results.CAS(false, true)
|
||||
}
|
||||
}
|
||||
r.progress.Stop()
|
||||
|
||||
|
||||
@ -65,6 +65,32 @@ type Result struct {
|
||||
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.
|
||||
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),
|
||||
DynamicValues: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Start with the extractors first and evaluate them.
|
||||
for _, extractor := range r.Extractors {
|
||||
var extractorResults []string
|
||||
|
||||
@ -22,6 +22,7 @@ type Client struct {
|
||||
// requests is a stored cache for interactsh-url->request-event data.
|
||||
requests *ccache.Cache
|
||||
|
||||
matched bool
|
||||
dotHostname string
|
||||
eviction time.Duration
|
||||
pollDuration time.Duration
|
||||
@ -77,24 +78,38 @@ func New(options *Options) (*Client, error) {
|
||||
pollDuration: options.PollDuration,
|
||||
cooldownDuration: options.ColldownPeriod,
|
||||
}
|
||||
|
||||
interactClient.interactsh.StartPolling(interactClient.pollDuration, func(interaction *server.Interaction) {
|
||||
item := interactClient.requests.Get(interaction.UniqueID)
|
||||
if item == nil {
|
||||
return
|
||||
}
|
||||
data, ok := item.Value().(*internalRequestEvent)
|
||||
data, ok := item.Value().(*RequestData)
|
||||
if !ok {
|
||||
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)
|
||||
|
||||
data.event.OperatorsResult = &operators.Result{
|
||||
Matches: map[string]struct{}{strings.ToLower(interaction.Protocol): {}},
|
||||
if data.Event.OperatorsResult != nil {
|
||||
data.Event.OperatorsResult.Merge(result)
|
||||
} else {
|
||||
data.Event.OperatorsResult = result
|
||||
}
|
||||
data.event.Results = data.makeResultFunc(data.event)
|
||||
for _, result := range data.event.Results {
|
||||
data.Event.Results = data.MakeResultFunc(data.Event)
|
||||
for _, result := range data.Event.Results {
|
||||
result.Interaction = interaction
|
||||
_ = options.Output.Write(result)
|
||||
if !interactClient.matched {
|
||||
interactClient.matched = true
|
||||
}
|
||||
options.Progress.IncrementMatched()
|
||||
}
|
||||
})
|
||||
@ -107,12 +122,13 @@ func (c *Client) URL() string {
|
||||
}
|
||||
|
||||
// Close closes the interactsh clients after waiting for cooldown period.
|
||||
func (c *Client) Close() {
|
||||
func (c *Client) Close() bool {
|
||||
if c.cooldownDuration > 0 {
|
||||
time.Sleep(c.cooldownDuration)
|
||||
}
|
||||
c.interactsh.StopPolling()
|
||||
c.interactsh.Close()
|
||||
return c.matched
|
||||
}
|
||||
|
||||
// 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
|
||||
type MakeResultEventFunc func(wrapped *output.InternalWrappedEvent) []*output.ResultEvent
|
||||
|
||||
type internalRequestEvent struct {
|
||||
makeResultFunc MakeResultEventFunc
|
||||
event *output.InternalWrappedEvent
|
||||
// RequestData contains data for a request event
|
||||
type RequestData struct {
|
||||
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.
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||
"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/http/httpclientpool"
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
@ -219,8 +220,16 @@ func (r *Request) ExecuteWithResults(reqURL string, dynamicValues, previous outp
|
||||
gotOutput = true
|
||||
dynamicValues = generators.MergeMaps(dynamicValues, event.OperatorsResult.DynamicValues)
|
||||
}
|
||||
if r.options.Interactsh != nil {
|
||||
r.options.Interactsh.RequestEvent(interactURL, event, r.MakeResultEvent)
|
||||
if interactsh.HasMatchers(r.CompiledOperators) {
|
||||
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)
|
||||
}, requestCount)
|
||||
@ -398,14 +407,16 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, previ
|
||||
}
|
||||
|
||||
event := &output.InternalWrappedEvent{InternalEvent: outputEvent}
|
||||
if r.CompiledOperators != nil {
|
||||
var ok bool
|
||||
event.OperatorsResult, ok = r.CompiledOperators.Execute(finalEvent, r.Match, r.Extract)
|
||||
if ok && event.OperatorsResult != nil {
|
||||
event.OperatorsResult.PayloadValues = request.meta
|
||||
event.Results = r.MakeResultEvent(event)
|
||||
if !interactsh.HasMatchers(r.CompiledOperators) {
|
||||
if r.CompiledOperators != nil {
|
||||
var ok bool
|
||||
event.OperatorsResult, ok = r.CompiledOperators.Execute(finalEvent, r.Match, r.Extract)
|
||||
if ok && event.OperatorsResult != nil {
|
||||
event.OperatorsResult.PayloadValues = request.meta
|
||||
event.Results = r.MakeResultEvent(event)
|
||||
}
|
||||
event.InternalEvent = outputEvent
|
||||
}
|
||||
event.InternalEvent = outputEvent
|
||||
}
|
||||
callback(event)
|
||||
return nil
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
||||
"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"
|
||||
)
|
||||
|
||||
@ -77,6 +78,11 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
|
||||
defer conn.Close()
|
||||
_ = 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{}
|
||||
reqBuilder := &strings.Builder{}
|
||||
|
||||
@ -88,6 +94,9 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
|
||||
case "hex":
|
||||
data, err = hex.DecodeString(input.Data)
|
||||
default:
|
||||
if r.options.Interactsh != nil {
|
||||
input.Data = r.options.Interactsh.ReplaceMarkers(input.Data, interactURL)
|
||||
}
|
||||
data = []byte(input.Data)
|
||||
}
|
||||
if err != nil {
|
||||
@ -150,11 +159,23 @@ func (r *Request) executeAddress(actualAddress, address, input string, shouldUse
|
||||
}
|
||||
|
||||
event := &output.InternalWrappedEvent{InternalEvent: outputEvent}
|
||||
if r.CompiledOperators != nil {
|
||||
result, ok := r.CompiledOperators.Execute(outputEvent, r.Match, r.Extract)
|
||||
if ok && result != nil {
|
||||
event.OperatorsResult = result
|
||||
event.Results = r.MakeResultEvent(event)
|
||||
if !interactsh.HasMatchers(r.CompiledOperators) {
|
||||
if r.CompiledOperators != nil {
|
||||
result, ok := r.CompiledOperators.Execute(outputEvent, r.Match, r.Extract)
|
||||
if ok && result != nil {
|
||||
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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user