Added directory support

dev
Ice3man543 2020-06-29 17:43:08 +05:30
parent 2b109b5a82
commit b3c52d941f
5 changed files with 152 additions and 66 deletions

View File

@ -3,11 +3,11 @@ package runner
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"sync"
@ -94,11 +94,12 @@ func (r *Runner) RunEnumeration() {
// Check if the template is an absolute path or relative path.
// If the path is absolute, use it. Otherwise,
if r.isRelative(r.options.Templates) {
r.options.Templates, err = r.resolvePath(r.options.Templates)
newPath, err := r.resolvePath(r.options.Templates)
if err != nil {
gologger.Errorf("Could not find template file '%s': %s\n", r.options.Templates, err)
return
}
r.options.Templates = newPath
}
// Single yaml provided
@ -329,42 +330,116 @@ func (r *Runner) ProcessWorkflowWithList(workflow *workflows.Workflow) {
if text == "" {
continue
}
r.ProcessWorkflow(workflow, text)
if err := r.ProcessWorkflow(workflow, text); err != nil {
gologger.Warningf("Could not run workflow for %s: %s\n", text, err)
}
}
}
// ProcessWorkflow towards an URL
func (r *Runner) ProcessWorkflow(workflow *workflows.Workflow, URL string) error {
script := tengo.NewScript([]byte(workflow.Logic))
for name, value := range workflow.Variables {
var writer *bufio.Writer
if r.output != nil {
writer = bufio.NewWriter(r.output)
defer writer.Flush()
}
templatePath := path.Join(r.options.TemplatesDirectory, value)
template, err := templates.ParseTemplate(templatePath)
// Check if the template is an absolute path or relative path.
// If the path is absolute, use it. Otherwise,
if r.isRelative(value) {
newPath, err := r.resolvePath(value)
if err != nil {
gologger.Errorf("Could not parse template file '%s': %s\n", value, err)
return err
}
value = newPath
}
httpOptions := &executor.HTTPOptions{
// Single yaml provided
var templatesList []*workflows.Template
if strings.HasSuffix(value, ".yaml") {
t, err := templates.Parse(value)
if err != nil {
return err
}
template := &workflows.Template{}
if len(t.RequestsHTTP) > 0 {
template.HTTPOptions = &executor.HTTPOptions{
Debug: r.options.Debug,
Writer: writer,
Template: template,
Template: t,
Timeout: r.options.Timeout,
Retries: r.options.Retries,
ProxyURL: r.options.ProxyURL,
ProxySocksURL: r.options.ProxySocksURL,
CustomHeaders: r.options.CustomHeaders,
}
dnsOptions := &executor.DNSOptions{
} else if len(t.RequestsDNS) > 0 {
template.DNSOptions = &executor.DNSOptions{
Debug: r.options.Debug,
Template: template,
Template: t,
Writer: writer,
}
script.Add(name, &workflows.NucleiVar{HTTPOptions: httpOptions, DNSOptions: dnsOptions, URL: URL})
}
if template.DNSOptions != nil || template.HTTPOptions != nil {
templatesList = append(templatesList, template)
}
} else {
matches := []string{}
err := godirwalk.Walk(value, &godirwalk.Options{
Callback: func(path string, d *godirwalk.Dirent) error {
if !d.IsDir() && strings.HasSuffix(path, ".yaml") {
matches = append(matches, path)
}
return nil
},
ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
return godirwalk.SkipNode
},
Unsorted: true,
})
if err != nil {
return err
}
// 0 matches means no templates were found in directory
if len(matches) == 0 {
return errors.New("no match found in the directory")
}
for _, match := range matches {
t, err := templates.Parse(match)
if err != nil {
return err
}
template := &workflows.Template{}
if len(t.RequestsHTTP) > 0 {
template.HTTPOptions = &executor.HTTPOptions{
Debug: r.options.Debug,
Writer: writer,
Template: t,
Timeout: r.options.Timeout,
Retries: r.options.Retries,
ProxyURL: r.options.ProxyURL,
ProxySocksURL: r.options.ProxySocksURL,
CustomHeaders: r.options.CustomHeaders,
}
} else if len(t.RequestsDNS) > 0 {
template.DNSOptions = &executor.DNSOptions{
Debug: r.options.Debug,
Template: t,
Writer: writer,
}
}
if template.DNSOptions != nil || template.HTTPOptions != nil {
templatesList = append(templatesList, template)
}
}
}
script.Add(name, &workflows.NucleiVar{Templates: templatesList, URL: URL})
}
_, err := script.RunContext(context.Background())
@ -372,13 +447,12 @@ func (r *Runner) ProcessWorkflow(workflow *workflows.Workflow, URL string) error
gologger.Errorf("Could not execute workflow '%s': %s\n", workflow.ID, err)
return err
}
return nil
}
func (r *Runner) parse(file string) interface{} {
// check if it's a template
template, errTemplate := templates.ParseTemplate(r.options.Templates)
template, errTemplate := templates.Parse(r.options.Templates)
if errTemplate == nil {
return template
}
@ -388,6 +462,5 @@ func (r *Runner) parse(file string) interface{} {
if errWorkflow == nil {
return workflow
}
return nil
}

View File

@ -10,8 +10,8 @@ import (
"gopkg.in/yaml.v2"
)
// ParseTemplate parses a yaml request template file
func ParseTemplate(file string) (*Template, error) {
// Parse parses a yaml request template file
func Parse(file string) (*Template, error) {
template := &Template{}
f, err := os.Open(file)
@ -25,6 +25,7 @@ func ParseTemplate(file string) (*Template, error) {
}
defer f.Close()
// If no requests, and it is also not a workflow, return error.
if len(template.RequestsHTTP)+len(template.RequestsDNS) <= 0 {
return nil, errors.New("No requests defined")
}

View File

@ -11,9 +11,9 @@ type Template struct {
// Info contains information about the template
Info Info `yaml:"info"`
// RequestHTTP contains the http request to make in the template
RequestsHTTP []*requests.HTTPRequest `yaml:"requests"`
RequestsHTTP []*requests.HTTPRequest `yaml:"requests,omitempty"`
// RequestDNS contains the dns request to make in the template
RequestsDNS []*requests.DNSRequest `yaml:"dns"`
RequestsDNS []*requests.DNSRequest `yaml:"dns,omitempty"`
}
// Info contains information about the request template
@ -25,10 +25,3 @@ type Info struct {
// Severity optionally describes the severity of the template
Severity string `yaml:"severity,omitempty"`
}
// Levels of severity for a request template
const (
SeverityHigh = "high"
SeverityMedium = "medium"
SeverityLow = "low"
)

View File

@ -2,15 +2,21 @@ package workflows
import (
"github.com/d5/tengo/v2"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/pkg/executor"
)
// NucleiVar within the scripting engine
type NucleiVar struct {
tengo.ObjectImpl
Templates []*Template
URL string
}
// Template contains HTTPOptions and DNSOptions for a single template
type Template struct {
HTTPOptions *executor.HTTPOptions
DNSOptions *executor.DNSOptions
URL string
}
// TypeName of the variable
@ -25,34 +31,45 @@ func (n *NucleiVar) CanCall() bool {
// Call logic - actually it doesn't require arguments
func (n *NucleiVar) Call(args ...tengo.Object) (ret tengo.Object, err error) {
for _, request := range n.HTTPOptions.Template.RequestsHTTP {
n.HTTPOptions.HTTPRequest = request
httpExecutor, err := executor.NewHTTPExecutor(n.HTTPOptions)
var gotResult bool
for _, template := range n.Templates {
if template.HTTPOptions != nil {
for _, request := range template.HTTPOptions.Template.RequestsHTTP {
template.HTTPOptions.HTTPRequest = request
httpExecutor, err := executor.NewHTTPExecutor(template.HTTPOptions)
if err != nil {
return nil, err
gologger.Warningf("Could not compile request for template '%s': %s\n", template.HTTPOptions.Template.ID, err)
continue
}
err = httpExecutor.ExecuteHTTP(n.URL)
if err != nil {
return nil, err
gologger.Warningf("Could not send request for template '%s': %s\n", template.HTTPOptions.Template.ID, err)
continue
}
if httpExecutor.GotResults() {
return tengo.TrueValue, nil
gotResult = true
}
}
return tengo.FalseValue, nil
}
for _, request := range n.DNSOptions.Template.RequestsDNS {
n.DNSOptions.DNSRequest = request
dnsExecutor := executor.NewDNSExecutor(n.DNSOptions)
if template.DNSOptions != nil {
for _, request := range template.DNSOptions.Template.RequestsDNS {
template.DNSOptions.DNSRequest = request
dnsExecutor := executor.NewDNSExecutor(template.DNSOptions)
err = dnsExecutor.ExecuteDNS(n.URL)
if err != nil {
return nil, err
gologger.Warningf("Could not compile request for template '%s': %s\n", template.HTTPOptions.Template.ID, err)
continue
}
if dnsExecutor.GotResults() {
gotResult = true
}
}
}
}
if gotResult {
return tengo.TrueValue, nil
}
return tengo.FalseValue, nil
}
return nil, nil
}

View File

@ -1,6 +1,6 @@
package workflows
// Workflow is workflow parsed from a yaml file
// Workflow is a workflow to execute with chained requests, etc.
type Workflow struct {
// ID is the unique id for the template
ID string `yaml:"id"`
@ -18,4 +18,6 @@ type Info struct {
Name string `yaml:"name"`
// Author is the name of the author of the workflow
Author string `yaml:"author"`
// Severity optionally describes the severity of the template
Severity string `yaml:"severity,omitempty"`
}