2020-04-04 15:59:05 +05:30
|
|
|
package runner
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
2022-03-03 19:44:29 +05:30
|
|
|
"context"
|
2021-11-29 14:38:45 +01:00
|
|
|
"encoding/json"
|
2022-03-03 19:44:29 +05:30
|
|
|
"net/http"
|
|
|
|
|
_ "net/http/pprof"
|
2020-04-04 15:59:05 +05:30
|
|
|
"os"
|
2021-08-23 14:53:37 +03:00
|
|
|
"path/filepath"
|
2020-04-04 15:59:05 +05:30
|
|
|
"strings"
|
2021-04-16 16:56:41 +05:30
|
|
|
"time"
|
2020-04-04 15:59:05 +05:30
|
|
|
|
2020-08-24 00:16:18 +05:30
|
|
|
"github.com/logrusorgru/aurora"
|
2021-07-07 19:15:09 +05:30
|
|
|
"github.com/pkg/errors"
|
2022-01-18 20:59:37 +05:30
|
|
|
"go.uber.org/atomic"
|
2021-07-19 21:04:08 +03:00
|
|
|
"go.uber.org/ratelimit"
|
|
|
|
|
|
2020-04-04 15:59:05 +05:30
|
|
|
"github.com/projectdiscovery/gologger"
|
2020-12-29 15:38:14 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/internal/colorizer"
|
2021-02-26 13:13:11 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
2021-06-30 18:39:01 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
2021-10-27 16:50:36 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/core"
|
2021-10-28 17:45:38 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/core/inputs/hybrid"
|
2020-12-29 15:38:14 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
2021-07-19 21:04:08 +03:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/parsers"
|
2021-03-09 17:19:03 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/progress"
|
2020-10-18 03:09:24 +02:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/projectfile"
|
2021-01-13 12:58:23 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
2022-03-14 12:32:05 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/automaticscan"
|
2021-08-16 21:24:37 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/hosterrorscache"
|
2021-04-16 16:56:41 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
|
2021-04-18 11:57:43 +02:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
|
2021-02-21 16:31:34 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
|
2022-03-09 12:31:12 +01:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
|
2021-03-22 14:03:05 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
|
2021-09-19 16:26:47 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/markdown"
|
2021-06-05 18:01:08 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
2021-11-22 00:19:53 +01:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
2020-12-29 15:38:14 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
2021-07-19 21:04:08 +03:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/utils"
|
2021-08-31 19:27:26 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/utils/stats"
|
2021-11-20 13:25:27 +05:30
|
|
|
yamlwrapper "github.com/projectdiscovery/nuclei/v2/pkg/utils/yaml"
|
2022-03-09 12:31:12 +01:00
|
|
|
"github.com/projectdiscovery/retryablehttp-go"
|
2022-01-14 11:20:19 +01:00
|
|
|
"github.com/projectdiscovery/stringsutil"
|
2020-04-04 15:59:05 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Runner is a client for running the enumeration process.
|
|
|
|
|
type Runner struct {
|
2021-10-27 15:53:04 +05:30
|
|
|
output output.Writer
|
|
|
|
|
interactsh *interactsh.Client
|
|
|
|
|
templatesConfig *config.Config
|
|
|
|
|
options *types.Options
|
|
|
|
|
projectFile *projectfile.ProjectFile
|
|
|
|
|
catalog *catalog.Catalog
|
|
|
|
|
progress progress.Progress
|
|
|
|
|
colorizer aurora.Aurora
|
|
|
|
|
issuesClient *reporting.Client
|
2021-10-28 17:20:07 +05:30
|
|
|
hmapInputProvider *hybrid.Input
|
2021-10-27 15:53:04 +05:30
|
|
|
browser *engine.Browser
|
|
|
|
|
ratelimiter ratelimit.Limiter
|
|
|
|
|
hostErrors *hosterrorscache.Cache
|
2021-11-29 14:38:45 +01:00
|
|
|
resumeCfg *types.ResumeCfg
|
2022-03-03 19:44:29 +05:30
|
|
|
pprofServer *http.Server
|
2020-04-04 15:59:05 +05:30
|
|
|
}
|
|
|
|
|
|
2022-03-03 19:44:29 +05:30
|
|
|
const pprofServerAddress = "127.0.0.1:8086"
|
|
|
|
|
|
2020-04-04 15:59:05 +05:30
|
|
|
// New creates a new client for running enumeration process.
|
2020-12-29 15:38:14 +05:30
|
|
|
func New(options *types.Options) (*Runner, error) {
|
2020-04-04 15:59:05 +05:30
|
|
|
runner := &Runner{
|
2020-12-29 15:38:14 +05:30
|
|
|
options: options,
|
2020-04-04 15:59:05 +05:30
|
|
|
}
|
2021-07-25 03:07:19 +05:30
|
|
|
if options.UpdateNuclei {
|
2021-07-25 03:13:46 +05:30
|
|
|
if err := updateNucleiVersionToLatest(runner.options.Verbose); err != nil {
|
2021-07-25 03:07:19 +05:30
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2021-08-31 19:53:53 +05:30
|
|
|
if options.Validate {
|
|
|
|
|
parsers.ShouldValidate = true
|
2021-12-01 10:35:18 -06:00
|
|
|
// Does not update the templates when validate flag is used
|
|
|
|
|
options.NoUpdateTemplates = true
|
2021-08-31 19:53:53 +05:30
|
|
|
}
|
2020-06-25 03:53:37 +05:30
|
|
|
if err := runner.updateTemplates(); err != nil {
|
2021-12-16 13:59:19 +05:30
|
|
|
gologger.Error().Msgf("Could not update templates: %s\n", err)
|
2020-06-25 03:53:37 +05:30
|
|
|
}
|
2021-07-25 03:13:46 +05:30
|
|
|
if options.Headless {
|
2021-12-05 20:14:16 +05:30
|
|
|
if engine.MustDisableSandbox() {
|
|
|
|
|
gologger.Warning().Msgf("The current platform and privileged user will run the browser without sandbox\n")
|
|
|
|
|
}
|
2021-07-25 03:13:46 +05:30
|
|
|
browser, err := engine.New(options)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
runner.browser = browser
|
|
|
|
|
}
|
2021-06-11 14:44:37 +05:30
|
|
|
|
|
|
|
|
runner.catalog = catalog.New(runner.options.TemplatesDirectory)
|
2021-03-22 14:03:05 +05:30
|
|
|
|
2022-03-09 12:31:12 +01:00
|
|
|
var httpclient *retryablehttp.Client
|
|
|
|
|
if options.ProxyInternal && types.ProxyURL != "" || types.ProxySocksURL != "" {
|
|
|
|
|
var err error
|
|
|
|
|
httpclient, err = httpclientpool.Get(options, &httpclientpool.Configuration{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-03 16:48:39 +03:00
|
|
|
reportingOptions, err := createReportingOptions(options)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2021-06-05 18:01:08 +05:30
|
|
|
}
|
2022-03-09 12:31:12 +01:00
|
|
|
if reportingOptions != nil && httpclient != nil {
|
|
|
|
|
reportingOptions.HttpClient = httpclient
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-22 14:03:05 +05:30
|
|
|
if reportingOptions != nil {
|
2021-07-07 19:23:25 +05:30
|
|
|
client, err := reporting.New(reportingOptions, options.ReportingDB)
|
|
|
|
|
if err != nil {
|
2021-07-07 19:15:09 +05:30
|
|
|
return nil, errors.Wrap(err, "could not create issue reporting client")
|
2021-02-02 12:10:47 +05:30
|
|
|
}
|
2021-07-07 19:23:25 +05:30
|
|
|
runner.issuesClient = client
|
2021-02-02 12:10:47 +05:30
|
|
|
}
|
2021-03-22 14:03:05 +05:30
|
|
|
|
2020-09-19 14:43:35 +02:00
|
|
|
// output coloring
|
|
|
|
|
useColor := !options.NoColor
|
2020-12-29 15:38:14 +05:30
|
|
|
runner.colorizer = aurora.NewAurora(useColor)
|
2022-03-14 12:32:05 +05:30
|
|
|
templates.Colorizer = runner.colorizer
|
|
|
|
|
templates.SeverityColorizer = colorizer.New(runner.colorizer)
|
2020-09-19 14:43:35 +02:00
|
|
|
|
2020-08-29 23:02:45 +02:00
|
|
|
if options.TemplateList {
|
|
|
|
|
runner.listAvailableTemplates()
|
2020-08-28 11:17:37 +02:00
|
|
|
os.Exit(0)
|
|
|
|
|
}
|
2022-03-03 19:44:29 +05:30
|
|
|
if options.EnablePprof {
|
|
|
|
|
server := &http.Server{
|
|
|
|
|
Addr: pprofServerAddress,
|
|
|
|
|
Handler: http.DefaultServeMux,
|
|
|
|
|
}
|
|
|
|
|
gologger.Info().Msgf("Listening pprof debug server on: %s", pprofServerAddress)
|
|
|
|
|
runner.pprofServer = server
|
|
|
|
|
go func() {
|
2022-03-07 10:28:25 +05:30
|
|
|
_ = server.ListenAndServe()
|
2022-03-03 19:44:29 +05:30
|
|
|
}()
|
|
|
|
|
}
|
2020-08-28 11:17:37 +02:00
|
|
|
|
2021-08-03 22:47:09 -07:00
|
|
|
if (len(options.Templates) == 0 || !options.NewTemplates || (options.TargetsFilePath == "" && !options.Stdin && len(options.Targets) == 0)) && options.UpdateTemplates {
|
2020-06-25 03:53:37 +05:30
|
|
|
os.Exit(0)
|
|
|
|
|
}
|
2020-04-26 06:48:10 +05:30
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
// Initialize the input source
|
2021-10-28 17:20:07 +05:30
|
|
|
hmapInput, err := hybrid.New(options)
|
2021-10-27 15:53:04 +05:30
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create input provider")
|
2020-07-23 20:19:19 +02:00
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
runner.hmapInputProvider = hmapInput
|
2020-07-23 20:19:19 +02:00
|
|
|
|
2020-04-04 18:21:05 +05:30
|
|
|
// Create the output file if asked
|
2022-04-01 14:29:02 -05:00
|
|
|
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.NoTimestamp, options.JSON, options.JSONRequests, options.MatcherStatus, options.StoreResponse, options.Output, options.TraceLogFile, options.ErrorLogFile, options.StoreResponseDir)
|
2020-12-29 18:15:27 +05:30
|
|
|
if err != nil {
|
2021-07-07 19:15:09 +05:30
|
|
|
return nil, errors.Wrap(err, "could not create output file")
|
2020-04-04 18:21:05 +05:30
|
|
|
}
|
2021-02-26 13:13:11 +05:30
|
|
|
runner.output = outputWriter
|
2020-07-23 20:19:19 +02:00
|
|
|
|
2021-07-25 04:27:25 +05:30
|
|
|
if options.JSON && options.EnableProgressBar {
|
2021-07-08 15:26:58 +05:30
|
|
|
options.StatsJSON = true
|
|
|
|
|
}
|
2021-07-08 18:50:03 +05:30
|
|
|
if options.StatsJSON {
|
|
|
|
|
options.EnableProgressBar = true
|
|
|
|
|
}
|
2020-07-31 23:07:33 +02:00
|
|
|
// Creates the progress tracking object
|
2020-12-17 20:33:42 +05:30
|
|
|
var progressErr error
|
2021-06-22 16:02:37 -07:00
|
|
|
runner.progress, progressErr = progress.NewStatsTicker(options.StatsInterval, options.EnableProgressBar, options.StatsJSON, options.Metrics, options.MetricsPort)
|
2020-12-17 20:33:42 +05:30
|
|
|
if progressErr != nil {
|
|
|
|
|
return nil, progressErr
|
|
|
|
|
}
|
2020-07-23 20:19:19 +02:00
|
|
|
|
2021-09-01 17:34:51 +03:00
|
|
|
// create project file if requested or load the existing one
|
2020-10-17 02:10:47 +02:00
|
|
|
if options.Project {
|
2020-10-30 13:06:05 +01:00
|
|
|
var projectFileErr error
|
2021-08-03 14:51:34 +03:00
|
|
|
runner.projectFile, projectFileErr = projectfile.New(&projectfile.Options{Path: options.ProjectPath, Cleanup: utils.IsBlank(options.ProjectPath)})
|
2020-10-30 13:06:05 +01:00
|
|
|
if projectFileErr != nil {
|
|
|
|
|
return nil, projectFileErr
|
2020-10-15 23:39:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-29 14:38:45 +01:00
|
|
|
// create the resume configuration structure
|
|
|
|
|
resumeCfg := types.NewResumeCfg()
|
|
|
|
|
if runner.options.ShouldLoadResume() {
|
|
|
|
|
gologger.Info().Msg("Resuming from save checkpoint")
|
2022-02-25 13:04:46 +01:00
|
|
|
file, err := os.ReadFile(runner.options.Resume)
|
2021-11-29 14:38:45 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
err = json.Unmarshal([]byte(file), &resumeCfg)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-12-16 22:32:03 +01:00
|
|
|
resumeCfg.Compile()
|
2021-11-29 14:38:45 +01:00
|
|
|
}
|
|
|
|
|
runner.resumeCfg = resumeCfg
|
|
|
|
|
|
2021-11-16 20:02:39 +05:30
|
|
|
opts := interactsh.NewDefaultOptions(runner.output, runner.issuesClient, runner.progress)
|
|
|
|
|
opts.Debug = runner.options.Debug
|
2022-02-25 23:11:56 +05:30
|
|
|
opts.NoColor = runner.options.NoColor
|
2022-01-13 13:22:43 +05:30
|
|
|
if options.InteractshURL != "" {
|
|
|
|
|
opts.ServerURL = options.InteractshURL
|
|
|
|
|
}
|
2021-11-16 20:02:39 +05:30
|
|
|
opts.Authorization = options.InteractshToken
|
|
|
|
|
opts.CacheSize = int64(options.InteractionsCacheSize)
|
|
|
|
|
opts.Eviction = time.Duration(options.InteractionsEviction) * time.Second
|
2022-03-09 12:31:12 +01:00
|
|
|
opts.CooldownPeriod = time.Duration(options.InteractionsCoolDownPeriod) * time.Second
|
2021-11-16 20:02:39 +05:30
|
|
|
opts.PollDuration = time.Duration(options.InteractionsPollDuration) * time.Second
|
|
|
|
|
opts.NoInteractsh = runner.options.NoInteractsh
|
2021-12-21 14:20:03 +05:30
|
|
|
opts.StopAtFirstMatch = runner.options.StopAtFirstMatch
|
2022-01-27 12:14:32 +05:30
|
|
|
opts.Debug = runner.options.Debug
|
|
|
|
|
opts.DebugRequest = runner.options.DebugRequests
|
|
|
|
|
opts.DebugResponse = runner.options.DebugResponse
|
2022-03-09 12:31:12 +01:00
|
|
|
if httpclient != nil {
|
|
|
|
|
opts.HTTPClient = httpclient
|
|
|
|
|
}
|
2021-11-16 20:02:39 +05:30
|
|
|
interactshClient, err := interactsh.New(opts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Error().Msgf("Could not create interactsh client: %s", err)
|
|
|
|
|
} else {
|
|
|
|
|
runner.interactsh = interactshClient
|
2021-04-16 16:56:41 +05:30
|
|
|
}
|
|
|
|
|
|
2021-07-31 15:46:21 +05:30
|
|
|
if options.RateLimitMinute > 0 {
|
|
|
|
|
runner.ratelimiter = ratelimit.New(options.RateLimitMinute, ratelimit.Per(60*time.Second))
|
|
|
|
|
} else if options.RateLimit > 0 {
|
2020-11-16 00:40:32 +01:00
|
|
|
runner.ratelimiter = ratelimit.New(options.RateLimit)
|
|
|
|
|
} else {
|
|
|
|
|
runner.ratelimiter = ratelimit.NewUnlimited()
|
|
|
|
|
}
|
2020-04-04 15:59:05 +05:30
|
|
|
return runner, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-03 16:48:39 +03:00
|
|
|
func createReportingOptions(options *types.Options) (*reporting.Options, error) {
|
|
|
|
|
var reportingOptions *reporting.Options
|
|
|
|
|
if options.ReportingConfig != "" {
|
|
|
|
|
file, err := os.Open(options.ReportingConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not open reporting config file")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reportingOptions = &reporting.Options{}
|
2021-11-20 13:25:27 +05:30
|
|
|
if err := yamlwrapper.DecodeAndValidate(file, reportingOptions); err != nil {
|
2021-09-03 16:48:39 +03:00
|
|
|
file.Close()
|
2021-11-20 13:25:27 +05:30
|
|
|
return nil, errors.Wrap(err, "could not parse reporting config file")
|
2021-09-03 16:48:39 +03:00
|
|
|
}
|
|
|
|
|
file.Close()
|
|
|
|
|
}
|
2021-09-19 16:26:47 +05:30
|
|
|
if options.MarkdownExportDirectory != "" {
|
2021-09-03 16:48:39 +03:00
|
|
|
if reportingOptions != nil {
|
2021-09-19 16:26:47 +05:30
|
|
|
reportingOptions.MarkdownExporter = &markdown.Options{Directory: options.MarkdownExportDirectory}
|
2021-09-03 16:48:39 +03:00
|
|
|
} else {
|
|
|
|
|
reportingOptions = &reporting.Options{}
|
2021-09-19 16:26:47 +05:30
|
|
|
reportingOptions.MarkdownExporter = &markdown.Options{Directory: options.MarkdownExportDirectory}
|
2021-09-03 16:48:39 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if options.SarifExport != "" {
|
|
|
|
|
if reportingOptions != nil {
|
|
|
|
|
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
|
|
|
|
} else {
|
|
|
|
|
reportingOptions = &reporting.Options{}
|
|
|
|
|
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return reportingOptions, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 15:59:05 +05:30
|
|
|
// Close releases all the resources and cleans up
|
2020-04-04 18:21:05 +05:30
|
|
|
func (r *Runner) Close() {
|
2020-09-11 21:35:29 +05:30
|
|
|
if r.output != nil {
|
|
|
|
|
r.output.Close()
|
|
|
|
|
}
|
2020-12-29 15:38:14 +05:30
|
|
|
if r.projectFile != nil {
|
|
|
|
|
r.projectFile.Close()
|
2020-10-15 23:39:00 +02:00
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
r.hmapInputProvider.Close()
|
2021-04-18 11:57:43 +02:00
|
|
|
protocolinit.Close()
|
2022-03-03 19:44:29 +05:30
|
|
|
if r.pprofServer != nil {
|
|
|
|
|
_ = r.pprofServer.Shutdown(context.Background())
|
|
|
|
|
}
|
2020-04-04 18:21:05 +05:30
|
|
|
}
|
2020-04-04 15:59:05 +05:30
|
|
|
|
2020-08-02 13:49:16 +02:00
|
|
|
// RunEnumeration sets up the input layer for giving input nuclei.
|
|
|
|
|
// binary and runs the actual enumeration
|
2021-07-07 19:15:09 +05:30
|
|
|
func (r *Runner) RunEnumeration() error {
|
2021-03-22 15:00:26 +05:30
|
|
|
defer r.Close()
|
|
|
|
|
|
2021-09-01 17:34:51 +03:00
|
|
|
// If user asked for new templates to be executed, collect the list from the templates' directory.
|
2021-03-05 12:08:31 +05:30
|
|
|
if r.options.NewTemplates {
|
2021-03-09 15:00:22 +05:30
|
|
|
templatesLoaded, err := r.readNewTemplatesFile()
|
2021-03-05 12:08:31 +05:30
|
|
|
if err != nil {
|
2021-07-07 19:15:09 +05:30
|
|
|
return errors.Wrap(err, "could not get newly added templates")
|
2021-03-05 12:08:31 +05:30
|
|
|
}
|
2021-03-09 15:00:22 +05:30
|
|
|
r.options.Templates = append(r.options.Templates, templatesLoaded...)
|
2021-03-05 12:08:31 +05:30
|
|
|
}
|
2021-12-01 10:35:18 -06:00
|
|
|
// Exclude ignored file for validation
|
|
|
|
|
if !r.options.Validate {
|
|
|
|
|
ignoreFile := config.ReadIgnoreFile()
|
|
|
|
|
r.options.ExcludeTags = append(r.options.ExcludeTags, ignoreFile.Tags...)
|
|
|
|
|
r.options.ExcludedTemplates = append(r.options.ExcludedTemplates, ignoreFile.Files...)
|
|
|
|
|
}
|
2021-08-16 21:24:37 +05:30
|
|
|
var cache *hosterrorscache.Cache
|
2021-09-01 01:09:16 +05:30
|
|
|
if r.options.MaxHostError > 0 {
|
|
|
|
|
cache = hosterrorscache.New(r.options.MaxHostError, hosterrorscache.DefaultMaxHostsCount).SetVerbose(r.options.Verbose)
|
2021-08-16 21:24:37 +05:30
|
|
|
}
|
|
|
|
|
r.hostErrors = cache
|
2021-10-27 16:50:36 +05:30
|
|
|
|
|
|
|
|
// Create the executer options which will be used throughout the execution
|
|
|
|
|
// stage by the nuclei engine modules.
|
2021-07-01 14:36:40 +05:30
|
|
|
executerOpts := protocols.ExecuterOptions{
|
2021-08-16 21:24:37 +05:30
|
|
|
Output: r.output,
|
|
|
|
|
Options: r.options,
|
|
|
|
|
Progress: r.progress,
|
|
|
|
|
Catalog: r.catalog,
|
|
|
|
|
IssuesClient: r.issuesClient,
|
|
|
|
|
RateLimiter: r.ratelimiter,
|
|
|
|
|
Interactsh: r.interactsh,
|
|
|
|
|
ProjectFile: r.projectFile,
|
|
|
|
|
Browser: r.browser,
|
|
|
|
|
HostErrorsCache: cache,
|
2021-10-28 17:20:07 +05:30
|
|
|
Colorizer: r.colorizer,
|
2021-11-29 14:38:45 +01:00
|
|
|
ResumeCfg: r.resumeCfg,
|
2021-07-01 14:36:40 +05:30
|
|
|
}
|
2021-10-27 16:50:36 +05:30
|
|
|
engine := core.New(r.options)
|
|
|
|
|
engine.SetExecuterOptions(executerOpts)
|
2021-07-15 13:41:41 +03:00
|
|
|
|
|
|
|
|
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.Wrap(err, "Could not create loader.")
|
|
|
|
|
}
|
|
|
|
|
executerOpts.WorkflowLoader = workflowLoader
|
|
|
|
|
|
2022-03-02 16:50:20 +05:30
|
|
|
store, err := loader.New(loader.NewConfig(r.options, r.templatesConfig, r.catalog, executerOpts))
|
2021-06-30 18:39:01 +05:30
|
|
|
if err != nil {
|
2021-07-07 19:15:09 +05:30
|
|
|
return errors.Wrap(err, "could not load templates from config")
|
2020-08-02 15:48:10 +02:00
|
|
|
}
|
2021-08-31 19:27:26 +05:30
|
|
|
store.Load()
|
|
|
|
|
|
2021-07-07 19:03:14 +05:30
|
|
|
if r.options.Validate {
|
2021-08-27 17:06:06 +03:00
|
|
|
if err := store.ValidateTemplates(r.options.Templates, r.options.Workflows); err != nil {
|
|
|
|
|
return err
|
2021-07-07 19:03:14 +05:30
|
|
|
}
|
2021-12-05 15:11:14 +01:00
|
|
|
if stats.GetValue(parsers.SyntaxErrorStats) == 0 && stats.GetValue(parsers.SyntaxWarningStats) == 0 && stats.GetValue(parsers.RuntimeWarningsStats) == 0 {
|
2021-08-31 19:27:26 +05:30
|
|
|
gologger.Info().Msgf("All templates validated successfully\n")
|
|
|
|
|
} else {
|
|
|
|
|
return errors.New("encountered errors while performing template validation")
|
|
|
|
|
}
|
2021-07-07 19:23:25 +05:30
|
|
|
return nil // exit
|
2021-07-07 19:03:14 +05:30
|
|
|
}
|
2021-08-31 19:27:26 +05:30
|
|
|
|
2021-10-28 23:17:05 +05:30
|
|
|
r.displayExecutionInfo(store)
|
2020-08-02 13:49:16 +02:00
|
|
|
|
2022-01-18 20:59:37 +05:30
|
|
|
var results *atomic.Bool
|
2022-03-14 12:32:05 +05:30
|
|
|
if r.options.AutomaticScan {
|
2022-04-18 17:21:33 -05:00
|
|
|
if results, err = r.executeSmartWorkflowInput(executerOpts, store, engine); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-18 20:59:37 +05:30
|
|
|
} else {
|
2022-04-18 17:21:33 -05:00
|
|
|
if results, err = r.executeTemplatesInput(store, engine); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2022-01-18 20:59:37 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if r.interactsh != nil {
|
|
|
|
|
matched := r.interactsh.Close()
|
|
|
|
|
if matched {
|
|
|
|
|
results.CAS(false, true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
r.progress.Stop()
|
|
|
|
|
|
|
|
|
|
if r.issuesClient != nil {
|
|
|
|
|
r.issuesClient.Close()
|
|
|
|
|
}
|
2022-03-21 11:33:10 +01:00
|
|
|
|
2022-01-18 20:59:37 +05:30
|
|
|
if !results.Load() {
|
|
|
|
|
gologger.Info().Msgf("No results found. Better luck next time!")
|
|
|
|
|
}
|
|
|
|
|
if r.browser != nil {
|
|
|
|
|
r.browser.Close()
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) executeSmartWorkflowInput(executerOpts protocols.ExecuterOptions, store *loader.Store, engine *core.Engine) (*atomic.Bool, error) {
|
2022-03-14 16:25:27 +05:30
|
|
|
r.progress.Init(r.hmapInputProvider.Count(), 0, 0)
|
|
|
|
|
|
2022-03-14 12:32:05 +05:30
|
|
|
service, err := automaticscan.New(automaticscan.Options{
|
2022-01-18 20:59:37 +05:30
|
|
|
ExecuterOpts: executerOpts,
|
|
|
|
|
Store: store,
|
|
|
|
|
Engine: engine,
|
|
|
|
|
Target: r.hmapInputProvider,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2022-04-19 16:14:49 +05:30
|
|
|
return nil, errors.Wrap(err, "could not create automatic scan service")
|
2022-01-18 20:59:37 +05:30
|
|
|
}
|
2022-03-14 12:32:05 +05:30
|
|
|
service.Execute()
|
2022-01-18 20:59:37 +05:30
|
|
|
result := &atomic.Bool{}
|
|
|
|
|
result.Store(service.Close())
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *Runner) executeTemplatesInput(store *loader.Store, engine *core.Engine) (*atomic.Bool, error) {
|
2021-07-05 17:29:45 +05:30
|
|
|
var unclusteredRequests int64
|
2021-07-01 14:36:40 +05:30
|
|
|
for _, template := range store.Templates() {
|
2021-01-15 14:17:34 +05:30
|
|
|
// workflows will dynamically adjust the totals while running, as
|
2021-08-03 14:51:34 +03:00
|
|
|
// it can't be known in advance which requests will be called
|
2021-01-15 14:17:34 +05:30
|
|
|
if len(template.Workflows) > 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
unclusteredRequests += int64(template.TotalRequests) * r.hmapInputProvider.Count()
|
2021-01-15 14:17:34 +05:30
|
|
|
}
|
|
|
|
|
|
2021-07-01 18:22:08 +05:30
|
|
|
if r.options.VerboseVerbose {
|
2021-07-01 14:36:40 +05:30
|
|
|
for _, template := range store.Templates() {
|
|
|
|
|
r.logAvailableTemplate(template.Path)
|
|
|
|
|
}
|
|
|
|
|
for _, template := range store.Workflows() {
|
|
|
|
|
r.logAvailableTemplate(template.Path)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-15 14:17:34 +05:30
|
|
|
|
2021-10-28 17:20:07 +05:30
|
|
|
// Cluster the templates first because we want info on how many
|
|
|
|
|
// templates did we cluster for showing to user in CLI
|
|
|
|
|
originalTemplatesCount := len(store.Templates())
|
2021-11-22 00:19:53 +01:00
|
|
|
finalTemplates, clusterCount := templates.ClusterTemplates(store.Templates(), engine.ExecuterOptions())
|
2021-10-28 17:20:07 +05:30
|
|
|
finalTemplates = append(finalTemplates, store.Workflows()...)
|
|
|
|
|
|
2021-06-14 17:14:16 +05:30
|
|
|
var totalRequests int64
|
2021-01-15 14:17:34 +05:30
|
|
|
for _, t := range finalTemplates {
|
|
|
|
|
if len(t.Workflows) > 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-01-17 03:33:10 +05:30
|
|
|
totalRequests += int64(t.Executer.Requests()) * r.hmapInputProvider.Count()
|
2021-01-15 14:17:34 +05:30
|
|
|
}
|
|
|
|
|
if totalRequests < unclusteredRequests {
|
2021-07-08 00:41:22 +05:30
|
|
|
gologger.Info().Msgf("Templates clustered: %d (Reduced %d HTTP Requests)", clusterCount, unclusteredRequests-totalRequests)
|
2021-01-15 14:17:34 +05:30
|
|
|
}
|
2021-07-01 14:36:40 +05:30
|
|
|
workflowCount := len(store.Workflows())
|
|
|
|
|
templateCount := originalTemplatesCount + workflowCount
|
2020-08-02 18:33:55 +02:00
|
|
|
|
2020-05-02 12:10:52 -05:00
|
|
|
// 0 matches means no templates were found in directory
|
2020-08-02 18:33:55 +02:00
|
|
|
if templateCount == 0 {
|
2022-03-21 11:33:10 +01:00
|
|
|
return &atomic.Bool{}, errors.New("no valid templates were found")
|
2020-04-04 15:59:05 +05:30
|
|
|
}
|
2020-06-27 20:50:43 +05:30
|
|
|
|
2021-02-07 03:46:26 +05:30
|
|
|
// tracks global progress and captures stdout/stderr until p.Wait finishes
|
2021-10-27 15:53:04 +05:30
|
|
|
r.progress.Init(r.hmapInputProvider.Count(), templateCount, totalRequests)
|
2021-02-07 03:46:26 +05:30
|
|
|
|
2021-10-28 17:20:07 +05:30
|
|
|
results := engine.ExecuteWithOpts(finalTemplates, r.hmapInputProvider, true)
|
2022-01-18 20:59:37 +05:30
|
|
|
return results, nil
|
2020-04-04 15:59:05 +05:30
|
|
|
}
|
2021-03-05 12:08:31 +05:30
|
|
|
|
2021-10-28 23:17:05 +05:30
|
|
|
// displayExecutionInfo displays misc info about the nuclei engine execution
|
|
|
|
|
func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
|
|
|
|
// Display stats for any loaded templates' syntax warnings or errors
|
|
|
|
|
stats.Display(parsers.SyntaxWarningStats)
|
|
|
|
|
stats.Display(parsers.SyntaxErrorStats)
|
2021-12-05 15:11:14 +01:00
|
|
|
stats.Display(parsers.RuntimeWarningsStats)
|
2021-10-28 23:17:05 +05:30
|
|
|
|
|
|
|
|
builder := &strings.Builder{}
|
|
|
|
|
if r.templatesConfig != nil && r.templatesConfig.NucleiLatestVersion != "" {
|
|
|
|
|
builder.WriteString(" (")
|
|
|
|
|
|
|
|
|
|
if strings.Contains(config.Version, "-dev") {
|
|
|
|
|
builder.WriteString(r.colorizer.Blue("development").String())
|
|
|
|
|
} else if config.Version == r.templatesConfig.NucleiLatestVersion {
|
|
|
|
|
builder.WriteString(r.colorizer.Green("latest").String())
|
|
|
|
|
} else {
|
|
|
|
|
builder.WriteString(r.colorizer.Red("outdated").String())
|
|
|
|
|
}
|
|
|
|
|
builder.WriteString(")")
|
|
|
|
|
}
|
|
|
|
|
messageStr := builder.String()
|
|
|
|
|
builder.Reset()
|
|
|
|
|
|
|
|
|
|
gologger.Info().Msgf("Using Nuclei Engine %s%s", config.Version, messageStr)
|
|
|
|
|
|
|
|
|
|
if r.templatesConfig != nil && r.templatesConfig.NucleiTemplatesLatestVersion != "" { // TODO extract duplicated logic
|
|
|
|
|
builder.WriteString(" (")
|
|
|
|
|
|
|
|
|
|
if r.templatesConfig.TemplateVersion == r.templatesConfig.NucleiTemplatesLatestVersion {
|
|
|
|
|
builder.WriteString(r.colorizer.Green("latest").String())
|
|
|
|
|
} else {
|
|
|
|
|
builder.WriteString(r.colorizer.Red("outdated").String())
|
|
|
|
|
}
|
|
|
|
|
builder.WriteString(")")
|
|
|
|
|
}
|
|
|
|
|
messageStr = builder.String()
|
|
|
|
|
builder.Reset()
|
|
|
|
|
|
|
|
|
|
if r.templatesConfig != nil {
|
|
|
|
|
gologger.Info().Msgf("Using Nuclei Templates %s%s", r.templatesConfig.TemplateVersion, messageStr)
|
|
|
|
|
}
|
|
|
|
|
if len(store.Templates()) > 0 {
|
|
|
|
|
gologger.Info().Msgf("Templates added in last update: %d", r.countNewTemplates())
|
|
|
|
|
gologger.Info().Msgf("Templates loaded for scan: %d", len(store.Templates()))
|
|
|
|
|
}
|
|
|
|
|
if len(store.Workflows()) > 0 {
|
|
|
|
|
gologger.Info().Msgf("Workflows loaded for scan: %d", len(store.Workflows()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-05 12:08:31 +05:30
|
|
|
// readNewTemplatesFile reads newly added templates from directory if it exists
|
|
|
|
|
func (r *Runner) readNewTemplatesFile() ([]string, error) {
|
2021-08-27 23:23:01 +05:30
|
|
|
if r.templatesConfig == nil {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2021-08-23 14:53:37 +03:00
|
|
|
additionsFile := filepath.Join(r.templatesConfig.TemplatesDirectory, ".new-additions")
|
2021-03-05 12:08:31 +05:30
|
|
|
file, err := os.Open(additionsFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
2021-03-09 15:00:22 +05:30
|
|
|
templatesList := []string{}
|
2021-03-05 12:08:31 +05:30
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
text := scanner.Text()
|
|
|
|
|
if text == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-02-09 05:46:17 +01:00
|
|
|
if isTemplate(text) {
|
2022-01-14 11:20:19 +01:00
|
|
|
templatesList = append(templatesList, text)
|
|
|
|
|
}
|
2021-03-05 12:08:31 +05:30
|
|
|
}
|
2021-03-09 15:00:22 +05:30
|
|
|
return templatesList, nil
|
2021-03-05 12:08:31 +05:30
|
|
|
}
|
2021-07-08 15:15:26 +05:30
|
|
|
|
2021-09-01 17:34:51 +03:00
|
|
|
// countNewTemplates returns the number of newly added templates
|
2021-07-08 15:15:26 +05:30
|
|
|
func (r *Runner) countNewTemplates() int {
|
2021-08-27 23:23:01 +05:30
|
|
|
if r.templatesConfig == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
2021-08-23 14:53:37 +03:00
|
|
|
additionsFile := filepath.Join(r.templatesConfig.TemplatesDirectory, ".new-additions")
|
2021-07-08 15:15:26 +05:30
|
|
|
file, err := os.Open(additionsFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
count := 0
|
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
text := scanner.Text()
|
|
|
|
|
if text == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-01-14 11:20:19 +01:00
|
|
|
|
2022-02-09 05:46:17 +01:00
|
|
|
if isTemplate(text) {
|
2022-01-14 11:20:19 +01:00
|
|
|
count++
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 15:15:26 +05:30
|
|
|
}
|
|
|
|
|
return count
|
|
|
|
|
}
|
2021-11-29 14:38:45 +01:00
|
|
|
|
2022-02-09 05:46:17 +01:00
|
|
|
func isTemplate(filename string) bool {
|
2022-01-14 11:20:19 +01:00
|
|
|
return stringsutil.EqualFoldAny(filepath.Ext(filename), templates.TemplateExtension)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-29 14:38:45 +01:00
|
|
|
// SaveResumeConfig to file
|
2022-02-24 14:32:41 +05:30
|
|
|
func (r *Runner) SaveResumeConfig(path string) error {
|
2021-11-29 14:38:45 +01:00
|
|
|
resumeCfg := types.NewResumeCfg()
|
2022-05-17 11:08:48 +02:00
|
|
|
r.resumeCfg.Lock()
|
2021-12-16 22:32:03 +01:00
|
|
|
resumeCfg.ResumeFrom = r.resumeCfg.Current
|
|
|
|
|
data, _ := json.MarshalIndent(resumeCfg, "", "\t")
|
2022-05-17 11:08:48 +02:00
|
|
|
r.resumeCfg.Unlock()
|
2021-11-29 14:38:45 +01:00
|
|
|
|
2022-02-24 14:32:41 +05:30
|
|
|
return os.WriteFile(path, data, os.ModePerm)
|
2021-11-29 14:38:45 +01:00
|
|
|
}
|