2020-04-04 10:29:05 +00:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2020-06-26 08:23:54 +00:00
|
|
|
"context"
|
2020-06-29 12:13:08 +00:00
|
|
|
"errors"
|
2020-04-04 10:29:05 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2020-07-16 14:32:42 +00:00
|
|
|
"net/http/cookiejar"
|
2020-04-04 10:29:05 +00:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2020-07-10 07:04:38 +00:00
|
|
|
tengo "github.com/d5/tengo/v2"
|
|
|
|
"github.com/d5/tengo/v2/stdlib"
|
2020-05-02 17:10:52 +00:00
|
|
|
"github.com/karrick/godirwalk"
|
2020-04-04 10:29:05 +00:00
|
|
|
"github.com/projectdiscovery/gologger"
|
2020-07-23 18:19:19 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/internal/progress"
|
2020-07-16 08:57:28 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/executer"
|
2020-07-01 10:47:24 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/requests"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/workflows"
|
2020-04-04 10:29:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Runner is a client for running the enumeration process.
|
|
|
|
type Runner struct {
|
2020-07-23 18:19:19 +00:00
|
|
|
input *os.File
|
|
|
|
inputCount int64
|
|
|
|
|
2020-04-04 12:51:05 +00:00
|
|
|
// output is the output file to write if any
|
|
|
|
output *os.File
|
|
|
|
outputMutex *sync.Mutex
|
2020-04-26 01:18:10 +00:00
|
|
|
|
2020-06-24 22:23:37 +00:00
|
|
|
tempFile string
|
|
|
|
templatesConfig *nucleiConfig
|
2020-04-04 12:51:05 +00:00
|
|
|
// options contains configuration options for runner
|
2020-04-04 10:29:05 +00:00
|
|
|
options *Options
|
2020-07-23 18:19:19 +00:00
|
|
|
|
|
|
|
// progress tracking
|
|
|
|
progress *progress.Progress
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new client for running enumeration process.
|
|
|
|
func New(options *Options) (*Runner, error) {
|
|
|
|
runner := &Runner{
|
2020-04-04 12:51:05 +00:00
|
|
|
outputMutex: &sync.Mutex{},
|
|
|
|
options: options,
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-04-04 11:42:29 +00:00
|
|
|
|
2020-06-24 22:23:37 +00:00
|
|
|
if err := runner.updateTemplates(); err != nil {
|
2020-07-06 07:00:02 +00:00
|
|
|
gologger.Warningf("Could not update templates: %s\n", err)
|
2020-06-24 22:23:37 +00:00
|
|
|
}
|
2020-07-13 22:04:19 +00:00
|
|
|
if (len(options.Templates) == 0 || (options.Targets == "" && !options.Stdin && options.Target == "")) && options.UpdateTemplates {
|
2020-06-24 22:23:37 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2020-04-26 01:18:10 +00:00
|
|
|
// If we have stdin, write it to a new file
|
|
|
|
if options.Stdin {
|
|
|
|
tempInput, err := ioutil.TempFile("", "stdin-input-*")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-04-26 01:30:28 +00:00
|
|
|
if _, err := io.Copy(tempInput, os.Stdin); err != nil {
|
2020-04-26 01:18:10 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
runner.tempFile = tempInput.Name()
|
|
|
|
tempInput.Close()
|
|
|
|
}
|
2020-06-25 16:10:20 +00:00
|
|
|
// If we have single target, write it to a new file
|
|
|
|
if options.Target != "" {
|
|
|
|
tempInput, err := ioutil.TempFile("", "stdin-input-*")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
tempInput.WriteString(options.Target)
|
|
|
|
runner.tempFile = tempInput.Name()
|
|
|
|
tempInput.Close()
|
|
|
|
}
|
2020-04-26 01:18:10 +00:00
|
|
|
|
2020-07-23 18:19:19 +00:00
|
|
|
// Setup input, handle a list of hosts as argument
|
|
|
|
var err error
|
|
|
|
if options.Targets != "" {
|
|
|
|
runner.input, err = os.Open(options.Targets)
|
|
|
|
} else if options.Stdin || options.Target != "" {
|
|
|
|
runner.input, err = os.Open(runner.tempFile)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
gologger.Fatalf("Could not open targets file '%s': %s\n", options.Targets, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Precompute total number of targets
|
|
|
|
scanner := bufio.NewScanner(runner.input)
|
|
|
|
runner.inputCount = 0
|
|
|
|
for scanner.Scan() {
|
|
|
|
runner.inputCount++
|
|
|
|
}
|
|
|
|
runner.input.Seek(0, 0)
|
|
|
|
|
2020-04-04 12:51:05 +00:00
|
|
|
// Create the output file if asked
|
|
|
|
if options.Output != "" {
|
|
|
|
output, err := os.Create(options.Output)
|
|
|
|
if err != nil {
|
|
|
|
gologger.Fatalf("Could not create output file '%s': %s\n", options.Output, err)
|
|
|
|
}
|
|
|
|
runner.output = output
|
|
|
|
}
|
2020-07-23 18:19:19 +00:00
|
|
|
|
|
|
|
// Creates the progress tracking object
|
|
|
|
runner.progress = progress.NewProgress(runner.options.NoColor)
|
|
|
|
|
2020-04-04 10:29:05 +00:00
|
|
|
return runner, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close releases all the resources and cleans up
|
2020-04-04 12:51:05 +00:00
|
|
|
func (r *Runner) Close() {
|
|
|
|
r.output.Close()
|
2020-07-23 18:19:19 +00:00
|
|
|
r.input.Close()
|
2020-04-26 01:18:10 +00:00
|
|
|
os.Remove(r.tempFile)
|
2020-04-04 12:51:05 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
|
2020-07-13 22:04:19 +00:00
|
|
|
func isFilePath(path string) (bool, error) {
|
|
|
|
info, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
return info.Mode().IsRegular(), nil
|
|
|
|
}
|
|
|
|
|
2020-07-19 12:24:43 +00:00
|
|
|
func (r *Runner) resolvePathIfRelative(path string) (string, error) {
|
2020-07-13 22:04:19 +00:00
|
|
|
if r.isRelative(path) {
|
|
|
|
newPath, err := r.resolvePath(path)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return newPath, nil
|
|
|
|
}
|
|
|
|
return path, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isNewPath(path string, pathMap map[string]bool) bool {
|
|
|
|
if _, already := pathMap[path]; already {
|
|
|
|
gologger.Warningf("Skipping already specified path '%s'", path)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-04-04 19:48:57 +00:00
|
|
|
// RunEnumeration sets up the input layer for giving input nuclei.
|
2020-04-04 10:29:05 +00:00
|
|
|
// binary and runs the actual enumeration
|
|
|
|
func (r *Runner) RunEnumeration() {
|
2020-07-13 22:04:19 +00:00
|
|
|
// keeps track of processed dirs and files
|
|
|
|
processed := make(map[string]bool)
|
|
|
|
allTemplates := []string{}
|
|
|
|
|
|
|
|
// parses user input, handle file/directory cases and produce a list of unique templates
|
|
|
|
for _, t := range r.options.Templates {
|
2020-07-19 12:04:49 +00:00
|
|
|
// resolve and convert relative to absolute path
|
2020-07-19 12:24:43 +00:00
|
|
|
absPath, err := r.resolvePathIfRelative(t)
|
2020-07-13 22:04:19 +00:00
|
|
|
if err != nil {
|
2020-07-19 12:04:49 +00:00
|
|
|
gologger.Errorf("Could not find template file '%s': %s\n", t, err)
|
2020-07-13 22:04:19 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-06-27 15:20:43 +00:00
|
|
|
|
2020-07-19 12:04:49 +00:00
|
|
|
// determine file/directory
|
|
|
|
isFile, err := isFilePath(absPath)
|
2020-06-27 15:20:43 +00:00
|
|
|
if err != nil {
|
2020-07-19 12:04:49 +00:00
|
|
|
gologger.Errorf("Could not stat '%s': %s\n", absPath, err)
|
2020-07-13 22:04:19 +00:00
|
|
|
continue
|
2020-06-27 15:20:43 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 22:04:19 +00:00
|
|
|
// test for uniqueness
|
|
|
|
if !isNewPath(absPath, processed) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// mark this absolute path as processed
|
|
|
|
// - if it's a file, we'll never process it again
|
|
|
|
// - if it's a dir, we'll never walk it again
|
|
|
|
processed[absPath] = true
|
|
|
|
|
|
|
|
if isFile {
|
|
|
|
allTemplates = append(allTemplates, absPath)
|
|
|
|
} else {
|
|
|
|
matches := []string{}
|
|
|
|
|
|
|
|
// Recursively walk down the Templates directory and run all the template file checks
|
|
|
|
err = godirwalk.Walk(absPath, &godirwalk.Options{
|
|
|
|
Callback: func(path string, d *godirwalk.Dirent) error {
|
|
|
|
if !d.IsDir() && strings.HasSuffix(path, ".yaml") {
|
|
|
|
if isNewPath(path, processed) {
|
|
|
|
matches = append(matches, path)
|
|
|
|
processed[path] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
|
|
|
|
return godirwalk.SkipNode
|
|
|
|
},
|
|
|
|
Unsorted: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
// directory couldn't be walked
|
|
|
|
if err != nil {
|
2020-07-18 16:55:52 +00:00
|
|
|
gologger.Labelf("Could not find templates in directory '%s': %s\n", absPath, err)
|
2020-07-13 22:04:19 +00:00
|
|
|
continue
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2020-04-22 20:45:02 +00:00
|
|
|
|
2020-07-13 22:04:19 +00:00
|
|
|
// couldn't find templates in directory
|
|
|
|
if len(matches) == 0 {
|
2020-07-18 16:55:52 +00:00
|
|
|
gologger.Labelf("Error, no templates were found in '%s'.\n", absPath)
|
|
|
|
continue
|
2020-06-26 12:37:55 +00:00
|
|
|
}
|
2020-07-13 22:04:19 +00:00
|
|
|
|
|
|
|
allTemplates = append(allTemplates, matches...)
|
2020-04-22 22:26:41 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-06-26 12:37:55 +00:00
|
|
|
|
2020-05-02 17:10:52 +00:00
|
|
|
// 0 matches means no templates were found in directory
|
2020-07-13 22:04:19 +00:00
|
|
|
if len(allTemplates) == 0 {
|
|
|
|
gologger.Fatalf("Error, no templates were found.\n")
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-06-27 15:20:43 +00:00
|
|
|
|
2020-07-23 18:19:19 +00:00
|
|
|
p := r.progress
|
|
|
|
templateCount := len(allTemplates)
|
|
|
|
isSingleTemplate := templateCount == 1
|
|
|
|
|
|
|
|
if !isSingleTemplate {
|
|
|
|
// precompute request count
|
|
|
|
var totalRequests int64 = 0
|
|
|
|
for _, match := range allTemplates {
|
|
|
|
t, err := r.parse(match)
|
|
|
|
switch t.(type) {
|
|
|
|
case *templates.Template:
|
|
|
|
template := t.(*templates.Template)
|
|
|
|
totalRequests += template.GetHTTPRequestsCount()
|
|
|
|
default:
|
|
|
|
gologger.Errorf("Could not parse file '%s': %s\n", match, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// track global progress
|
|
|
|
p.SetupGlobalProgressbar(r.inputCount, templateCount, r.inputCount*totalRequests)
|
|
|
|
}
|
|
|
|
|
2020-07-13 22:04:19 +00:00
|
|
|
// run with the specified templates
|
2020-06-22 14:27:32 +00:00
|
|
|
var results bool
|
2020-07-23 18:19:19 +00:00
|
|
|
for i, match := range allTemplates {
|
2020-06-30 16:57:52 +00:00
|
|
|
t, err := r.parse(match)
|
2020-06-26 12:37:55 +00:00
|
|
|
switch t.(type) {
|
|
|
|
case *templates.Template:
|
|
|
|
template := t.(*templates.Template)
|
2020-07-23 18:19:19 +00:00
|
|
|
|
|
|
|
if isSingleTemplate {
|
|
|
|
// track single template progress
|
|
|
|
p.SetupTemplateProgressbar(-1, -1, template.ID, r.inputCount*template.GetHTTPRequestsCount())
|
|
|
|
} else {
|
|
|
|
p.SetupTemplateProgressbar(i, templateCount, template.ID, r.inputCount * template.GetHTTPRequestsCount())
|
|
|
|
}
|
|
|
|
|
2020-06-26 12:37:55 +00:00
|
|
|
for _, request := range template.RequestsDNS {
|
2020-07-23 18:19:19 +00:00
|
|
|
dnsResults := r.processTemplateWithList(p, template, request)
|
2020-06-26 12:37:55 +00:00
|
|
|
if dnsResults {
|
|
|
|
results = dnsResults
|
|
|
|
}
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2020-07-18 19:42:23 +00:00
|
|
|
for _, request := range template.BulkRequestsHTTP {
|
2020-07-23 18:19:19 +00:00
|
|
|
httpResults := r.processTemplateWithList(p, template, request)
|
2020-06-26 12:37:55 +00:00
|
|
|
if httpResults {
|
|
|
|
results = httpResults
|
|
|
|
}
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2020-06-26 12:37:55 +00:00
|
|
|
case *workflows.Workflow:
|
|
|
|
workflow := t.(*workflows.Workflow)
|
|
|
|
r.ProcessWorkflowWithList(workflow)
|
|
|
|
default:
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StartStdCapture()
|
2020-07-13 17:53:19 +00:00
|
|
|
gologger.Errorf("Could not parse file '%s': %s\n", match, err)
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StopStdCapture()
|
2020-04-22 22:26:41 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-07-23 18:19:19 +00:00
|
|
|
p.Wait()
|
|
|
|
p.ShowStdErr()
|
|
|
|
p.ShowStdOut()
|
|
|
|
|
2020-06-22 14:27:32 +00:00
|
|
|
if !results {
|
|
|
|
if r.output != nil {
|
|
|
|
outputFile := r.output.Name()
|
|
|
|
r.output.Close()
|
|
|
|
os.Remove(outputFile)
|
|
|
|
}
|
2020-07-18 18:32:00 +00:00
|
|
|
gologger.Infof("No results found. Happy hacking!")
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2020-05-02 17:10:52 +00:00
|
|
|
return
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-23 18:19:19 +00:00
|
|
|
// processTemplateWithList processes a template and runs the enumeration on all the targets
|
|
|
|
func (r *Runner) processTemplateWithList(p *progress.Progress, template *templates.Template, request interface{}) bool {
|
2020-04-04 10:29:05 +00:00
|
|
|
// Display the message for the template
|
|
|
|
message := fmt.Sprintf("[%s] Loaded template %s (@%s)", template.ID, template.Info.Name, template.Info.Author)
|
|
|
|
if template.Info.Severity != "" {
|
|
|
|
message += " [" + template.Info.Severity + "]"
|
|
|
|
}
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StartStdCapture()
|
2020-04-04 10:29:05 +00:00
|
|
|
gologger.Infof("%s\n", message)
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StopStdCapture()
|
2020-04-04 10:29:05 +00:00
|
|
|
|
2020-04-04 12:51:05 +00:00
|
|
|
var writer *bufio.Writer
|
|
|
|
if r.output != nil {
|
|
|
|
writer = bufio.NewWriter(r.output)
|
|
|
|
defer writer.Flush()
|
|
|
|
}
|
|
|
|
|
2020-07-16 08:57:28 +00:00
|
|
|
var httpExecuter *executer.HTTPExecuter
|
|
|
|
var dnsExecuter *executer.DNSExecuter
|
2020-04-27 18:19:53 +00:00
|
|
|
var err error
|
2020-04-26 01:18:10 +00:00
|
|
|
|
2020-07-16 08:57:28 +00:00
|
|
|
// Create an executer based on the request type.
|
2020-04-26 01:18:10 +00:00
|
|
|
switch value := request.(type) {
|
|
|
|
case *requests.DNSRequest:
|
2020-07-16 08:57:28 +00:00
|
|
|
dnsExecuter = executer.NewDNSExecuter(&executer.DNSOptions{
|
2020-06-22 14:00:01 +00:00
|
|
|
Debug: r.options.Debug,
|
2020-04-26 01:18:10 +00:00
|
|
|
Template: template,
|
|
|
|
DNSRequest: value,
|
|
|
|
Writer: writer,
|
2020-06-27 14:49:43 +00:00
|
|
|
JSON: r.options.JSON,
|
2020-04-26 01:18:10 +00:00
|
|
|
})
|
2020-07-18 19:42:23 +00:00
|
|
|
case *requests.BulkHTTPRequest:
|
2020-07-16 08:57:28 +00:00
|
|
|
httpExecuter, err = executer.NewHTTPExecuter(&executer.HTTPOptions{
|
2020-07-18 19:42:23 +00:00
|
|
|
Debug: r.options.Debug,
|
|
|
|
Template: template,
|
|
|
|
BulkHttpRequest: value,
|
|
|
|
Writer: writer,
|
|
|
|
Timeout: r.options.Timeout,
|
|
|
|
Retries: r.options.Retries,
|
|
|
|
ProxyURL: r.options.ProxyURL,
|
|
|
|
ProxySocksURL: r.options.ProxySocksURL,
|
|
|
|
CustomHeaders: r.options.CustomHeaders,
|
|
|
|
JSON: r.options.JSON,
|
2020-07-15 11:38:45 +00:00
|
|
|
JSONRequests: r.options.JSONRequests,
|
2020-07-18 19:42:23 +00:00
|
|
|
CookieReuse: value.CookieReuse,
|
2020-04-26 01:18:10 +00:00
|
|
|
})
|
|
|
|
}
|
2020-04-27 18:19:53 +00:00
|
|
|
if err != nil {
|
|
|
|
gologger.Warningf("Could not create http client: %s\n", err)
|
2020-06-22 14:27:32 +00:00
|
|
|
return false
|
2020-04-27 18:19:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
limiter := make(chan struct{}, r.options.Threads)
|
|
|
|
wg := &sync.WaitGroup{}
|
2020-04-22 22:26:41 +00:00
|
|
|
|
2020-07-23 18:19:19 +00:00
|
|
|
r.input.Seek(0, 0)
|
|
|
|
scanner := bufio.NewScanner(r.input)
|
2020-04-04 10:29:05 +00:00
|
|
|
for scanner.Scan() {
|
|
|
|
text := scanner.Text()
|
|
|
|
if text == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
limiter <- struct{}{}
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go func(URL string) {
|
2020-07-16 08:57:28 +00:00
|
|
|
var result executer.Result
|
2020-04-26 01:18:10 +00:00
|
|
|
|
2020-07-16 08:57:28 +00:00
|
|
|
if httpExecuter != nil {
|
2020-07-23 18:19:19 +00:00
|
|
|
result = httpExecuter.ExecuteHTTP(p, URL)
|
2020-04-26 01:18:10 +00:00
|
|
|
}
|
2020-07-16 08:57:28 +00:00
|
|
|
if dnsExecuter != nil {
|
|
|
|
result = dnsExecuter.ExecuteDNS(URL)
|
2020-04-26 01:18:10 +00:00
|
|
|
}
|
2020-07-10 07:04:38 +00:00
|
|
|
if result.Error != nil {
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StartStdCapture()
|
2020-07-10 07:04:38 +00:00
|
|
|
gologger.Warningf("Could not execute step: %s\n", result.Error)
|
2020-07-23 18:19:19 +00:00
|
|
|
p.StopStdCapture()
|
2020-04-26 01:18:10 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
<-limiter
|
|
|
|
wg.Done()
|
|
|
|
}(text)
|
|
|
|
}
|
|
|
|
close(limiter)
|
|
|
|
wg.Wait()
|
2020-06-22 14:27:32 +00:00
|
|
|
|
2020-07-16 08:57:28 +00:00
|
|
|
// See if we got any results from the executers
|
2020-06-22 14:27:32 +00:00
|
|
|
var results bool
|
2020-07-16 08:57:28 +00:00
|
|
|
if httpExecuter != nil {
|
2020-07-18 19:42:23 +00:00
|
|
|
results = httpExecuter.Results
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2020-07-16 08:57:28 +00:00
|
|
|
if dnsExecuter != nil {
|
2020-06-22 14:27:32 +00:00
|
|
|
if !results {
|
2020-07-18 19:42:23 +00:00
|
|
|
results = dnsExecuter.Results
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return results
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-06-26 08:23:54 +00:00
|
|
|
|
|
|
|
// ProcessWorkflowWithList coming from stdin or list of targets
|
|
|
|
func (r *Runner) ProcessWorkflowWithList(workflow *workflows.Workflow) {
|
2020-07-23 18:19:19 +00:00
|
|
|
r.input.Seek(0, 0)
|
|
|
|
scanner := bufio.NewScanner(r.input)
|
2020-06-26 08:23:54 +00:00
|
|
|
for scanner.Scan() {
|
|
|
|
text := scanner.Text()
|
|
|
|
if text == "" {
|
|
|
|
continue
|
|
|
|
}
|
2020-06-29 12:13:08 +00:00
|
|
|
if err := r.ProcessWorkflow(workflow, text); err != nil {
|
|
|
|
gologger.Warningf("Could not run workflow for %s: %s\n", text, err)
|
|
|
|
}
|
2020-06-26 08:23:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessWorkflow towards an URL
|
|
|
|
func (r *Runner) ProcessWorkflow(workflow *workflows.Workflow, URL string) error {
|
|
|
|
script := tengo.NewScript([]byte(workflow.Logic))
|
2020-07-10 07:04:38 +00:00
|
|
|
script.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
2020-07-16 14:32:42 +00:00
|
|
|
var jar *cookiejar.Jar
|
|
|
|
if workflow.CookieReuse {
|
|
|
|
var err error
|
|
|
|
jar, err = cookiejar.New(nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 08:23:54 +00:00
|
|
|
for name, value := range workflow.Variables {
|
|
|
|
var writer *bufio.Writer
|
|
|
|
if r.output != nil {
|
|
|
|
writer = bufio.NewWriter(r.output)
|
|
|
|
defer writer.Flush()
|
|
|
|
}
|
|
|
|
|
2020-06-29 12:13:08 +00:00
|
|
|
// 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 {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
value = newPath
|
2020-06-26 08:23:54 +00:00
|
|
|
}
|
2020-06-29 12:13:08 +00:00
|
|
|
|
|
|
|
// 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{}
|
2020-07-18 19:42:23 +00:00
|
|
|
if len(t.BulkRequestsHTTP) > 0 {
|
2020-07-16 08:57:28 +00:00
|
|
|
template.HTTPOptions = &executer.HTTPOptions{
|
2020-06-29 12:13:08 +00:00
|
|
|
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,
|
2020-07-16 14:32:42 +00:00
|
|
|
CookieJar: jar,
|
2020-06-29 12:13:08 +00:00
|
|
|
}
|
|
|
|
} else if len(t.RequestsDNS) > 0 {
|
2020-07-16 08:57:28 +00:00
|
|
|
template.DNSOptions = &executer.DNSOptions{
|
2020-06-29 12:13:08 +00:00
|
|
|
Debug: r.options.Debug,
|
|
|
|
Template: t,
|
|
|
|
Writer: writer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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{}
|
2020-07-18 19:42:23 +00:00
|
|
|
if len(t.BulkRequestsHTTP) > 0 {
|
2020-07-16 08:57:28 +00:00
|
|
|
template.HTTPOptions = &executer.HTTPOptions{
|
2020-06-29 12:13:08 +00:00
|
|
|
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,
|
2020-07-16 14:32:42 +00:00
|
|
|
CookieJar: jar,
|
2020-06-29 12:13:08 +00:00
|
|
|
}
|
|
|
|
} else if len(t.RequestsDNS) > 0 {
|
2020-07-16 08:57:28 +00:00
|
|
|
template.DNSOptions = &executer.DNSOptions{
|
2020-06-29 12:13:08 +00:00
|
|
|
Debug: r.options.Debug,
|
|
|
|
Template: t,
|
|
|
|
Writer: writer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if template.DNSOptions != nil || template.HTTPOptions != nil {
|
|
|
|
templatesList = append(templatesList, template)
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 13:10:42 +00:00
|
|
|
}
|
2020-06-29 12:13:08 +00:00
|
|
|
|
|
|
|
script.Add(name, &workflows.NucleiVar{Templates: templatesList, URL: URL})
|
2020-06-26 08:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err := script.RunContext(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
gologger.Errorf("Could not execute workflow '%s': %s\n", workflow.ID, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-26 12:37:55 +00:00
|
|
|
|
2020-06-30 16:57:52 +00:00
|
|
|
func (r *Runner) parse(file string) (interface{}, error) {
|
2020-06-26 12:37:55 +00:00
|
|
|
// check if it's a template
|
2020-06-30 16:57:52 +00:00
|
|
|
template, errTemplate := templates.Parse(file)
|
2020-06-26 12:37:55 +00:00
|
|
|
if errTemplate == nil {
|
2020-06-30 16:57:52 +00:00
|
|
|
return template, nil
|
2020-06-26 12:37:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if it's a workflow
|
2020-06-30 16:57:52 +00:00
|
|
|
workflow, errWorkflow := workflows.Parse(file)
|
2020-06-26 12:37:55 +00:00
|
|
|
if errWorkflow == nil {
|
2020-06-30 16:57:52 +00:00
|
|
|
return workflow, nil
|
2020-06-26 12:37:55 +00:00
|
|
|
}
|
2020-06-30 16:57:52 +00:00
|
|
|
|
|
|
|
if errTemplate != nil {
|
|
|
|
return nil, errTemplate
|
|
|
|
}
|
|
|
|
if errWorkflow != nil {
|
|
|
|
return nil, errWorkflow
|
|
|
|
}
|
|
|
|
return nil, errors.New("unknown error occured")
|
2020-06-26 12:37:55 +00:00
|
|
|
}
|