mirror of https://github.com/daffainfo/nuclei.git
Added directory support
parent
2b109b5a82
commit
b3c52d941f
|
@ -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)
|
||||
if err != nil {
|
||||
gologger.Errorf("Could not parse template file '%s': %s\n", value, err)
|
||||
return err
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
httpOptions := &executor.HTTPOptions{
|
||||
Debug: r.options.Debug,
|
||||
Writer: writer,
|
||||
Template: template,
|
||||
Timeout: r.options.Timeout,
|
||||
Retries: r.options.Retries,
|
||||
ProxyURL: r.options.ProxyURL,
|
||||
ProxySocksURL: r.options.ProxySocksURL,
|
||||
CustomHeaders: r.options.CustomHeaders,
|
||||
// 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: 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)
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
dnsOptions := &executor.DNSOptions{
|
||||
Debug: r.options.Debug,
|
||||
Template: template,
|
||||
Writer: writer,
|
||||
}
|
||||
script.Add(name, &workflows.NucleiVar{HTTPOptions: httpOptions, DNSOptions: dnsOptions, URL: URL})
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = httpExecutor.ExecuteHTTP(n.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if httpExecutor.GotResults() {
|
||||
return tengo.TrueValue, nil
|
||||
}
|
||||
return tengo.FalseValue, nil
|
||||
}
|
||||
var gotResult bool
|
||||
|
||||
for _, request := range n.DNSOptions.Template.RequestsDNS {
|
||||
n.DNSOptions.DNSRequest = request
|
||||
dnsExecutor := executor.NewDNSExecutor(n.DNSOptions)
|
||||
err = dnsExecutor.ExecuteDNS(n.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
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 {
|
||||
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 {
|
||||
gologger.Warningf("Could not send request for template '%s': %s\n", template.HTTPOptions.Template.ID, err)
|
||||
continue
|
||||
}
|
||||
if httpExecutor.GotResults() {
|
||||
gotResult = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if dnsExecutor.GotResults() {
|
||||
return tengo.TrueValue, nil
|
||||
}
|
||||
return tengo.FalseValue, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue