2020-04-04 10:29:05 +00:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2021-01-13 07:28:23 +00:00
|
|
|
"fmt"
|
2020-04-04 10:29:05 +00:00
|
|
|
"os"
|
2021-03-05 06:38:31 +00:00
|
|
|
"path"
|
2020-04-04 10:29:05 +00:00
|
|
|
"strings"
|
2021-04-16 11:26:41 +00:00
|
|
|
"time"
|
2020-04-04 10:29:05 +00:00
|
|
|
|
2020-08-23 18:46:18 +00:00
|
|
|
"github.com/logrusorgru/aurora"
|
2020-04-04 10:29:05 +00:00
|
|
|
"github.com/projectdiscovery/gologger"
|
2020-11-15 23:40:32 +00:00
|
|
|
"github.com/projectdiscovery/hmap/store/hybrid"
|
2020-12-29 10:08:14 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/internal/colorizer"
|
2021-02-26 07:43:11 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog"
|
2021-06-30 13:09:01 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/config"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/catalog/loader"
|
2020-12-29 10:08:14 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/output"
|
2021-03-09 11:49:03 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/progress"
|
2020-10-18 01:09:24 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/projectfile"
|
2021-01-13 07:28:23 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/clusterer"
|
2021-04-16 11:26:41 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
|
2021-04-18 09:57:43 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolinit"
|
2021-02-21 11:01:34 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/headless/engine"
|
2021-03-22 08:33:05 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting"
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/disk"
|
2021-06-05 12:31:08 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/reporting/exporters/sarif"
|
2020-07-01 10:47:24 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/templates"
|
2020-12-29 10:08:14 +00:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
2020-10-18 01:09:24 +00:00
|
|
|
"github.com/remeh/sizedwaitgroup"
|
2021-01-13 07:28:23 +00:00
|
|
|
"github.com/rs/xid"
|
2020-12-29 12:32:45 +00:00
|
|
|
"go.uber.org/atomic"
|
2020-11-15 23:40:32 +00:00
|
|
|
"go.uber.org/ratelimit"
|
2021-03-22 08:33:05 +00:00
|
|
|
"gopkg.in/yaml.v2"
|
2020-04-04 10:29:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Runner is a client for running the enumeration process.
|
|
|
|
type Runner struct {
|
2020-12-29 10:08:14 +00:00
|
|
|
hostMap *hybrid.HybridMap
|
|
|
|
output output.Writer
|
2021-04-16 11:26:41 +00:00
|
|
|
interactsh *interactsh.Client
|
2020-12-29 10:08:14 +00:00
|
|
|
inputCount int64
|
2021-06-30 13:09:01 +00:00
|
|
|
templatesConfig *config.Config
|
2020-12-29 10:08:14 +00:00
|
|
|
options *types.Options
|
|
|
|
projectFile *projectfile.ProjectFile
|
2021-02-26 07:43:11 +00:00
|
|
|
catalog *catalog.Catalog
|
2021-03-09 11:49:03 +00:00
|
|
|
progress progress.Progress
|
2020-12-29 10:08:14 +00:00
|
|
|
colorizer aurora.Aurora
|
2021-03-22 08:33:05 +00:00
|
|
|
issuesClient *reporting.Client
|
2020-12-29 10:08:14 +00:00
|
|
|
severityColors *colorizer.Colorizer
|
2021-02-21 11:01:34 +00:00
|
|
|
browser *engine.Browser
|
2020-12-29 10:08:14 +00:00
|
|
|
ratelimiter ratelimit.Limiter
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new client for running enumeration process.
|
2020-12-29 10:08:14 +00:00
|
|
|
func New(options *types.Options) (*Runner, error) {
|
2020-04-04 10:29:05 +00:00
|
|
|
runner := &Runner{
|
2020-12-29 10:08:14 +00:00
|
|
|
options: options,
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2021-02-21 11:01:34 +00:00
|
|
|
if options.Headless {
|
|
|
|
browser, err := engine.New(options)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
runner.browser = browser
|
|
|
|
}
|
2020-06-24 22:23:37 +00:00
|
|
|
if err := runner.updateTemplates(); err != nil {
|
2020-12-29 10:08:14 +00:00
|
|
|
gologger.Warning().Msgf("Could not update templates: %s\n", err)
|
2020-06-24 22:23:37 +00:00
|
|
|
}
|
2021-06-11 09:14:37 +00:00
|
|
|
|
|
|
|
runner.catalog = catalog.New(runner.options.TemplatesDirectory)
|
2021-03-22 08:33:05 +00:00
|
|
|
var reportingOptions *reporting.Options
|
2021-02-02 06:40:47 +00:00
|
|
|
if options.ReportingConfig != "" {
|
2021-03-22 08:33:05 +00:00
|
|
|
file, err := os.Open(options.ReportingConfig)
|
|
|
|
if err != nil {
|
|
|
|
gologger.Fatal().Msgf("Could not open reporting config file: %s\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
reportingOptions = &reporting.Options{}
|
2021-03-22 09:18:11 +00:00
|
|
|
if parseErr := yaml.NewDecoder(file).Decode(reportingOptions); parseErr != nil {
|
2021-03-22 08:33:05 +00:00
|
|
|
file.Close()
|
|
|
|
gologger.Fatal().Msgf("Could not parse reporting config file: %s\n", parseErr)
|
|
|
|
}
|
|
|
|
file.Close()
|
|
|
|
}
|
|
|
|
if options.DiskExportDirectory != "" {
|
|
|
|
if reportingOptions != nil {
|
|
|
|
reportingOptions.DiskExporter = &disk.Options{Directory: options.DiskExportDirectory}
|
|
|
|
} else {
|
|
|
|
reportingOptions = &reporting.Options{}
|
|
|
|
reportingOptions.DiskExporter = &disk.Options{Directory: options.DiskExportDirectory}
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 21:26:36 +00:00
|
|
|
if options.SarifExport != "" {
|
2021-06-05 12:31:08 +00:00
|
|
|
if reportingOptions != nil {
|
2021-06-05 21:26:36 +00:00
|
|
|
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
2021-06-05 12:31:08 +00:00
|
|
|
} else {
|
|
|
|
reportingOptions = &reporting.Options{}
|
2021-06-05 21:26:36 +00:00
|
|
|
reportingOptions.SarifExporter = &sarif.Options{File: options.SarifExport}
|
2021-06-05 12:31:08 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-22 08:33:05 +00:00
|
|
|
if reportingOptions != nil {
|
|
|
|
if client, err := reporting.New(reportingOptions, options.ReportingDB); err != nil {
|
2021-02-02 06:40:47 +00:00
|
|
|
gologger.Fatal().Msgf("Could not create issue reporting client: %s\n", err)
|
|
|
|
} else {
|
|
|
|
runner.issuesClient = client
|
|
|
|
}
|
|
|
|
}
|
2021-03-22 08:33:05 +00:00
|
|
|
|
2020-09-19 12:43:35 +00:00
|
|
|
// output coloring
|
|
|
|
useColor := !options.NoColor
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.colorizer = aurora.NewAurora(useColor)
|
|
|
|
runner.severityColors = colorizer.New(runner.colorizer)
|
2020-09-19 12:43:35 +00:00
|
|
|
|
2020-08-29 21:02:45 +00:00
|
|
|
if options.TemplateList {
|
|
|
|
runner.listAvailableTemplates()
|
2020-08-28 09:17:37 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2021-03-05 06:38:31 +00:00
|
|
|
if (len(options.Templates) == 0 || !options.NewTemplates || (options.Targets == "" && !options.Stdin && options.Target == "")) && options.UpdateTemplates {
|
2020-06-24 22:23:37 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
2020-11-15 23:40:32 +00:00
|
|
|
if hm, err := hybrid.New(hybrid.DefaultDiskOptions); err != nil {
|
2020-12-29 10:08:14 +00:00
|
|
|
gologger.Fatal().Msgf("Could not create temporary input file: %s\n", err)
|
2020-11-15 23:40:32 +00:00
|
|
|
} else {
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.hostMap = hm
|
2020-11-15 23:40:32 +00:00
|
|
|
}
|
2020-08-25 21:24:31 +00:00
|
|
|
|
2020-11-15 23:40:32 +00:00
|
|
|
runner.inputCount = 0
|
|
|
|
dupeCount := 0
|
2020-08-25 21:24:31 +00:00
|
|
|
|
2020-11-15 23:40:32 +00:00
|
|
|
// Handle single target
|
2020-06-25 16:10:20 +00:00
|
|
|
if options.Target != "" {
|
2020-11-15 23:40:32 +00:00
|
|
|
runner.inputCount++
|
2020-11-20 10:12:06 +00:00
|
|
|
// nolint:errcheck // ignoring error
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.hostMap.Set(options.Target, nil)
|
2020-06-25 16:10:20 +00:00
|
|
|
}
|
2020-04-26 01:18:10 +00:00
|
|
|
|
2020-11-15 23:40:32 +00:00
|
|
|
// Handle stdin
|
|
|
|
if options.Stdin {
|
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
for scanner.Scan() {
|
|
|
|
url := strings.TrimSpace(scanner.Text())
|
|
|
|
if url == "" {
|
|
|
|
continue
|
|
|
|
}
|
2020-12-29 10:08:14 +00:00
|
|
|
if _, ok := runner.hostMap.Get(url); ok {
|
2020-11-15 23:40:32 +00:00
|
|
|
dupeCount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
runner.inputCount++
|
2020-11-20 10:12:06 +00:00
|
|
|
// nolint:errcheck // ignoring error
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.hostMap.Set(url, nil)
|
2020-11-15 23:40:32 +00:00
|
|
|
}
|
2020-07-23 18:19:19 +00:00
|
|
|
}
|
|
|
|
|
2020-11-15 23:40:32 +00:00
|
|
|
// Handle taget file
|
|
|
|
if options.Targets != "" {
|
|
|
|
input, err := os.Open(options.Targets)
|
|
|
|
if err != nil {
|
2020-12-29 10:08:14 +00:00
|
|
|
gologger.Fatal().Msgf("Could not open targets file '%s': %s\n", options.Targets, err)
|
2020-07-26 14:43:53 +00:00
|
|
|
}
|
2020-11-15 23:40:32 +00:00
|
|
|
scanner := bufio.NewScanner(input)
|
|
|
|
for scanner.Scan() {
|
|
|
|
url := strings.TrimSpace(scanner.Text())
|
|
|
|
if url == "" {
|
|
|
|
continue
|
|
|
|
}
|
2020-12-29 10:08:14 +00:00
|
|
|
if _, ok := runner.hostMap.Get(url); ok {
|
2020-11-15 23:40:32 +00:00
|
|
|
dupeCount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
runner.inputCount++
|
2020-11-20 10:12:06 +00:00
|
|
|
// nolint:errcheck // ignoring error
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.hostMap.Set(url, nil)
|
2020-07-25 21:02:24 +00:00
|
|
|
}
|
2020-11-15 23:40:32 +00:00
|
|
|
input.Close()
|
2020-07-25 21:02:24 +00:00
|
|
|
}
|
2020-08-25 21:24:31 +00:00
|
|
|
|
2020-07-25 21:02:24 +00:00
|
|
|
if dupeCount > 0 {
|
2020-12-29 10:08:14 +00:00
|
|
|
gologger.Info().Msgf("Supplied input was automatically deduplicated (%d removed).", dupeCount)
|
2020-07-23 18:19:19 +00:00
|
|
|
}
|
|
|
|
|
2020-04-04 12:51:05 +00:00
|
|
|
// Create the output file if asked
|
2021-02-26 07:43:11 +00:00
|
|
|
outputWriter, err := output.NewStandardWriter(!options.NoColor, options.NoMeta, options.JSON, options.Output, options.TraceLogFile)
|
2020-12-29 12:45:27 +00:00
|
|
|
if err != nil {
|
|
|
|
gologger.Fatal().Msgf("Could not create output file '%s': %s\n", options.Output, err)
|
2020-04-04 12:51:05 +00:00
|
|
|
}
|
2021-02-26 07:43:11 +00:00
|
|
|
runner.output = outputWriter
|
2020-07-23 18:19:19 +00:00
|
|
|
|
2020-07-31 21:07:33 +00:00
|
|
|
// Creates the progress tracking object
|
2020-12-17 15:03:42 +00:00
|
|
|
var progressErr error
|
2021-03-09 11:49:03 +00:00
|
|
|
runner.progress, progressErr = progress.NewStatsTicker(options.StatsInterval, options.EnableProgressBar, options.Metrics, options.MetricsPort)
|
2020-12-17 15:03:42 +00:00
|
|
|
if progressErr != nil {
|
|
|
|
return nil, progressErr
|
|
|
|
}
|
2020-07-23 18:19:19 +00:00
|
|
|
|
2020-10-17 00:10:47 +00:00
|
|
|
// create project file if requested or load existing one
|
|
|
|
if options.Project {
|
2020-10-30 12:06:05 +00:00
|
|
|
var projectFileErr error
|
2020-12-29 10:08:14 +00:00
|
|
|
runner.projectFile, projectFileErr = projectfile.New(&projectfile.Options{Path: options.ProjectPath, Cleanup: options.ProjectPath == ""})
|
2020-10-30 12:06:05 +00:00
|
|
|
if projectFileErr != nil {
|
|
|
|
return nil, projectFileErr
|
2020-10-15 21:39:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 11:59:01 +00:00
|
|
|
if !options.NoInteractsh {
|
2021-04-16 11:26:41 +00:00
|
|
|
interactshClient, err := interactsh.New(&interactsh.Options{
|
|
|
|
ServerURL: options.InteractshURL,
|
|
|
|
CacheSize: int64(options.InteractionsCacheSize),
|
|
|
|
Eviction: time.Duration(options.InteractionsEviction) * time.Second,
|
|
|
|
ColldownPeriod: time.Duration(options.InteractionsColldownPeriod) * time.Second,
|
|
|
|
PollDuration: time.Duration(options.InteractionsPollDuration) * time.Second,
|
|
|
|
Output: runner.output,
|
2021-05-03 08:38:09 +00:00
|
|
|
IssuesClient: runner.issuesClient,
|
2021-04-16 11:26:41 +00:00
|
|
|
Progress: runner.progress,
|
|
|
|
})
|
|
|
|
if err != nil {
|
2021-05-08 20:07:22 +00:00
|
|
|
gologger.Error().Msgf("Could not create interactsh client: %s", err)
|
|
|
|
} else {
|
|
|
|
runner.interactsh = interactshClient
|
2021-04-16 11:26:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 23:40:32 +00:00
|
|
|
if options.RateLimit > 0 {
|
|
|
|
runner.ratelimiter = ratelimit.New(options.RateLimit)
|
|
|
|
} else {
|
|
|
|
runner.ratelimiter = ratelimit.NewUnlimited()
|
|
|
|
}
|
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() {
|
2020-09-11 16:05:29 +00:00
|
|
|
if r.output != nil {
|
|
|
|
r.output.Close()
|
|
|
|
}
|
2020-12-29 10:08:14 +00:00
|
|
|
r.hostMap.Close()
|
|
|
|
if r.projectFile != nil {
|
|
|
|
r.projectFile.Close()
|
2020-10-15 21:39:00 +00:00
|
|
|
}
|
2021-04-18 09:57:43 +00:00
|
|
|
protocolinit.Close()
|
2020-04-04 12:51:05 +00:00
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
|
2020-08-02 11:49:16 +00:00
|
|
|
// RunEnumeration sets up the input layer for giving input nuclei.
|
|
|
|
// binary and runs the actual enumeration
|
|
|
|
func (r *Runner) RunEnumeration() {
|
2021-03-22 09:30:26 +00:00
|
|
|
defer r.Close()
|
|
|
|
|
2021-06-30 13:09:01 +00:00
|
|
|
// If user asked for new templates to be executed, collect the list from template directory.
|
2021-03-05 06:38:31 +00:00
|
|
|
if r.options.NewTemplates {
|
2021-03-09 09:30:22 +00:00
|
|
|
templatesLoaded, err := r.readNewTemplatesFile()
|
2021-03-05 06:38:31 +00:00
|
|
|
if err != nil {
|
|
|
|
gologger.Warning().Msgf("Could not get newly added templates: %s\n", err)
|
|
|
|
}
|
2021-03-09 09:30:22 +00:00
|
|
|
r.options.Templates = append(r.options.Templates, templatesLoaded...)
|
2021-03-05 06:38:31 +00:00
|
|
|
}
|
2021-06-30 13:09:01 +00:00
|
|
|
ignoreFile := config.ReadIgnoreFile()
|
|
|
|
r.options.ExcludeTags = append(r.options.ExcludeTags, ignoreFile.Tags...)
|
|
|
|
r.options.ExcludedTemplates = append(r.options.ExcludedTemplates, ignoreFile.Files...)
|
|
|
|
|
2021-07-01 09:06:40 +00:00
|
|
|
executerOpts := protocols.ExecuterOptions{
|
|
|
|
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,
|
|
|
|
}
|
2021-07-04 23:05:53 +00:00
|
|
|
loaderConfig := loader.Config{
|
2021-06-30 13:09:01 +00:00
|
|
|
Templates: r.options.Templates,
|
|
|
|
Workflows: r.options.Workflows,
|
|
|
|
ExcludeTemplates: r.options.ExcludedTemplates,
|
|
|
|
Tags: r.options.Tags,
|
|
|
|
ExcludeTags: r.options.ExcludeTags,
|
|
|
|
IncludeTemplates: r.options.IncludeTemplates,
|
|
|
|
Authors: r.options.Author,
|
|
|
|
Severities: r.options.Severity,
|
|
|
|
IncludeTags: r.options.IncludeTags,
|
|
|
|
TemplatesDirectory: r.options.TemplatesDirectory,
|
|
|
|
Catalog: r.catalog,
|
2021-07-01 09:06:40 +00:00
|
|
|
ExecutorOptions: executerOpts,
|
2021-06-30 13:09:01 +00:00
|
|
|
}
|
2021-07-04 23:05:53 +00:00
|
|
|
store, err := loader.New(&loaderConfig)
|
2021-06-30 13:09:01 +00:00
|
|
|
if err != nil {
|
|
|
|
gologger.Fatal().Msgf("Could not load templates from config: %s\n", err)
|
2020-08-02 13:48:10 +00:00
|
|
|
}
|
2021-07-01 09:06:40 +00:00
|
|
|
store.Load()
|
|
|
|
|
|
|
|
builder := &strings.Builder{}
|
|
|
|
if r.templatesConfig != nil && r.templatesConfig.NucleiLatestVersion != "" {
|
|
|
|
builder.WriteString(" (")
|
|
|
|
|
2021-07-05 15:31:51 +00:00
|
|
|
if strings.Contains(config.Version, "-dev") {
|
|
|
|
builder.WriteString(r.colorizer.Blue("development").String())
|
|
|
|
} else if config.Version == r.templatesConfig.NucleiLatestVersion {
|
2021-07-01 09:06:40 +00:00
|
|
|
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 != "" {
|
|
|
|
builder.WriteString(" (")
|
|
|
|
|
|
|
|
if r.templatesConfig.CurrentVersion == 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()
|
|
|
|
|
|
|
|
gologger.Info().Msgf("Using Nuclei Templates %s%s", r.templatesConfig.CurrentVersion, messageStr)
|
|
|
|
|
|
|
|
if r.interactsh != nil {
|
|
|
|
gologger.Info().Msgf("Using Interactsh Server %s", r.options.InteractshURL)
|
|
|
|
}
|
|
|
|
if len(store.Templates()) > 0 {
|
2021-07-03 12:13:02 +00:00
|
|
|
gologger.Info().Msgf("Running %d Nuclei Templates", len(store.Templates()))
|
2021-07-01 09:06:40 +00:00
|
|
|
}
|
|
|
|
if len(store.Workflows()) > 0 {
|
2021-07-03 12:13:02 +00:00
|
|
|
gologger.Info().Msgf("Running %d Nuclei Workflows", len(store.Workflows()))
|
2021-07-01 09:06:40 +00:00
|
|
|
}
|
2020-08-02 11:49:16 +00:00
|
|
|
|
2020-08-02 16:33:55 +00:00
|
|
|
// pre-parse all the templates, apply filters
|
2021-01-13 07:28:23 +00:00
|
|
|
finalTemplates := []*templates.Template{}
|
2021-03-05 06:38:31 +00:00
|
|
|
|
2021-07-05 11:59:45 +00:00
|
|
|
var unclusteredRequests int64
|
2021-07-01 09:06:40 +00:00
|
|
|
for _, template := range store.Templates() {
|
2021-01-15 08:47:34 +00:00
|
|
|
// workflows will dynamically adjust the totals while running, as
|
|
|
|
// it can't be know in advance which requests will be called
|
|
|
|
if len(template.Workflows) > 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
unclusteredRequests += int64(template.TotalRequests) * r.inputCount
|
|
|
|
}
|
|
|
|
|
2021-07-01 12:52:08 +00:00
|
|
|
if r.options.VerboseVerbose {
|
2021-07-01 09:06:40 +00:00
|
|
|
for _, template := range store.Templates() {
|
|
|
|
r.logAvailableTemplate(template.Path)
|
|
|
|
}
|
|
|
|
for _, template := range store.Workflows() {
|
|
|
|
r.logAvailableTemplate(template.Path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
templatesMap := make(map[string]*templates.Template)
|
|
|
|
for _, v := range store.Templates() {
|
|
|
|
templatesMap[v.ID] = v
|
|
|
|
}
|
|
|
|
originalTemplatesCount := len(store.Templates())
|
2021-01-16 06:56:38 +00:00
|
|
|
clusterCount := 0
|
2021-07-01 09:06:40 +00:00
|
|
|
clusters := clusterer.Cluster(templatesMap)
|
2021-01-13 07:28:23 +00:00
|
|
|
for _, cluster := range clusters {
|
2021-02-07 09:42:38 +00:00
|
|
|
if len(cluster) > 1 && !r.options.OfflineHTTP {
|
|
|
|
executerOpts := protocols.ExecuterOptions{
|
2021-02-07 20:37:19 +00:00
|
|
|
Output: r.output,
|
|
|
|
Options: r.options,
|
|
|
|
Progress: r.progress,
|
2021-02-26 07:43:11 +00:00
|
|
|
Catalog: r.catalog,
|
2021-02-07 20:37:19 +00:00
|
|
|
RateLimiter: r.ratelimiter,
|
|
|
|
IssuesClient: r.issuesClient,
|
2021-02-21 11:01:34 +00:00
|
|
|
Browser: r.browser,
|
2021-02-07 20:37:19 +00:00
|
|
|
ProjectFile: r.projectFile,
|
2021-04-16 11:26:41 +00:00
|
|
|
Interactsh: r.interactsh,
|
2021-02-07 09:42:38 +00:00
|
|
|
}
|
2021-01-13 07:28:23 +00:00
|
|
|
clusterID := fmt.Sprintf("cluster-%s", xid.New().String())
|
|
|
|
|
|
|
|
finalTemplates = append(finalTemplates, &templates.Template{
|
|
|
|
ID: clusterID,
|
|
|
|
RequestsHTTP: cluster[0].RequestsHTTP,
|
2021-02-07 09:42:38 +00:00
|
|
|
Executer: clusterer.NewExecuter(cluster, &executerOpts),
|
2021-01-14 07:51:21 +00:00
|
|
|
TotalRequests: len(cluster[0].RequestsHTTP),
|
2021-01-13 07:28:23 +00:00
|
|
|
})
|
2021-02-25 07:07:47 +00:00
|
|
|
clusterCount += len(cluster)
|
2021-01-13 07:28:23 +00:00
|
|
|
} else {
|
2021-02-26 07:43:11 +00:00
|
|
|
finalTemplates = append(finalTemplates, cluster...)
|
2021-01-13 07:28:23 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-05 11:59:45 +00:00
|
|
|
finalTemplates = append(finalTemplates, store.Workflows()...)
|
2021-01-15 08:47:34 +00:00
|
|
|
|
2021-06-14 11:44:16 +00:00
|
|
|
var totalRequests int64
|
2021-01-15 08:47:34 +00:00
|
|
|
for _, t := range finalTemplates {
|
|
|
|
if len(t.Workflows) > 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
totalRequests += int64(t.TotalRequests) * r.inputCount
|
|
|
|
}
|
|
|
|
if totalRequests < unclusteredRequests {
|
2021-07-01 09:06:40 +00:00
|
|
|
gologger.Info().Msgf("Reduced %d requests (%d templates clustered)", unclusteredRequests-totalRequests, clusterCount)
|
2021-01-15 08:47:34 +00:00
|
|
|
}
|
2021-07-01 09:06:40 +00:00
|
|
|
workflowCount := len(store.Workflows())
|
|
|
|
templateCount := originalTemplatesCount + workflowCount
|
2020-08-02 16:33:55 +00:00
|
|
|
|
2020-05-02 17:10:52 +00:00
|
|
|
// 0 matches means no templates were found in directory
|
2020-08-02 16:33:55 +00:00
|
|
|
if templateCount == 0 {
|
2020-12-29 10:08:14 +00:00
|
|
|
gologger.Fatal().Msgf("Error, no templates were found.\n")
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2020-06-27 15:20:43 +00:00
|
|
|
|
2020-12-29 12:32:45 +00:00
|
|
|
results := &atomic.Bool{}
|
2020-10-17 00:10:47 +00:00
|
|
|
wgtemplates := sizedwaitgroup.New(r.options.TemplateThreads)
|
2020-07-24 16:12:16 +00:00
|
|
|
|
2021-02-06 22:16:26 +00:00
|
|
|
// tracks global progress and captures stdout/stderr until p.Wait finishes
|
|
|
|
r.progress.Init(r.inputCount, templateCount, totalRequests)
|
|
|
|
|
|
|
|
for _, t := range finalTemplates {
|
|
|
|
wgtemplates.Add()
|
|
|
|
go func(template *templates.Template) {
|
|
|
|
defer wgtemplates.Done()
|
|
|
|
|
|
|
|
if len(template.Workflows) > 0 {
|
|
|
|
results.CAS(false, r.processWorkflowWithList(template))
|
2021-03-01 07:12:13 +00:00
|
|
|
} else {
|
2021-02-06 22:16:26 +00:00
|
|
|
results.CAS(false, r.processTemplateWithList(template))
|
|
|
|
}
|
|
|
|
}(t)
|
2020-07-27 16:47:23 +00:00
|
|
|
}
|
2021-02-06 22:16:26 +00:00
|
|
|
wgtemplates.Wait()
|
2021-04-16 11:26:41 +00:00
|
|
|
|
|
|
|
if r.interactsh != nil {
|
2021-04-18 10:40:10 +00:00
|
|
|
matched := r.interactsh.Close()
|
|
|
|
if matched {
|
|
|
|
results.CAS(false, true)
|
|
|
|
}
|
2021-04-16 11:26:41 +00:00
|
|
|
}
|
2021-02-05 09:33:58 +00:00
|
|
|
r.progress.Stop()
|
2020-07-23 18:19:19 +00:00
|
|
|
|
2021-02-02 06:40:47 +00:00
|
|
|
if r.issuesClient != nil {
|
|
|
|
r.issuesClient.Close()
|
|
|
|
}
|
2020-12-29 12:32:45 +00:00
|
|
|
if !results.Load() {
|
2021-01-11 14:49:16 +00:00
|
|
|
gologger.Info().Msgf("No results found. Better luck next time!")
|
2020-06-22 14:27:32 +00:00
|
|
|
}
|
2021-02-21 11:01:34 +00:00
|
|
|
if r.browser != nil {
|
|
|
|
r.browser.Close()
|
|
|
|
}
|
2020-04-04 10:29:05 +00:00
|
|
|
}
|
2021-03-05 06:38:31 +00:00
|
|
|
|
|
|
|
// readNewTemplatesFile reads newly added templates from directory if it exists
|
|
|
|
func (r *Runner) readNewTemplatesFile() ([]string, error) {
|
|
|
|
additionsFile := path.Join(r.templatesConfig.TemplatesDirectory, ".new-additions")
|
|
|
|
file, err := os.Open(additionsFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
2021-03-09 09:30:22 +00:00
|
|
|
templatesList := []string{}
|
2021-03-05 06:38:31 +00:00
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
text := scanner.Text()
|
|
|
|
if text == "" {
|
|
|
|
continue
|
|
|
|
}
|
2021-03-09 09:30:22 +00:00
|
|
|
templatesList = append(templatesList, text)
|
2021-03-05 06:38:31 +00:00
|
|
|
}
|
2021-03-09 09:30:22 +00:00
|
|
|
return templatesList, nil
|
2021-03-05 06:38:31 +00:00
|
|
|
}
|