Skip to content

Commit

Permalink
conditional flow support using flow
Browse files Browse the repository at this point in the history
  • Loading branch information
tarunKoyalwar committed Aug 7, 2023
1 parent bc4ec8d commit 816d4cc
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 18 deletions.
3 changes: 2 additions & 1 deletion v2/pkg/protocols/common/executer/executer.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ func (e *Executer) executeFlow(input *contextargs.Context, callback protocols.Ou
if req.Type() == types.MultiProtocol {
// multiprotocol execution is also mutually exclusive with flow and is slightly advanced version of executeWithCallbac()
// if request type is multiprotocol , then array does not contain any other request type
gologger.Info().Msgf("reqtype is %v", req.Type().String())
return e.executeWithCallback(input, nil, nil)
}
switch req.Type() {
Expand All @@ -205,6 +204,8 @@ func (e *Executer) executeFlow(input *contextargs.Context, callback protocols.Ou
allprotos[types.WHOISProtocol.String()] = append(allprotos[types.WHOISProtocol.String()], req)
case types.CodeProtocol:
allprotos[types.CodeProtocol.String()] = append(allprotos[types.CodeProtocol.String()], req)
default:
gologger.Error().Msgf("invalid request type %s", req.Type().String())
}
}
flow := &FlowExecutor{
Expand Down
45 changes: 31 additions & 14 deletions v2/pkg/protocols/common/executer/flow_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/vardump"
"github.com/projectdiscovery/nuclei/v2/pkg/types"
errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file"
mapsutil "github.com/projectdiscovery/utils/maps"
Expand All @@ -32,7 +33,7 @@ type FlowExecutor struct {
results *atomic.Bool
jsVM *goja.Runtime
program *goja.Program
protoFunctions map[string]func(id ...string) // reqFunctions contains functions that allow executing requests/protocols from js
protoFunctions map[string]func(call goja.FunctionCall) goja.Value // reqFunctions contains functions that allow executing requests/protocols from js
}

// Init initializes the flow executor all dependencies
Expand Down Expand Up @@ -78,7 +79,7 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
// ------

// ---- define callback functions/objects----
f.protoFunctions = map[string]func(id ...string){}
f.protoFunctions = map[string]func(call goja.FunctionCall) goja.Value{}
compileErrors := []error{}

for proto, requests := range f.allProtocols {
Expand All @@ -96,12 +97,18 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
counter++
}
// ---define hook that allows protocol/request execution from js-----
f.protoFunctions[proto] = func(ids ...string) {
f.protoFunctions[proto] = func(call goja.FunctionCall) goja.Value {
defer func() {
// to avoid polling update template variables everytime we execute a protocol
var m map[string]interface{} = f.options.TemplateCtx.GetAll()
_ = f.jsVM.Set("template", m)
}()
ids := []string{}
for _, v := range call.Arguments {
ids = append(ids, types.ToString(v.Export()))
}
matcherStatus := &atomic.Bool{} // due to interactsh matcher polling logic this needs to be atomic bool

// if no id is passed execute all requests in sequence
if len(ids) == 0 {
// execution logic for http()/dns() etc
Expand All @@ -114,9 +121,12 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
// export dynamic values from operators (i.e internal:true)
// add add it to template context
// this is a conflicting behaviour with iterate-all
if result.HasOperatorResult() && len(result.OperatorsResult.DynamicValues) > 0 {
for k, v := range result.OperatorsResult.DynamicValues {
f.options.TemplateCtx.Set(k, v)
if result.HasOperatorResult() {
matcherStatus.CompareAndSwap(false, result.OperatorsResult.Matched)
if len(result.OperatorsResult.DynamicValues) > 0 {
for k, v := range result.OperatorsResult.DynamicValues {
f.options.TemplateCtx.Set(k, v)
}
}
}
}
Expand All @@ -129,10 +139,10 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
id, _ = reqMap.GetKeyWithValue(req)
}
_ = f.allErrs.Set(id, err)
return
return f.jsVM.ToValue(matcherStatus.Load())
}
}
return
return f.jsVM.ToValue(matcherStatus.Load())
}

// execution logic for http("0") or http("get-aws-vpcs")
Expand All @@ -142,19 +152,22 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
gologger.Error().Msgf("invalid request id '%s' provided", id)
// compile error
compileErrors = append(compileErrors, ErrInvalidRequestID.Msgf(id))
return
return f.jsVM.ToValue(matcherStatus.Load())
}
err := req.ExecuteWithResults(f.input, output.InternalEvent(f.options.TemplateCtx.GetAll()), nil, func(result *output.InternalWrappedEvent) {
if result != nil {
f.results.CompareAndSwap(false, true)
callback(result)
// export dynamic values from operators (i.e internal:true)
// add add it to template context
if result.HasOperatorResult() && len(result.OperatorsResult.DynamicValues) > 0 {
for k, v := range result.OperatorsResult.DynamicValues {
f.options.TemplateCtx.Set(k, v)
if result.HasOperatorResult() {
matcherStatus.CompareAndSwap(false, result.OperatorsResult.Matched)
if len(result.OperatorsResult.DynamicValues) > 0 {
for k, v := range result.OperatorsResult.DynamicValues {
f.options.TemplateCtx.Set(k, v)
}
_ = f.jsVM.Set("template", f.options.TemplateCtx.GetAll())
}
_ = f.jsVM.Set("template", f.options.TemplateCtx.GetAll())
}
}
})
Expand All @@ -163,6 +176,7 @@ func (f *FlowExecutor) Compile(callback func(event *output.InternalWrappedEvent)
_ = f.allErrs.Set(index, err)
}
}
return f.jsVM.ToValue(matcherStatus.Load())
}
}

Expand Down Expand Up @@ -235,10 +249,13 @@ func (f *FlowExecutor) RegisterBuiltInFunctions() error {
// Execute executes the flow
func (f *FlowExecutor) Execute() (bool, error) {
// pass flow and execute the js vm and handle errors
_, err := f.jsVM.RunProgram(f.program)
value, err := f.jsVM.RunProgram(f.program)
if err != nil {
return false, errorutil.NewWithErr(err).Msgf("failed to execute flow\n%v\n", f.options.Flow)
}
if value != nil {
return value.ToBoolean(), nil
}
return f.results.Load(), nil
}

Expand Down
28 changes: 28 additions & 0 deletions v2/pkg/protocols/common/executer/testcases/condition-flow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
id: ghost-blog-detection
info:
name: Ghost blog detection
author: pdteam
severity: info


flow: |
dns() && http("0");
dns:
- name: "{{FQDN}}"
type: CNAME

matchers:
- type: word
words:
- "ghost.io"

http:
- method: GET
path:
- "{{BaseURL}}"

matchers:
- type: word
words:
- "ghost.io"
13 changes: 10 additions & 3 deletions v2/pkg/templates/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,32 @@ func (template *Template) compileProtocolRequests(options protocols.ExecutorOpti
switch {
case len(template.RequestsDNS) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsDNS)...)
fallthrough
case len(template.RequestsFile) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsFile)...)
fallthrough
case len(template.RequestsNetwork) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsNetwork)...)
fallthrough
case len(template.RequestsHTTP) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsHTTP)...)
fallthrough
case len(template.RequestsHeadless) > 0 && options.Options.Headless:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsHeadless)...)
fallthrough
case len(template.RequestsSSL) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsSSL)...)
fallthrough
case len(template.RequestsWebsocket) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsWebsocket)...)
fallthrough
case len(template.RequestsWHOIS) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsWHOIS)...)
fallthrough
case len(template.RequestsCode) > 0:
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsCode)...)
}
}
if len(template.RequestsCode) > 0 {
requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsCode)...)
}
template.Executer = executer.NewExecuter(requests, &options)
return nil
}
Expand Down

0 comments on commit 816d4cc

Please sign in to comment.