From bc5e61ecc3af04271dcd9993deae5fd40784045d Mon Sep 17 00:00:00 2001 From: Bp0lr Date: Mon, 23 Nov 2020 18:39:06 +0000 Subject: [PATCH] Empty filter (#330) * add support to filter empty result files. * update readme * add contributors. * add changelog * Update ffufrc.example --- CHANGELOG.md | 1 + CONTRIBUTORS.md | 1 + README.md | 1 + ffufrc.example | 1 + help.go | 2 +- main.go | 1 + pkg/ffuf/config.go | 1 + pkg/ffuf/optionsparser.go | 3 +++ pkg/output/file_csv.go | 5 +++++ pkg/output/file_html.go | 4 ++++ pkg/output/file_json.go | 5 +++++ pkg/output/file_md.go | 4 ++++ pkg/output/stdout.go | 4 ++++ 13 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5b5907..3424505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Ffuf now reads a default configuration file `$HOME/.ffufrc` upon startup. Options set in this file are overwritten by the ones provided on CLI. - Change banner logging to stderr instead of stdout. + - New CLI flag `-or` to avoid creating result files if we didn't get any. - Changed - Pre-flight errors are now displayed also after the usage text to prevent the need to scroll through backlog. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1f1e535..1c739b6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,5 +1,6 @@ # Contributors * [AverageSecurityGuy](https://github.com/averagesecurityguy) +* [bp0](https://github.com/bp0lr) * [bjhulst](https://github.com/bjhulst) * [bsysop](https://twitter.com/bsysop) * [ccsplit](https://github.com/ccsplit) diff --git a/README.md b/README.md index 93e5458..c44de54 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,7 @@ OUTPUT OPTIONS: -o Write output to file -od Directory path to store matched results to. -of Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json) + -or Don't create the output file if we don't have results EXAMPLE USAGE: Fuzz file paths from wordlist.txt, match all responses but filter out those with content-size 42. diff --git a/ffufrc.example b/ffufrc.example index fd9d442..eb6912a 100644 --- a/ffufrc.example +++ b/ffufrc.example @@ -60,6 +60,7 @@ outputdirectory = "/tmp/rawoutputdir" outputfile = "output.json" outputformat = "json" + outputcreateemptyfile = false [filter] lines = "" diff --git a/help.go b/help.go index 3f1c6d0..3bffdf9 100644 --- a/help.go +++ b/help.go @@ -96,7 +96,7 @@ func Usage() { Description: "Options for output. Output file formats, file names and debug file locations.", Flags: make([]UsageFlag, 0), Hidden: false, - ExpectedFlags: []string{"debug-log", "o", "of", "od"}, + ExpectedFlags: []string{"debug-log", "o", "of", "od", "or"}, } sections := []UsageSection{u_http, u_general, u_compat, u_matcher, u_filter, u_input, u_output} diff --git a/main.go b/main.go index 6d76e7f..a026914 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions { flag.BoolVar(&ignored, "compressed", true, "Dummy flag for copy as curl functionality (ignored)") flag.BoolVar(&ignored, "i", true, "Dummy flag for copy as curl functionality (ignored)") flag.BoolVar(&ignored, "k", false, "Dummy flag for backwards compatibility") + flag.BoolVar(&opts.Output.OutputCreateEmptyFile, "or", opts.Output.OutputCreateEmptyFile, "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.Colors, "c", opts.General.Colors, "Colorize output.") flag.BoolVar(&opts.General.Quiet, "s", opts.General.Quiet, "Do not print additional information (silent mode)") diff --git a/pkg/ffuf/config.go b/pkg/ffuf/config.go index 0268738..78ab960 100644 --- a/pkg/ffuf/config.go +++ b/pkg/ffuf/config.go @@ -32,6 +32,7 @@ type Config struct { OutputDirectory string `json:"outputdirectory"` OutputFile string `json:"outputfile"` OutputFormat string `json:"outputformat"` + OutputCreateEmptyFile bool `json:"OutputCreateEmptyFile"` ProgressFrequency int `json:"-"` ProxyURL string `json:"proxyurl"` Quiet bool `json:"quiet"` diff --git a/pkg/ffuf/optionsparser.go b/pkg/ffuf/optionsparser.go index 1a12b80..61009dc 100644 --- a/pkg/ffuf/optionsparser.go +++ b/pkg/ffuf/optionsparser.go @@ -75,6 +75,7 @@ type OutputOptions struct { OutputDirectory string OutputFile string OutputFormat string + OutputCreateEmptyFile bool } type FilterOptions struct { @@ -140,6 +141,7 @@ func NewConfigOptions() *ConfigOptions { c.Output.OutputDirectory = "" c.Output.OutputFile = "" c.Output.OutputFormat = "json" + c.Output.OutputCreateEmptyFile = false return c } @@ -374,6 +376,7 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con conf.InputMode = parseOpts.Input.InputMode conf.OutputFile = parseOpts.Output.OutputFile conf.OutputDirectory = parseOpts.Output.OutputDirectory + conf.OutputCreateEmptyFile = parseOpts.Output.OutputCreateEmptyFile conf.IgnoreBody = parseOpts.HTTP.IgnoreBody conf.Quiet = parseOpts.General.Quiet conf.StopOn403 = parseOpts.General.StopOn403 diff --git a/pkg/output/file_csv.go b/pkg/output/file_csv.go index 3394f8a..3024aab 100644 --- a/pkg/output/file_csv.go +++ b/pkg/output/file_csv.go @@ -12,6 +12,11 @@ import ( var staticheaders = []string{"url", "redirectlocation", "position", "status_code", "content_length", "content_words", "content_lines", "resultfile"} func writeCSV(config *ffuf.Config, res []Result, encode bool) error { + + if(config.OutputCreateEmptyFile && (len(res) == 0)){ + return nil + } + header := make([]string, 0) f, err := os.Create(config.OutputFile) if err != nil { diff --git a/pkg/output/file_html.go b/pkg/output/file_html.go index 7059277..ee5aa4d 100644 --- a/pkg/output/file_html.go +++ b/pkg/output/file_html.go @@ -176,6 +176,10 @@ func colorizeResults(results []Result) []Result { func writeHTML(config *ffuf.Config, results []Result) error { + if(config.OutputCreateEmptyFile && (len(results) == 0)){ + return nil + } + results = colorizeResults(results) ti := time.Now() diff --git a/pkg/output/file_json.go b/pkg/output/file_json.go index 43c7d44..604b4fc 100644 --- a/pkg/output/file_json.go +++ b/pkg/output/file_json.go @@ -36,6 +36,11 @@ type jsonFileOutput struct { } func writeEJSON(config *ffuf.Config, res []Result) error { + + if(config.OutputCreateEmptyFile && (len(res) == 0)){ + return nil + } + t := time.Now() outJSON := ejsonFileOutput{ CommandLine: config.CommandLine, diff --git a/pkg/output/file_md.go b/pkg/output/file_md.go index 3e86c23..6e25b4a 100644 --- a/pkg/output/file_md.go +++ b/pkg/output/file_md.go @@ -22,6 +22,10 @@ const ( func writeMarkdown(config *ffuf.Config, res []Result) error { + if(config.OutputCreateEmptyFile && (len(res) == 0)){ + return nil + } + ti := time.Now() keywords := make([]string, 0) diff --git a/pkg/output/stdout.go b/pkg/output/stdout.go index e888cfe..8de94a1 100644 --- a/pkg/output/stdout.go +++ b/pkg/output/stdout.go @@ -210,6 +210,10 @@ func (s *Stdoutput) writeToAll(config *ffuf.Config, res []Result) error { // Go through each type of write, adding // the suffix to each output file. + if(config.OutputCreateEmptyFile && (len(res) == 0)){ + return nil + } + s.config.OutputFile = BaseFilename + ".json" err = writeJSON(s.config, s.Results) if err != nil {