From 0068ef747481d36ab8dcb785f87813bd3b5c6f87 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Jun 2023 18:04:39 +0300 Subject: [PATCH 1/7] blacklist detection --- CHANGELOG.md | 1 + main.go | 1 + pkg/ffuf/blacklist_detection.go | 90 +++++++++++++++++++++++++++++++++ pkg/ffuf/config.go | 1 + pkg/ffuf/configmarshaller.go | 1 + pkg/ffuf/job.go | 7 +++ pkg/ffuf/optionsparser.go | 7 +++ 7 files changed, 108 insertions(+) create mode 100644 pkg/ffuf/blacklist_detection.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 35eeef69..e3c95b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Changelog - master - New + - New command line parameter for blacklist response detection `-blacklist-detection` - Changed - Explicitly allow TLS1.0 - Fix markdown output file format diff --git a/main.go b/main.go index dcbdc05e..12f046c8 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.BoolVar(&opts.Output.OutputSkipEmptyFile, "or", opts.Output.OutputSkipEmptyFile, "Don't create the output file if we don't have results") flag.BoolVar(&opts.General.AutoCalibration, "ac", opts.General.AutoCalibration, "Automatically calibrate filtering options") flag.BoolVar(&opts.General.AutoCalibrationPerHost, "ach", opts.General.AutoCalibration, "Per host autocalibration") + flag.BoolVar(&opts.General.BlackListDetection, "blacklist-detection", opts.General.BlackListDetection, "Uses a list of commonly blacklisted files to discover & filter blacklisted responses") flag.BoolVar(&opts.General.Colors, "c", opts.General.Colors, "Colorize output.") flag.BoolVar(&opts.General.Json, "json", opts.General.Json, "JSON output, printing newline-delimited JSON records") flag.BoolVar(&opts.General.Noninteractive, "noninteractive", opts.General.Noninteractive, "Disable the interactive console functionality") diff --git a/pkg/ffuf/blacklist_detection.go b/pkg/ffuf/blacklist_detection.go new file mode 100644 index 00000000..beb9b1b2 --- /dev/null +++ b/pkg/ffuf/blacklist_detection.go @@ -0,0 +1,90 @@ +package ffuf + +import ( + "fmt" + "log" +) + +func (j *Job) blackListFiles() map[string][]string { + blackListedFiles := make(map[string][]string) + + blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".htaccess") + blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], "web.config") + blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".bash_history") + blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".git/HEAD") + blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".ssh/id_rsa") + + return blackListedFiles +} + +func (j *Job) blackListRequest(input map[string][]byte) (Response, error) { + basereq := BaseRequest(j.Config) + req, err := j.Runner.Prepare(input, &basereq) + if err != nil { + j.Output.Error(fmt.Sprintf("Encountered an error while preparing blacklist detection request: %s\n", err)) + j.incError() + log.Printf("%s", err) + return Response{}, err + } + + resp, err := j.Runner.Execute(&req) + if err != nil { + j.Output.Error(fmt.Sprintf("Encountered an error while executing blacklist detection request: %s\n", err)) + j.incError() + log.Printf("%s", err) + return Response{}, err + } + + return resp, nil +} + +func (j *Job) DetectBlacklist(host string, inputs map[string][]byte) error { + j.blacklistMutex.Lock() + defer j.blacklistMutex.Unlock() + if j.blacklistChecked { + return nil + } + + responses := make([]Response, 0) + input := make(map[string][]byte) + + blacklist := j.blackListFiles() + for _, f := range blacklist { + for _, cs := range f { + input[j.Config.AutoCalibrationKeyword] = []byte(cs) + + resp, err := j.blackListRequest(input) + if err != nil { + continue + } + responses = append(responses, resp) + } + } + + successResponses := make(map[int64][]Response, 0) + blackListedResponses := make([]Response, 0) + for _, r := range responses { + if r.StatusCode != 200 { + blackListedResponses = append(blackListedResponses, r) + } else { + successResponses[r.ContentWords] = append(successResponses[r.ContentWords], r) + } + } + + for _, r := range successResponses { + if len(r) > 1 { + err := j.calibrateFilters(r, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath*/) + if err != nil { + j.Output.Error(fmt.Sprintf("%s", err)) + } + } + } + + err := j.calibrateFilters(responses, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath*/) + if err != nil { + j.Output.Error(fmt.Sprintf("%s", err)) + } + + j.blacklistChecked = true + return nil +} diff --git a/pkg/ffuf/config.go b/pkg/ffuf/config.go index 81e3a39d..342af371 100644 --- a/pkg/ffuf/config.go +++ b/pkg/ffuf/config.go @@ -10,6 +10,7 @@ type Config struct { AutoCalibrationPerHost bool `json:"autocalibration_perhost"` AutoCalibrationStrategy string `json:"autocalibration_strategy"` AutoCalibrationStrings []string `json:"autocalibration_strings"` + BlackListDetection bool `json:"blacklist_detection"` Cancel context.CancelFunc `json:"-"` Colors bool `json:"colors"` CommandKeywords []string `json:"-"` diff --git a/pkg/ffuf/configmarshaller.go b/pkg/ffuf/configmarshaller.go index ce733a21..19624fee 100644 --- a/pkg/ffuf/configmarshaller.go +++ b/pkg/ffuf/configmarshaller.go @@ -32,6 +32,7 @@ func (c *Config) ToOptions() ConfigOptions { o.General.AutoCalibrationPerHost = c.AutoCalibrationPerHost o.General.AutoCalibrationStrategy = c.AutoCalibrationStrategy o.General.AutoCalibrationStrings = c.AutoCalibrationStrings + o.General.BlackListDetection = c.BlackListDetection o.General.Colors = c.Colors o.General.ConfigFile = "" if c.Delay.HasDelay { diff --git a/pkg/ffuf/job.go b/pkg/ffuf/job.go index e80bddf8..c7647554 100644 --- a/pkg/ffuf/job.go +++ b/pkg/ffuf/job.go @@ -39,7 +39,9 @@ type Job struct { skipQueue bool currentDepth int calibMutex sync.Mutex + blacklistMutex sync.Mutex pauseWg sync.WaitGroup + blacklistChecked bool } type QueueJob struct { @@ -430,6 +432,11 @@ func (j *Job) runTask(input map[string][]byte, position int, retried bool) { } j.pauseWg.Wait() + // use a list of commonly blacklisted files to detect & filter blacklist responses + if j.Config.BlackListDetection { + j.DetectBlacklist(HostURLFromRequest(req), input) + } + // Handle autocalibration, must be done after the actual request to ensure sane value in req.Host _ = j.CalibrateIfNeeded(HostURLFromRequest(req), input) diff --git a/pkg/ffuf/optionsparser.go b/pkg/ffuf/optionsparser.go index 6b5cd59c..95de608b 100644 --- a/pkg/ffuf/optionsparser.go +++ b/pkg/ffuf/optionsparser.go @@ -49,6 +49,7 @@ type GeneralOptions struct { AutoCalibrationPerHost bool `json:"autocalibration_per_host"` AutoCalibrationStrategy string `json:"autocalibration_strategy"` AutoCalibrationStrings []string `json:"autocalibration_strings"` + BlackListDetection bool `json:"blacklist_detection"` Colors bool `json:"colors"` ConfigFile string `toml:"-" json:"config_file"` Delay string `json:"delay"` @@ -478,6 +479,7 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.AutoCalibration = parseOpts.General.AutoCalibration conf.AutoCalibrationPerHost = parseOpts.General.AutoCalibrationPerHost conf.AutoCalibrationStrategy = parseOpts.General.AutoCalibrationStrategy + conf.BlackListDetection = parseOpts.General.BlackListDetection conf.Threads = parseOpts.General.Threads conf.Timeout = parseOpts.HTTP.Timeout conf.MaxTime = parseOpts.General.MaxTime @@ -515,6 +517,11 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.AutoCalibration = true } + if conf.BlackListDetection { + // BlackListDetection implies AutoCalibration + conf.AutoCalibration = true + } + // Handle copy as curl situation where POST method is implied by --data flag. If method is set to anything but GET, NOOP if len(conf.Data) > 0 && conf.Method == "GET" && From d902b95da96cdc4c397f1c13e2c2f30a2744992a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Jun 2023 09:52:06 +0300 Subject: [PATCH 2/7] added option to help.go --- help.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help.go b/help.go index 0f5fa430..bbb9f8f0 100644 --- a/help.go +++ b/help.go @@ -61,7 +61,7 @@ func Usage() { Description: "", Flags: make([]UsageFlag, 0), Hidden: false, - ExpectedFlags: []string{"ac", "acc", "ack", "ach", "acs", "c", "config", "json", "maxtime", "maxtime-job", "noninteractive", "p", "rate", "scraperfile", "scrapers", "search", "s", "sa", "se", "sf", "t", "v", "V"}, + ExpectedFlags: []string{"ac", "acc", "ack", "ach", "acs", "blacklist-detection", "c", "config", "json", "maxtime", "maxtime-job", "noninteractive", "p", "rate", "scraperfile", "scrapers", "search", "s", "sa", "se", "sf", "t", "v", "V"}, } u_compat := UsageSection{ Name: "COMPATIBILITY OPTIONS", From fcecd0a81b6cef6461fe5efe23def5fe1e1401d9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 22 Jun 2023 12:19:43 +0300 Subject: [PATCH 3/7] refactored -blacklist-detection to autocalibrationstrategy extra --- CHANGELOG.md | 2 +- help.go | 2 +- main.go | 3 +-- pkg/ffuf/autocalibration.go | 2 +- pkg/ffuf/config.go | 1 - pkg/ffuf/configmarshaller.go | 1 - pkg/ffuf/job.go | 2 +- pkg/ffuf/optionsparser.go | 7 ------- 8 files changed, 5 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3c95b13..d6432b5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Changelog - master - New - - New command line parameter for blacklist response detection `-blacklist-detection` + - New autocalibration strategy "extra" that uses a list of commonly blacklisted files to filter blacklist responses - Changed - Explicitly allow TLS1.0 - Fix markdown output file format diff --git a/help.go b/help.go index bbb9f8f0..0f5fa430 100644 --- a/help.go +++ b/help.go @@ -61,7 +61,7 @@ func Usage() { Description: "", Flags: make([]UsageFlag, 0), Hidden: false, - ExpectedFlags: []string{"ac", "acc", "ack", "ach", "acs", "blacklist-detection", "c", "config", "json", "maxtime", "maxtime-job", "noninteractive", "p", "rate", "scraperfile", "scrapers", "search", "s", "sa", "se", "sf", "t", "v", "V"}, + ExpectedFlags: []string{"ac", "acc", "ack", "ach", "acs", "c", "config", "json", "maxtime", "maxtime-job", "noninteractive", "p", "rate", "scraperfile", "scrapers", "search", "s", "sa", "se", "sf", "t", "v", "V"}, } u_compat := UsageSection{ Name: "COMPATIBILITY OPTIONS", diff --git a/main.go b/main.go index 12f046c8..a2a85942 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,6 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.BoolVar(&opts.Output.OutputSkipEmptyFile, "or", opts.Output.OutputSkipEmptyFile, "Don't create the output file if we don't have results") flag.BoolVar(&opts.General.AutoCalibration, "ac", opts.General.AutoCalibration, "Automatically calibrate filtering options") flag.BoolVar(&opts.General.AutoCalibrationPerHost, "ach", opts.General.AutoCalibration, "Per host autocalibration") - flag.BoolVar(&opts.General.BlackListDetection, "blacklist-detection", opts.General.BlackListDetection, "Uses a list of commonly blacklisted files to discover & filter blacklisted responses") flag.BoolVar(&opts.General.Colors, "c", opts.General.Colors, "Colorize output.") flag.BoolVar(&opts.General.Json, "json", opts.General.Json, "JSON output, printing newline-delimited JSON records") flag.BoolVar(&opts.General.Noninteractive, "noninteractive", opts.General.Noninteractive, "Disable the interactive console functionality") @@ -89,7 +88,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.IntVar(&opts.HTTP.Timeout, "timeout", opts.HTTP.Timeout, "HTTP request timeout in seconds.") flag.IntVar(&opts.Input.InputNum, "input-num", opts.Input.InputNum, "Number of inputs to test. Used in conjunction with --input-cmd.") flag.StringVar(&opts.General.AutoCalibrationKeyword, "ack", opts.General.AutoCalibrationKeyword, "Autocalibration keyword") - flag.StringVar(&opts.General.AutoCalibrationStrategy, "acs", opts.General.AutoCalibrationStrategy, "Autocalibration strategy: \"basic\" or \"advanced\"") + flag.StringVar(&opts.General.AutoCalibrationStrategy, "acs", opts.General.AutoCalibrationStrategy, "Autocalibration strategy: \"basic\", \"advanced\" or\"extra\"") flag.StringVar(&opts.General.ConfigFile, "config", "", "Load configuration from a file") flag.StringVar(&opts.General.ScraperFile, "scraperfile", "", "Custom scraper file path") flag.StringVar(&opts.General.Scrapers, "scrapers", opts.General.Scrapers, "Active scraper groups") diff --git a/pkg/ffuf/autocalibration.go b/pkg/ffuf/autocalibration.go index e9a5aeb3..b8b467d9 100644 --- a/pkg/ffuf/autocalibration.go +++ b/pkg/ffuf/autocalibration.go @@ -18,7 +18,7 @@ func (j *Job) autoCalibrationStrings() map[string][]string { cInputs["htaccess"] = append(cInputs["htaccess"], ".htaccess"+RandomString(8)) cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(16)) cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(8)) - if j.Config.AutoCalibrationStrategy == "advanced" { + if j.Config.AutoCalibrationStrategy == "advanced" || j.Config.AutoCalibrationStrategy == "extra" { // Add directory tests and .htaccess too cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(16)+"/") cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(8)+"/") diff --git a/pkg/ffuf/config.go b/pkg/ffuf/config.go index 342af371..81e3a39d 100644 --- a/pkg/ffuf/config.go +++ b/pkg/ffuf/config.go @@ -10,7 +10,6 @@ type Config struct { AutoCalibrationPerHost bool `json:"autocalibration_perhost"` AutoCalibrationStrategy string `json:"autocalibration_strategy"` AutoCalibrationStrings []string `json:"autocalibration_strings"` - BlackListDetection bool `json:"blacklist_detection"` Cancel context.CancelFunc `json:"-"` Colors bool `json:"colors"` CommandKeywords []string `json:"-"` diff --git a/pkg/ffuf/configmarshaller.go b/pkg/ffuf/configmarshaller.go index 19624fee..ce733a21 100644 --- a/pkg/ffuf/configmarshaller.go +++ b/pkg/ffuf/configmarshaller.go @@ -32,7 +32,6 @@ func (c *Config) ToOptions() ConfigOptions { o.General.AutoCalibrationPerHost = c.AutoCalibrationPerHost o.General.AutoCalibrationStrategy = c.AutoCalibrationStrategy o.General.AutoCalibrationStrings = c.AutoCalibrationStrings - o.General.BlackListDetection = c.BlackListDetection o.General.Colors = c.Colors o.General.ConfigFile = "" if c.Delay.HasDelay { diff --git a/pkg/ffuf/job.go b/pkg/ffuf/job.go index c7647554..0e5bc307 100644 --- a/pkg/ffuf/job.go +++ b/pkg/ffuf/job.go @@ -433,7 +433,7 @@ func (j *Job) runTask(input map[string][]byte, position int, retried bool) { j.pauseWg.Wait() // use a list of commonly blacklisted files to detect & filter blacklist responses - if j.Config.BlackListDetection { + if j.Config.AutoCalibrationStrategy == "extra" { j.DetectBlacklist(HostURLFromRequest(req), input) } diff --git a/pkg/ffuf/optionsparser.go b/pkg/ffuf/optionsparser.go index 95de608b..6b5cd59c 100644 --- a/pkg/ffuf/optionsparser.go +++ b/pkg/ffuf/optionsparser.go @@ -49,7 +49,6 @@ type GeneralOptions struct { AutoCalibrationPerHost bool `json:"autocalibration_per_host"` AutoCalibrationStrategy string `json:"autocalibration_strategy"` AutoCalibrationStrings []string `json:"autocalibration_strings"` - BlackListDetection bool `json:"blacklist_detection"` Colors bool `json:"colors"` ConfigFile string `toml:"-" json:"config_file"` Delay string `json:"delay"` @@ -479,7 +478,6 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.AutoCalibration = parseOpts.General.AutoCalibration conf.AutoCalibrationPerHost = parseOpts.General.AutoCalibrationPerHost conf.AutoCalibrationStrategy = parseOpts.General.AutoCalibrationStrategy - conf.BlackListDetection = parseOpts.General.BlackListDetection conf.Threads = parseOpts.General.Threads conf.Timeout = parseOpts.HTTP.Timeout conf.MaxTime = parseOpts.General.MaxTime @@ -517,11 +515,6 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.AutoCalibration = true } - if conf.BlackListDetection { - // BlackListDetection implies AutoCalibration - conf.AutoCalibration = true - } - // Handle copy as curl situation where POST method is implied by --data flag. If method is set to anything but GET, NOOP if len(conf.Data) > 0 && conf.Method == "GET" && From dee5d496821d3a68478aeac87e3f7a04e277506f Mon Sep 17 00:00:00 2001 From: Aristos Miliaressis Date: Sat, 1 Jul 2023 13:12:39 +0300 Subject: [PATCH 4/7] "No common filtering values found" fixed --- pkg/ffuf/blacklist_detection.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/ffuf/blacklist_detection.go b/pkg/ffuf/blacklist_detection.go index beb9b1b2..c6098ce6 100644 --- a/pkg/ffuf/blacklist_detection.go +++ b/pkg/ffuf/blacklist_detection.go @@ -62,10 +62,10 @@ func (j *Job) DetectBlacklist(host string, inputs map[string][]byte) error { } successResponses := make(map[int64][]Response, 0) - blackListedResponses := make([]Response, 0) + blackListedResponses := make(map[int64][]Response, 0) for _, r := range responses { if r.StatusCode != 200 { - blackListedResponses = append(blackListedResponses, r) + blackListedResponses[r.ContentWords] = append(blackListedResponses[r.ContentWords], r) } else { successResponses[r.ContentWords] = append(successResponses[r.ContentWords], r) } @@ -80,9 +80,13 @@ func (j *Job) DetectBlacklist(host string, inputs map[string][]byte) error { } } - err := j.calibrateFilters(responses, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath*/) - if err != nil { - j.Output.Error(fmt.Sprintf("%s", err)) + for _, r := range blackListedResponses { + if len(r) > 1 { + err := j.calibrateFilters(r, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath */) + if err != nil { + j.Output.Error(fmt.Sprintf("%s", err)) + } + } } j.blacklistChecked = true From 206e98896c52ce67b5700f57f4f9d35a54c69f88 Mon Sep 17 00:00:00 2001 From: Aristos Miliaressis Date: Sat, 1 Jul 2023 13:42:24 +0300 Subject: [PATCH 5/7] added wildcard not found detection --- pkg/ffuf/blacklist_detection.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pkg/ffuf/blacklist_detection.go b/pkg/ffuf/blacklist_detection.go index c6098ce6..05684f93 100644 --- a/pkg/ffuf/blacklist_detection.go +++ b/pkg/ffuf/blacklist_detection.go @@ -8,11 +8,18 @@ import ( func (j *Job) blackListFiles() map[string][]string { blackListedFiles := make(map[string][]string) - blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".htaccess") - blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], "web.config") - blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".bash_history") - blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".git/HEAD") - blackListedFiles["FUZZ"] = append(blackListedFiles["FUZZ"], ".ssh/id_rsa") + // if a couple of those returns same size, its probably a blacklisted response. + blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".htaccess") + blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], "web.config") + blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".bash_history") + blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".git/HEAD") + blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".ssh/id_rsa") + + // if a couple of those returns same size, its probably a wildcard not found response. + blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc.xyz") + blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc2.xyz") + blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc3.xyzz") + blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "xyz1.abc") return blackListedFiles } From 4f304a47a4fad224076ece01571e3e8b87ddc64b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Jul 2023 14:54:58 +0300 Subject: [PATCH 6/7] custom auto-calibration strategies --- CHANGELOG.md | 2 +- main.go | 10 ++- pkg/ffuf/autocalibration.go | 79 +++++++++++++++++---- pkg/ffuf/blacklist_detection.go | 101 --------------------------- pkg/ffuf/config.go | 118 ++++++++++++++++---------------- pkg/ffuf/configmarshaller.go | 2 +- pkg/ffuf/constants.go | 1 + pkg/ffuf/job.go | 5 -- pkg/ffuf/optionsparser.go | 58 +++++++++------- pkg/ffuf/util.go | 19 +++++ pkg/input/input.go | 11 +-- 11 files changed, 187 insertions(+), 219 deletions(-) delete mode 100644 pkg/ffuf/blacklist_detection.go diff --git a/CHANGELOG.md b/CHANGELOG.md index d6432b5b..9cea538e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Changelog - master - New - - New autocalibration strategy "extra" that uses a list of commonly blacklisted files to filter blacklist responses + - autocalibration-strategy refactored to support extensible strategy configuration - Changed - Explicitly allow TLS1.0 - Fix markdown output file format diff --git a/main.go b/main.go index a2a85942..a6b23a68 100644 --- a/main.go +++ b/main.go @@ -50,7 +50,7 @@ func (m *wordlistFlag) Set(value string) error { // ParseFlags parses the command line flags and (re)populates the ConfigOptions struct func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { var ignored bool - var cookies, autocalibrationstrings, headers, inputcommands multiStringFlag + var cookies, autocalibrationstrings, autocalibrationstrategies, headers, inputcommands multiStringFlag var wordlists wordlistFlag cookies = opts.HTTP.Cookies @@ -88,7 +88,6 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.IntVar(&opts.HTTP.Timeout, "timeout", opts.HTTP.Timeout, "HTTP request timeout in seconds.") flag.IntVar(&opts.Input.InputNum, "input-num", opts.Input.InputNum, "Number of inputs to test. Used in conjunction with --input-cmd.") flag.StringVar(&opts.General.AutoCalibrationKeyword, "ack", opts.General.AutoCalibrationKeyword, "Autocalibration keyword") - flag.StringVar(&opts.General.AutoCalibrationStrategy, "acs", opts.General.AutoCalibrationStrategy, "Autocalibration strategy: \"basic\", \"advanced\" or\"extra\"") flag.StringVar(&opts.General.ConfigFile, "config", "", "Load configuration from a file") flag.StringVar(&opts.General.ScraperFile, "scraperfile", "", "Custom scraper file path") flag.StringVar(&opts.General.Scrapers, "scrapers", opts.General.Scrapers, "Active scraper groups") @@ -128,6 +127,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.StringVar(&opts.Output.OutputFile, "o", opts.Output.OutputFile, "Write output to file") flag.StringVar(&opts.Output.OutputFormat, "of", opts.Output.OutputFormat, "Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats)") flag.Var(&autocalibrationstrings, "acc", "Custom auto-calibration string. Can be used multiple times. Implies -ac") + flag.Var(&autocalibrationstrategies, "acs", "Custom auto-calibration strategies. Can be used multiple times. Implies -ac") flag.Var(&cookies, "b", "Cookie data `\"NAME1=VALUE1; NAME2=VALUE2\"` for copy as curl functionality.") flag.Var(&cookies, "cookie", "Cookie data (alias of -b)") flag.Var(&headers, "H", "Header `\"Name: Value\"`, separated by colon. Multiple -H flags are accepted.") @@ -137,6 +137,12 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.Parse() opts.General.AutoCalibrationStrings = autocalibrationstrings + if len(autocalibrationstrategies) > 0 { + opts.General.AutoCalibrationStrategies = []string {} + for _, strategy := range autocalibrationstrategies { + opts.General.AutoCalibrationStrategies = append(opts.General.AutoCalibrationStrategies, strings.Split(strategy, ",")...) + } + } opts.HTTP.Cookies = cookies opts.HTTP.Headers = headers opts.Input.Inputcommands = inputcommands diff --git a/pkg/ffuf/autocalibration.go b/pkg/ffuf/autocalibration.go index b8b467d9..f0bfcd51 100644 --- a/pkg/ffuf/autocalibration.go +++ b/pkg/ffuf/autocalibration.go @@ -6,31 +6,80 @@ import ( "math/rand" "strconv" "time" + "encoding/json" + "path/filepath" + "os" ) +type AutocalibrationStrategy map[string][]string + func (j *Job) autoCalibrationStrings() map[string][]string { rand.Seed(time.Now().UnixNano()) cInputs := make(map[string][]string) - if len(j.Config.AutoCalibrationStrings) < 1 { - cInputs["basic_admin"] = append(cInputs["basic_admin"], "admin"+RandomString(16)) - cInputs["basic_admin"] = append(cInputs["basic_admin"], "admin"+RandomString(8)) - cInputs["htaccess"] = append(cInputs["htaccess"], ".htaccess"+RandomString(16)) - cInputs["htaccess"] = append(cInputs["htaccess"], ".htaccess"+RandomString(8)) - cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(16)) - cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(8)) - if j.Config.AutoCalibrationStrategy == "advanced" || j.Config.AutoCalibrationStrategy == "extra" { - // Add directory tests and .htaccess too - cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(16)+"/") - cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(8)+"/") - cInputs["random_dir"] = append(cInputs["random_dir"], RandomString(16)+"/") - cInputs["random_dir"] = append(cInputs["random_dir"], RandomString(8)+"/") - } - } else { + + if len(j.Config.AutoCalibrationStrings) > 0 { cInputs["custom"] = append(cInputs["custom"], j.Config.AutoCalibrationStrings...) + return cInputs + } + + for _, strategy := range j.Config.AutoCalibrationStrategies { + jsonStrategy, err := os.ReadFile(filepath.Join(AUTOCALIBDIR, strategy+".json")) + if err != nil { + j.Output.Warning(fmt.Sprintf("Skipping strategy \"%s\" because of error: %s\n", strategy, err)) + continue + } + + tmpStrategy := AutocalibrationStrategy{} + err = json.Unmarshal(jsonStrategy, &tmpStrategy) + if err != nil { + j.Output.Warning(fmt.Sprintf("Skipping strategy \"%s\" because of error: %s\n", strategy, err)) + continue + } + + cInputs = mergeMaps(cInputs, tmpStrategy) + } + return cInputs } +func setupDefaultAutocalibrationStrategies() error { + basic_strategy := AutocalibrationStrategy { + "basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)}, + "htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)}, + "basic_random": []string{RandomString(16), RandomString(8)}, + } + basic_strategy_json, err := json.Marshal(basic_strategy) + if err != nil { + return err + } + + advanced_strategy := AutocalibrationStrategy { + "basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)}, + "htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)}, + "basic_random": []string{RandomString(16), RandomString(8)}, + "admin_dir": []string{"admin"+RandomString(16)+"/", "admin"+RandomString(8)+"/"}, + "random_dir": []string{RandomString(16)+"/", RandomString(8)+"/"}, + } + advanced_strategy_json, err := json.Marshal(advanced_strategy) + if err != nil { + return err + } + + basic_strategy_file := filepath.Join(AUTOCALIBDIR, "basic.json") + if !FileExists(basic_strategy_file) { + err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "basic.json"), basic_strategy_json, 0640) + return err + } + advanced_strategy_file := filepath.Join(AUTOCALIBDIR, "advanced.json") + if !FileExists(advanced_strategy_file) { + err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "advanced.json"), advanced_strategy_json, 0640) + return err + } + + return nil +} + func (j *Job) calibrationRequest(inputs map[string][]byte) (Response, error) { basereq := BaseRequest(j.Config) req, err := j.Runner.Prepare(inputs, &basereq) diff --git a/pkg/ffuf/blacklist_detection.go b/pkg/ffuf/blacklist_detection.go deleted file mode 100644 index 05684f93..00000000 --- a/pkg/ffuf/blacklist_detection.go +++ /dev/null @@ -1,101 +0,0 @@ -package ffuf - -import ( - "fmt" - "log" -) - -func (j *Job) blackListFiles() map[string][]string { - blackListedFiles := make(map[string][]string) - - // if a couple of those returns same size, its probably a blacklisted response. - blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".htaccess") - blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], "web.config") - blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".bash_history") - blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".git/HEAD") - blackListedFiles["BLACKLIST"] = append(blackListedFiles["BLACKLIST"], ".ssh/id_rsa") - - // if a couple of those returns same size, its probably a wildcard not found response. - blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc.xyz") - blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc2.xyz") - blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "abc3.xyzz") - blackListedFiles["WILDCARD"] = append(blackListedFiles["WILDCARD"], "xyz1.abc") - - return blackListedFiles -} - -func (j *Job) blackListRequest(input map[string][]byte) (Response, error) { - basereq := BaseRequest(j.Config) - req, err := j.Runner.Prepare(input, &basereq) - if err != nil { - j.Output.Error(fmt.Sprintf("Encountered an error while preparing blacklist detection request: %s\n", err)) - j.incError() - log.Printf("%s", err) - return Response{}, err - } - - resp, err := j.Runner.Execute(&req) - if err != nil { - j.Output.Error(fmt.Sprintf("Encountered an error while executing blacklist detection request: %s\n", err)) - j.incError() - log.Printf("%s", err) - return Response{}, err - } - - return resp, nil -} - -func (j *Job) DetectBlacklist(host string, inputs map[string][]byte) error { - j.blacklistMutex.Lock() - defer j.blacklistMutex.Unlock() - if j.blacklistChecked { - return nil - } - - responses := make([]Response, 0) - input := make(map[string][]byte) - - blacklist := j.blackListFiles() - for _, f := range blacklist { - for _, cs := range f { - input[j.Config.AutoCalibrationKeyword] = []byte(cs) - - resp, err := j.blackListRequest(input) - if err != nil { - continue - } - responses = append(responses, resp) - } - } - - successResponses := make(map[int64][]Response, 0) - blackListedResponses := make(map[int64][]Response, 0) - for _, r := range responses { - if r.StatusCode != 200 { - blackListedResponses[r.ContentWords] = append(blackListedResponses[r.ContentWords], r) - } else { - successResponses[r.ContentWords] = append(successResponses[r.ContentWords], r) - } - } - - for _, r := range successResponses { - if len(r) > 1 { - err := j.calibrateFilters(r, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath*/) - if err != nil { - j.Output.Error(fmt.Sprintf("%s", err)) - } - } - } - - for _, r := range blackListedResponses { - if len(r) > 1 { - err := j.calibrateFilters(r, j.Config.AutoCalibrationPerHost /*, j.Config.AutoCalibrationPerPath */) - if err != nil { - j.Output.Error(fmt.Sprintf("%s", err)) - } - } - } - - j.blacklistChecked = true - return nil -} diff --git a/pkg/ffuf/config.go b/pkg/ffuf/config.go index 81e3a39d..d79095bf 100644 --- a/pkg/ffuf/config.go +++ b/pkg/ffuf/config.go @@ -5,64 +5,64 @@ import ( ) type Config struct { - AutoCalibration bool `json:"autocalibration"` - AutoCalibrationKeyword string `json:"autocalibration_keyword"` - AutoCalibrationPerHost bool `json:"autocalibration_perhost"` - AutoCalibrationStrategy string `json:"autocalibration_strategy"` - AutoCalibrationStrings []string `json:"autocalibration_strings"` - Cancel context.CancelFunc `json:"-"` - Colors bool `json:"colors"` - CommandKeywords []string `json:"-"` - CommandLine string `json:"cmdline"` - ConfigFile string `json:"configfile"` - Context context.Context `json:"-"` - Data string `json:"postdata"` - Debuglog string `json:"debuglog"` - Delay optRange `json:"delay"` - DirSearchCompat bool `json:"dirsearch_compatibility"` - Extensions []string `json:"extensions"` - FilterMode string `json:"fmode"` - FollowRedirects bool `json:"follow_redirects"` - Headers map[string]string `json:"headers"` - IgnoreBody bool `json:"ignorebody"` - IgnoreWordlistComments bool `json:"ignore_wordlist_comments"` - InputMode string `json:"inputmode"` - InputNum int `json:"cmd_inputnum"` - InputProviders []InputProviderConfig `json:"inputproviders"` - InputShell string `json:"inputshell"` - Json bool `json:"json"` - MatcherManager MatcherManager `json:"matchers"` - MatcherMode string `json:"mmode"` - MaxTime int `json:"maxtime"` - MaxTimeJob int `json:"maxtime_job"` - Method string `json:"method"` - Noninteractive bool `json:"noninteractive"` - OutputDirectory string `json:"outputdirectory"` - OutputFile string `json:"outputfile"` - OutputFormat string `json:"outputformat"` - OutputSkipEmptyFile bool `json:"OutputSkipEmptyFile"` - ProgressFrequency int `json:"-"` - ProxyURL string `json:"proxyurl"` - Quiet bool `json:"quiet"` - Rate int64 `json:"rate"` - Recursion bool `json:"recursion"` - RecursionDepth int `json:"recursion_depth"` - RecursionStrategy string `json:"recursion_strategy"` - ReplayProxyURL string `json:"replayproxyurl"` - RequestFile string `json:"requestfile"` - RequestProto string `json:"requestproto"` - ScraperFile string `json:"scraperfile"` - Scrapers string `json:"scrapers"` - SNI string `json:"sni"` - StopOn403 bool `json:"stop_403"` - StopOnAll bool `json:"stop_all"` - StopOnErrors bool `json:"stop_errors"` - Threads int `json:"threads"` - Timeout int `json:"timeout"` - Url string `json:"url"` - Verbose bool `json:"verbose"` - Wordlists []string `json:"wordlists"` - Http2 bool `json:"http2"` + AutoCalibration bool `json:"autocalibration"` + AutoCalibrationKeyword string `json:"autocalibration_keyword"` + AutoCalibrationPerHost bool `json:"autocalibration_perhost"` + AutoCalibrationStrategies []string `json:"autocalibration_strategies"` + AutoCalibrationStrings []string `json:"autocalibration_strings"` + Cancel context.CancelFunc `json:"-"` + Colors bool `json:"colors"` + CommandKeywords []string `json:"-"` + CommandLine string `json:"cmdline"` + ConfigFile string `json:"configfile"` + Context context.Context `json:"-"` + Data string `json:"postdata"` + Debuglog string `json:"debuglog"` + Delay optRange `json:"delay"` + DirSearchCompat bool `json:"dirsearch_compatibility"` + Extensions []string `json:"extensions"` + FilterMode string `json:"fmode"` + FollowRedirects bool `json:"follow_redirects"` + Headers map[string]string `json:"headers"` + IgnoreBody bool `json:"ignorebody"` + IgnoreWordlistComments bool `json:"ignore_wordlist_comments"` + InputMode string `json:"inputmode"` + InputNum int `json:"cmd_inputnum"` + InputProviders []InputProviderConfig `json:"inputproviders"` + InputShell string `json:"inputshell"` + Json bool `json:"json"` + MatcherManager MatcherManager `json:"matchers"` + MatcherMode string `json:"mmode"` + MaxTime int `json:"maxtime"` + MaxTimeJob int `json:"maxtime_job"` + Method string `json:"method"` + Noninteractive bool `json:"noninteractive"` + OutputDirectory string `json:"outputdirectory"` + OutputFile string `json:"outputfile"` + OutputFormat string `json:"outputformat"` + OutputSkipEmptyFile bool `json:"OutputSkipEmptyFile"` + ProgressFrequency int `json:"-"` + ProxyURL string `json:"proxyurl"` + Quiet bool `json:"quiet"` + Rate int64 `json:"rate"` + Recursion bool `json:"recursion"` + RecursionDepth int `json:"recursion_depth"` + RecursionStrategy string `json:"recursion_strategy"` + ReplayProxyURL string `json:"replayproxyurl"` + RequestFile string `json:"requestfile"` + RequestProto string `json:"requestproto"` + ScraperFile string `json:"scraperfile"` + Scrapers string `json:"scrapers"` + SNI string `json:"sni"` + StopOn403 bool `json:"stop_403"` + StopOnAll bool `json:"stop_all"` + StopOnErrors bool `json:"stop_errors"` + Threads int `json:"threads"` + Timeout int `json:"timeout"` + Url string `json:"url"` + Verbose bool `json:"verbose"` + Wordlists []string `json:"wordlists"` + Http2 bool `json:"http2"` } type InputProviderConfig struct { @@ -75,7 +75,7 @@ type InputProviderConfig struct { func NewConfig(ctx context.Context, cancel context.CancelFunc) Config { var conf Config conf.AutoCalibrationKeyword = "FUZZ" - conf.AutoCalibrationStrategy = "basic" + conf.AutoCalibrationStrategies = []string{"basic"} conf.AutoCalibrationStrings = make([]string, 0) conf.CommandKeywords = make([]string, 0) conf.Context = ctx diff --git a/pkg/ffuf/configmarshaller.go b/pkg/ffuf/configmarshaller.go index ce733a21..01a87f1c 100644 --- a/pkg/ffuf/configmarshaller.go +++ b/pkg/ffuf/configmarshaller.go @@ -30,7 +30,7 @@ func (c *Config) ToOptions() ConfigOptions { o.General.AutoCalibration = c.AutoCalibration o.General.AutoCalibrationKeyword = c.AutoCalibrationKeyword o.General.AutoCalibrationPerHost = c.AutoCalibrationPerHost - o.General.AutoCalibrationStrategy = c.AutoCalibrationStrategy + o.General.AutoCalibrationStrategies = c.AutoCalibrationStrategies o.General.AutoCalibrationStrings = c.AutoCalibrationStrings o.General.Colors = c.Colors o.General.ConfigFile = "" diff --git a/pkg/ffuf/constants.go b/pkg/ffuf/constants.go index 1dd00356..033b2678 100644 --- a/pkg/ffuf/constants.go +++ b/pkg/ffuf/constants.go @@ -13,4 +13,5 @@ var ( CONFIGDIR = filepath.Join(xdg.ConfigHome, "ffuf") HISTORYDIR = filepath.Join(CONFIGDIR, "history") SCRAPERDIR = filepath.Join(CONFIGDIR, "scraper") + AUTOCALIBDIR = filepath.Join(CONFIGDIR, "autocalibration") ) diff --git a/pkg/ffuf/job.go b/pkg/ffuf/job.go index 0e5bc307..7fb387b7 100644 --- a/pkg/ffuf/job.go +++ b/pkg/ffuf/job.go @@ -432,11 +432,6 @@ func (j *Job) runTask(input map[string][]byte, position int, retried bool) { } j.pauseWg.Wait() - // use a list of commonly blacklisted files to detect & filter blacklist responses - if j.Config.AutoCalibrationStrategy == "extra" { - j.DetectBlacklist(HostURLFromRequest(req), input) - } - // Handle autocalibration, must be done after the actual request to ensure sane value in req.Host _ = j.CalibrateIfNeeded(HostURLFromRequest(req), input) diff --git a/pkg/ffuf/optionsparser.go b/pkg/ffuf/optionsparser.go index 6b5cd59c..7124f40b 100644 --- a/pkg/ffuf/optionsparser.go +++ b/pkg/ffuf/optionsparser.go @@ -44,29 +44,29 @@ type HTTPOptions struct { } type GeneralOptions struct { - AutoCalibration bool `json:"autocalibration"` - AutoCalibrationKeyword string `json:"autocalibration_keyword"` - AutoCalibrationPerHost bool `json:"autocalibration_per_host"` - AutoCalibrationStrategy string `json:"autocalibration_strategy"` - AutoCalibrationStrings []string `json:"autocalibration_strings"` - Colors bool `json:"colors"` - ConfigFile string `toml:"-" json:"config_file"` - Delay string `json:"delay"` - Json bool `json:"json"` - MaxTime int `json:"maxtime"` - MaxTimeJob int `json:"maxtime_job"` - Noninteractive bool `json:"noninteractive"` - Quiet bool `json:"quiet"` - Rate int `json:"rate"` - ScraperFile string `json:"scraperfile"` - Scrapers string `json:"scrapers"` - Searchhash string `json:"-"` - ShowVersion bool `toml:"-" json:"-"` - StopOn403 bool `json:"stop_on_403"` - StopOnAll bool `json:"stop_on_all"` - StopOnErrors bool `json:"stop_on_errors"` - Threads int `json:"threads"` - Verbose bool `json:"verbose"` + AutoCalibration bool `json:"autocalibration"` + AutoCalibrationKeyword string `json:"autocalibration_keyword"` + AutoCalibrationPerHost bool `json:"autocalibration_per_host"` + AutoCalibrationStrategies []string `json:"autocalibration_strategies"` + AutoCalibrationStrings []string `json:"autocalibration_strings"` + Colors bool `json:"colors"` + ConfigFile string `toml:"-" json:"config_file"` + Delay string `json:"delay"` + Json bool `json:"json"` + MaxTime int `json:"maxtime"` + MaxTimeJob int `json:"maxtime_job"` + Noninteractive bool `json:"noninteractive"` + Quiet bool `json:"quiet"` + Rate int `json:"rate"` + ScraperFile string `json:"scraperfile"` + Scrapers string `json:"scrapers"` + Searchhash string `json:"-"` + ShowVersion bool `toml:"-" json:"-"` + StopOn403 bool `json:"stop_on_403"` + StopOnAll bool `json:"stop_on_all"` + StopOnErrors bool `json:"stop_on_errors"` + Threads int `json:"threads"` + Verbose bool `json:"verbose"` } type InputOptions struct { @@ -122,7 +122,7 @@ func NewConfigOptions() *ConfigOptions { c.Filter.Words = "" c.General.AutoCalibration = false c.General.AutoCalibrationKeyword = "FUZZ" - c.General.AutoCalibrationStrategy = "basic" + c.General.AutoCalibrationStrategies = []string{"basic"} c.General.Colors = false c.General.Delay = "" c.General.Json = false @@ -422,10 +422,18 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con if len(parseOpts.General.AutoCalibrationStrings) > 0 { conf.AutoCalibrationStrings = parseOpts.General.AutoCalibrationStrings } + // Auto-calibration strategies + if len(parseOpts.General.AutoCalibrationStrategies) > 0 { + conf.AutoCalibrationStrategies = parseOpts.General.AutoCalibrationStrategies + } // Using -acc implies -ac if len(parseOpts.General.AutoCalibrationStrings) > 0 { conf.AutoCalibration = true } + // Using -acs implies -ac + if len(parseOpts.General.AutoCalibrationStrategies) > 0 { + conf.AutoCalibration = true + } if parseOpts.General.Rate < 0 { conf.Rate = 0 @@ -477,7 +485,7 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.RecursionStrategy = parseOpts.HTTP.RecursionStrategy conf.AutoCalibration = parseOpts.General.AutoCalibration conf.AutoCalibrationPerHost = parseOpts.General.AutoCalibrationPerHost - conf.AutoCalibrationStrategy = parseOpts.General.AutoCalibrationStrategy + conf.AutoCalibrationStrategies = parseOpts.General.AutoCalibrationStrategies conf.Threads = parseOpts.General.Threads conf.Timeout = parseOpts.HTTP.Timeout conf.MaxTime = parseOpts.General.MaxTime diff --git a/pkg/ffuf/util.go b/pkg/ffuf/util.go index c7f5e136..84d94365 100644 --- a/pkg/ffuf/util.go +++ b/pkg/ffuf/util.go @@ -93,6 +93,14 @@ func CheckOrCreateConfigDir() error { return err } err = createConfigDir(SCRAPERDIR) + if err != nil { + return err + } + err = createConfigDir(AUTOCALIBDIR) + if err != nil { + return err + } + err = setupDefaultAutocalibrationStrategies() return err } @@ -116,3 +124,14 @@ func StrInSlice(key string, slice []string) bool { } return false } + +func mergeMaps(m1 map[string][]string, m2 map[string][]string) map[string][]string { + merged := make(map[string][]string) + for k, v := range m1 { + merged[k] = v + } + for key, value := range m2 { + merged[key] = value + } + return merged +} \ No newline at end of file diff --git a/pkg/input/input.go b/pkg/input/input.go index 993246ae..29ea4cf8 100644 --- a/pkg/input/input.go +++ b/pkg/input/input.go @@ -54,7 +54,7 @@ func (i *MainInputProvider) AddProvider(provider ffuf.InputProviderConfig) error // ActivateKeywords enables / disables wordlists based on list of active keywords func (i *MainInputProvider) ActivateKeywords(kws []string) { for _, p := range i.Providers { - if sliceContains(kws, p.Keyword()) { + if ffuf.StrInSlice(p.Keyword(), kws) { p.Active() } else { p.Disable() @@ -231,12 +231,3 @@ func (i *MainInputProvider) Total() int { return count } -// sliceContains is a helper function that returns true if a string is included in a string slice -func sliceContains(sslice []string, str string) bool { - for _, v := range sslice { - if v == str { - return true - } - } - return false -} From 09a272c213739cc7da7d3fcf4879429a5a4c651c Mon Sep 17 00:00:00 2001 From: Joona Hoikkala <5235109+joohoi@users.noreply.github.com> Date: Fri, 15 Sep 2023 18:21:10 +0300 Subject: [PATCH 7/7] Make linter happy --- pkg/ffuf/job.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/ffuf/job.go b/pkg/ffuf/job.go index 7fb387b7..e80bddf8 100644 --- a/pkg/ffuf/job.go +++ b/pkg/ffuf/job.go @@ -39,9 +39,7 @@ type Job struct { skipQueue bool currentDepth int calibMutex sync.Mutex - blacklistMutex sync.Mutex pauseWg sync.WaitGroup - blacklistChecked bool } type QueueJob struct {