mirror of https://github.com/daffainfo/nuclei.git
Added initial config file support with cobra cli
parent
0023aaed77
commit
3ee7428166
|
@ -1,19 +1,109 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v2/internal/runner"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
|
||||
options = &types.Options{}
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "nuclei",
|
||||
Short: "Nuclei is a fast and extensible security scanner",
|
||||
Long: `Nuclei is a fast tool for configurable targeted scanning
|
||||
based on templates offering massive extensibility and ease of use.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
mergeViperConfiguration(cmd)
|
||||
|
||||
runner.ParseOptions(options)
|
||||
|
||||
nucleiRunner, err := runner.New(options)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
}
|
||||
|
||||
nucleiRunner.RunEnumeration()
|
||||
nucleiRunner.Close()
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Parse the command line flags and read config files
|
||||
options := runner.ParseOptions()
|
||||
|
||||
nucleiRunner, err := runner.New(options)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
}
|
||||
|
||||
nucleiRunner.RunEnumeration()
|
||||
nucleiRunner.Close()
|
||||
rootCmd.Execute()
|
||||
}
|
||||
|
||||
// mergeViperConfiguration merges the flag configuration with viper file.
|
||||
func mergeViperConfiguration(cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().VisitAll(func(f *pflag.Flag) {
|
||||
if !f.Changed && viper.IsSet(f.Name) {
|
||||
switch p := viper.Get(f.Name).(type) {
|
||||
case []interface{}:
|
||||
for _, item := range p {
|
||||
cmd.PersistentFlags().Set(f.Name, cast.ToString(item))
|
||||
}
|
||||
default:
|
||||
cmd.PersistentFlags().Set(f.Name, viper.GetString(f.Name))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
home, _ := os.UserHomeDir()
|
||||
templatesDirectory := path.Join(home, "nuclei-templates")
|
||||
|
||||
cobra.OnInitialize(func() {
|
||||
if cfgFile != "" {
|
||||
viper.SetConfigFile(cfgFile)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
gologger.Fatal().Msgf("Could not read config: %s\n", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "Nuclei config file (default is $HOME/.nuclei.yaml)")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.Metrics, "metrics", false, "Expose nuclei metrics on a port")
|
||||
rootCmd.PersistentFlags().IntVar(&options.MetricsPort, "metrics-port", 9092, "Port to expose nuclei metrics on")
|
||||
rootCmd.PersistentFlags().StringVar(&options.Target, "target", "", "Target is a single target to scan using template")
|
||||
rootCmd.PersistentFlags().StringSliceVarP(&options.Templates, "templates", "t", []string{}, "Template input dir/file/files to run on host. Can be used multiple times. Supports globbing.")
|
||||
rootCmd.PersistentFlags().StringSliceVar(&options.ExcludedTemplates, "exclude", []string{}, "Template input dir/file/files to exclude. Can be used multiple times. Supports globbing.")
|
||||
rootCmd.PersistentFlags().StringVar(&options.Severity, "severity", "", "Filter templates based on their severity and only run the matching ones. Comma-separated values can be used to specify multiple severities.")
|
||||
rootCmd.PersistentFlags().StringVarP(&options.Targets, "list", "l", "", "List of URLs to run templates on")
|
||||
rootCmd.PersistentFlags().StringVarP(&options.Output, "output", "o", "", "File to write output to (optional)")
|
||||
rootCmd.PersistentFlags().StringVar(&options.ProxyURL, "proxy-url", "", "URL of the proxy server")
|
||||
rootCmd.PersistentFlags().StringVar(&options.ProxySocksURL, "proxy-socks-url", "", "URL of the proxy socks server")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.Silent, "silent", false, "Show only results in output")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.Version, "version", false, "Show version of nuclei")
|
||||
rootCmd.PersistentFlags().BoolVarP(&options.Verbose, "verbose", "v", false, "Show Verbose output")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.NoColor, "no-color", false, "Disable colors in output")
|
||||
rootCmd.PersistentFlags().IntVar(&options.Timeout, "timeout", 5, "Time to wait in seconds before timeout")
|
||||
rootCmd.PersistentFlags().IntVar(&options.Retries, "retries", 1, "Number of times to retry a failed request")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.RandomAgent, "random-agent", false, "Use randomly selected HTTP User-Agent header value")
|
||||
rootCmd.PersistentFlags().StringSliceVarP(&options.CustomHeaders, "header", "H", []string{}, "Custom Header.")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.Debug, "debug", false, "Allow debugging of request/responses")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.UpdateTemplates, "update-templates", false, "Update Templates updates the installed templates (optional)")
|
||||
rootCmd.PersistentFlags().StringVar(&options.TraceLogFile, "trace-log", "", "File to write sent requests trace log")
|
||||
rootCmd.PersistentFlags().StringVar(&options.TemplatesDirectory, "update-directory", templatesDirectory, "Directory to use for storing nuclei-templates")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.JSON, "json", false, "Write json output to files")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.JSONRequests, "include-rr", false, "Write requests/responses for matches in JSON output")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.EnableProgressBar, "stats", false, "Display stats of the running scan")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.TemplateList, "tl", false, "List available templates")
|
||||
rootCmd.PersistentFlags().IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit (maximum requests/second")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)")
|
||||
rootCmd.PersistentFlags().IntVar(&options.BulkSize, "bulk-size", 25, "Maximum Number of hosts analyzed in parallel per template")
|
||||
rootCmd.PersistentFlags().IntVarP(&options.TemplateThreads, "concurrency", "c", 10, "Maximum Number of templates executed in parallel")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.Project, "project", false, "Use a project folder to avoid sending same request multiple times")
|
||||
rootCmd.PersistentFlags().StringVar(&options.ProjectPath, "project-path", "", "Use a user defined project folder, temporary folder is used if not specified but enabled")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.NoMeta, "no-meta", false, "Don't display metadata for the matches")
|
||||
rootCmd.PersistentFlags().BoolVar(&options.TemplatesVersion, "templates-version", false, "Shows the installed nuclei-templates version")
|
||||
rootCmd.PersistentFlags().StringVar(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "", "Burp Collaborator BIID")
|
||||
}
|
||||
|
|
|
@ -2,10 +2,8 @@ package runner
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/gologger/formatter"
|
||||
|
@ -14,51 +12,7 @@ import (
|
|||
)
|
||||
|
||||
// ParseOptions parses the command line flags provided by a user
|
||||
func ParseOptions() *types.Options {
|
||||
options := &types.Options{}
|
||||
|
||||
home, _ := os.UserHomeDir()
|
||||
templatesDirectory := path.Join(home, "nuclei-templates")
|
||||
|
||||
flag.BoolVar(&options.Sandbox, "sandbox", false, "Run workflows in isolated sandbox mode")
|
||||
flag.BoolVar(&options.Metrics, "metrics", false, "Expose nuclei metrics on a port")
|
||||
flag.IntVar(&options.MetricsPort, "metrics-port", 9092, "Port to expose nuclei metrics on")
|
||||
flag.IntVar(&options.MaxWorkflowDuration, "workflow-duration", 10, "Max time for workflow run on single URL in minutes")
|
||||
flag.StringVar(&options.Target, "target", "", "Target is a single target to scan using template")
|
||||
flag.Var(&options.Templates, "t", "Template input dir/file/files to run on host. Can be used multiple times. Supports globbing.")
|
||||
flag.Var(&options.ExcludedTemplates, "exclude", "Template input dir/file/files to exclude. Can be used multiple times. Supports globbing.")
|
||||
flag.StringVar(&options.Severity, "severity", "", "Filter templates based on their severity and only run the matching ones. Comma-separated values can be used to specify multiple severities.")
|
||||
flag.StringVar(&options.Targets, "l", "", "List of URLs to run templates on")
|
||||
flag.StringVar(&options.Output, "o", "", "File to write output to (optional)")
|
||||
flag.StringVar(&options.ProxyURL, "proxy-url", "", "URL of the proxy server")
|
||||
flag.StringVar(&options.ProxySocksURL, "proxy-socks-url", "", "URL of the proxy socks server")
|
||||
flag.BoolVar(&options.Silent, "silent", false, "Show only results in output")
|
||||
flag.BoolVar(&options.Version, "version", false, "Show version of nuclei")
|
||||
flag.BoolVar(&options.Verbose, "v", false, "Show Verbose output")
|
||||
flag.BoolVar(&options.NoColor, "no-color", false, "Disable colors in output")
|
||||
flag.IntVar(&options.Timeout, "timeout", 5, "Time to wait in seconds before timeout")
|
||||
flag.IntVar(&options.Retries, "retries", 1, "Number of times to retry a failed request")
|
||||
flag.BoolVar(&options.RandomAgent, "random-agent", false, "Use randomly selected HTTP User-Agent header value")
|
||||
flag.Var(&options.CustomHeaders, "H", "Custom Header.")
|
||||
flag.BoolVar(&options.Debug, "debug", false, "Allow debugging of request/responses")
|
||||
flag.BoolVar(&options.UpdateTemplates, "update-templates", false, "Update Templates updates the installed templates (optional)")
|
||||
flag.StringVar(&options.TraceLogFile, "trace-log", "", "File to write sent requests trace log")
|
||||
flag.StringVar(&options.TemplatesDirectory, "update-directory", templatesDirectory, "Directory to use for storing nuclei-templates")
|
||||
flag.BoolVar(&options.JSON, "json", false, "Write json output to files")
|
||||
flag.BoolVar(&options.JSONRequests, "include-rr", false, "Write requests/responses for matches in JSON output")
|
||||
flag.BoolVar(&options.EnableProgressBar, "stats", false, "Display stats of the running scan")
|
||||
flag.BoolVar(&options.TemplateList, "tl", false, "List available templates")
|
||||
flag.IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit (maximum requests/second")
|
||||
flag.BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)")
|
||||
flag.IntVar(&options.BulkSize, "bulk-size", 25, "Maximum Number of hosts analyzed in parallel per template")
|
||||
flag.IntVar(&options.TemplateThreads, "c", 10, "Maximum Number of templates executed in parallel")
|
||||
flag.BoolVar(&options.Project, "project", false, "Use a project folder to avoid sending same request multiple times")
|
||||
flag.StringVar(&options.ProjectPath, "project-path", "", "Use a user defined project folder, temporary folder is used if not specified but enabled")
|
||||
flag.BoolVar(&options.NoMeta, "no-meta", false, "Don't display metadata for the matches")
|
||||
flag.BoolVar(&options.TemplatesVersion, "templates-version", false, "Shows the installed nuclei-templates version")
|
||||
flag.StringVar(&options.BurpCollaboratorBiid, "burp-collaborator-biid", "", "Burp Collaborator BIID")
|
||||
flag.Parse()
|
||||
|
||||
func ParseOptions(options *types.Options) {
|
||||
// Check if stdin pipe was given
|
||||
options.Stdin = hasStdin()
|
||||
|
||||
|
@ -87,7 +41,6 @@ func ParseOptions() *types.Options {
|
|||
if err != nil {
|
||||
gologger.Fatal().Msgf("Program exiting: %s\n", err)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// hasStdin returns true if we have stdin input
|
||||
|
|
|
@ -13,10 +13,8 @@ import (
|
|||
|
||||
// getParsedTemplatesFor parse the specified templates and returns a slice of the parsable ones, optionally filtered
|
||||
// by severity, along with a flag indicating if workflows are present.
|
||||
func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities string) (parsedTemplates []*templates.Template, workflowCount int) {
|
||||
func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities []string) (parsedTemplates []*templates.Template, workflowCount int) {
|
||||
workflowCount = 0
|
||||
severities = strings.ToLower(severities)
|
||||
allSeverities := strings.Split(severities, ",")
|
||||
filterBySeverity := len(severities) > 0
|
||||
|
||||
gologger.Info().Msgf("Loading templates...")
|
||||
|
@ -31,7 +29,7 @@ func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities string
|
|||
workflowCount++
|
||||
}
|
||||
sev := strings.ToLower(t.Info["severity"])
|
||||
if !filterBySeverity || hasMatchingSeverity(sev, allSeverities) {
|
||||
if !filterBySeverity || hasMatchingSeverity(sev, severities) {
|
||||
parsedTemplates = append(parsedTemplates, t)
|
||||
gologger.Info().Msgf("%s\n", r.templateLogMsg(t.ID, t.Info["name"], t.Info["author"], t.Info["severity"]))
|
||||
} else {
|
||||
|
@ -113,11 +111,11 @@ func (r *Runner) listAvailableTemplates() {
|
|||
|
||||
func hasMatchingSeverity(templateSeverity string, allowedSeverities []string) bool {
|
||||
for _, s := range allowedSeverities {
|
||||
s = strings.ToLower(s)
|
||||
if s != "" && strings.HasPrefix(templateSeverity, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package generators
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -30,7 +29,6 @@ func loadPayloads(payloads map[string]interface{}) (map[string][]string, error)
|
|||
loadedPayloads[name] = payloads
|
||||
}
|
||||
case interface{}:
|
||||
fmt.Printf("%v elements\n", pt)
|
||||
loadedPayloads[name] = cast.ToStringSlice(pt)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
)
|
||||
|
@ -66,7 +65,7 @@ type Request struct {
|
|||
options *protocols.ExecuterOptions
|
||||
attackType generators.Type
|
||||
totalRequests int
|
||||
customHeaders types.StringSlice
|
||||
customHeaders []string
|
||||
generator *generators.Generator // optional, only enabled when using payloads
|
||||
httpClient *retryablehttp.Client
|
||||
rawhttpClient *rawhttp.Client
|
||||
|
|
|
@ -213,7 +213,7 @@ func (r *Request) executeRequest(reqURL string, request *generatedRequest, dynam
|
|||
builder := &strings.Builder{}
|
||||
builder.WriteString("User-Agent: ")
|
||||
builder.WriteString(uarand.GetRandom())
|
||||
r.customHeaders.Set(builder.String())
|
||||
r.customHeaders = append(r.customHeaders, builder.String())
|
||||
}
|
||||
r.setCustomHeaders(request)
|
||||
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Options contains the configuration options for nuclei scanner.
|
||||
type Options struct {
|
||||
// RandomAgent generates random User-Agent
|
||||
RandomAgent bool
|
||||
// Metrics enables display of metrics via an http endpoint
|
||||
Metrics bool
|
||||
// Sandbox mode allows users to run isolated workflows with system commands disabled
|
||||
Sandbox bool
|
||||
// Debug mode allows debugging request/responses for the engine
|
||||
Debug bool
|
||||
// Silent suppresses any extra text and only writes found URLs on screen.
|
||||
|
@ -44,8 +38,6 @@ type Options struct {
|
|||
Project bool
|
||||
// MetricsPort is the port to show metrics on
|
||||
MetricsPort int
|
||||
// MaxWorkflowDuration is the maximum time a workflow can run for a URL
|
||||
MaxWorkflowDuration int
|
||||
// BulkSize is the of targets analyzed in parallel for each template
|
||||
BulkSize int
|
||||
// TemplateThreads is the number of templates executed in parallel
|
||||
|
@ -56,14 +48,12 @@ type Options struct {
|
|||
Retries int
|
||||
// Rate-Limit is the maximum number of requests per specified target
|
||||
RateLimit int
|
||||
// Thread controls the number of concurrent requests to make.
|
||||
Threads int
|
||||
// BurpCollaboratorBiid is the Burp Collaborator BIID for polling interactions.
|
||||
BurpCollaboratorBiid string
|
||||
// ProjectPath allows nuclei to use a user defined project folder
|
||||
ProjectPath string
|
||||
// Severity filters templates based on their severity and only run the matching ones.
|
||||
Severity string
|
||||
Severity []string
|
||||
// Target is a single URL/Domain to scan using a template
|
||||
Target string
|
||||
// Targets specifies the targets to scan using templates.
|
||||
|
@ -79,23 +69,9 @@ type Options struct {
|
|||
// TraceLogFile specifies a file to write with the trace of all requests
|
||||
TraceLogFile string
|
||||
// Templates specifies the template/templates to use
|
||||
Templates StringSlice
|
||||
Templates []string
|
||||
// ExcludedTemplates specifies the template/templates to exclude
|
||||
ExcludedTemplates StringSlice
|
||||
ExcludedTemplates []string
|
||||
// CustomHeaders is the list of custom global headers to send with each request.
|
||||
CustomHeaders StringSlice
|
||||
}
|
||||
|
||||
// StringSlice is a slice of strings as input
|
||||
type StringSlice []string
|
||||
|
||||
// String returns the stringified version of string slice
|
||||
func (s *StringSlice) String() string {
|
||||
return strings.Join(*s, ",")
|
||||
}
|
||||
|
||||
// Set appends a value to the string slice
|
||||
func (s *StringSlice) Set(value string) error {
|
||||
*s = append(*s, value)
|
||||
return nil
|
||||
CustomHeaders []string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue