From 3949e49b3b2fd0688f4bd6cbd1b40197d2047ff0 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sat, 16 Nov 2019 16:32:11 +0200 Subject: [PATCH] Implement -v (verbose) flag (#100) --- README.md | 4 +++- main.go | 2 +- pkg/ffuf/config.go | 4 ++-- pkg/output/stdout.go | 26 +++++++++++--------------- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1902fab..034285b 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,6 @@ Usage of ./ffuf: -input-num int Number of inputs to test. Used in conjunction with --input-cmd. (default 100) -k TLS identity verification - -l Show target location of redirect responses -mc string Match HTTP status codes from respose, use "all" to match every response code. (default "200,204,301,302,307,401,403") -ml string @@ -172,6 +171,7 @@ Usage of ./ffuf: HTTP request timeout in seconds. (default 10) -u string Target URL + -v Verbose output, printing full URL and redirect location (if any) with the results. -w value Wordlist file path and (optional) custom fuzz keyword, using colon as delimiter. Use file path '-' to read from standard input. Can be supplied multiple times. Format: '/path/to/wordlist:KEYWORD' -x string @@ -197,10 +197,12 @@ The only dependency of ffuf is Go 1.11. No dependencies outside of Go standard l - Redirect location is always shown in the output files (when using `-o`) - Full URL is always shown in the output files (when using `-o`) - HTML output format got [DataTables](https://datatables.net/) support allowing realtime searches, sorting by column etc. + - New CLI flag `-v` for verbose output. Including full URL, and redirect location. - Changed - Fixed a bug in the default multi wordlist mode - Fixed JSON output regression, where all the input data was always encoded in base64 - `--debug-log` no correctly logs connection errors + - Removed `-l` flag in favor of `-v` - v0.11 diff --git a/main.go b/main.go index cf87bbf..d7a3fe9 100644 --- a/main.go +++ b/main.go @@ -93,7 +93,6 @@ func main() { flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use") flag.StringVar(&conf.OutputFile, "o", "", "Write output to file") flag.StringVar(&opts.outputFormat, "of", "json", "Output file format. Available formats: json, ejson, html, md, csv, ecsv") - flag.BoolVar(&conf.ShowRedirectLocation, "l", false, "Show target location of redirect responses") flag.BoolVar(&conf.Quiet, "s", false, "Do not print additional information (silent mode)") flag.BoolVar(&conf.StopOn403, "sf", false, "Stop when > 95% of responses return 403 Forbidden") flag.BoolVar(&conf.StopOnErrors, "se", false, "Stop on spurious errors") @@ -103,6 +102,7 @@ func main() { flag.Var(&opts.AutoCalibrationStrings, "acc", "Custom auto-calibration string. Can be used multiple times. Implies -ac") flag.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.") flag.IntVar(&conf.Timeout, "timeout", 10, "HTTP request timeout in seconds.") + flag.BoolVar(&conf.Verbose, "v", false, "Verbose output, printing full URL and redirect location (if any) with the results.") flag.BoolVar(&opts.showVersion, "V", false, "Show version information.") flag.StringVar(&opts.debugLog, "debug-log", "", "Write all of the internal logging to the specified file.") flag.Parse() diff --git a/pkg/ffuf/config.go b/pkg/ffuf/config.go index a49a92c..5b8233c 100644 --- a/pkg/ffuf/config.go +++ b/pkg/ffuf/config.go @@ -37,7 +37,6 @@ type Config struct { FollowRedirects bool AutoCalibration bool AutoCalibrationStrings []string - ShowRedirectLocation bool Timeout int ProgressFrequency int Delay optRange @@ -47,6 +46,7 @@ type Config struct { Context context.Context ProxyURL func(*http.Request) (*url.URL, error) CommandLine string + Verbose bool } type InputProviderConfig struct { @@ -67,7 +67,6 @@ func NewConfig(ctx context.Context) Config { conf.StopOn403 = false conf.StopOnErrors = false conf.StopOnAll = false - conf.ShowRedirectLocation = false conf.FollowRedirects = false conf.InputProviders = make([]InputProviderConfig, 0) conf.CommandKeywords = make([]string, 0) @@ -81,5 +80,6 @@ func NewConfig(ctx context.Context) Config { // Progress update frequency, in milliseconds conf.ProgressFrequency = 100 conf.DirSearchCompat = false + conf.Verbose = false return conf } diff --git a/pkg/output/stdout.go b/pkg/output/stdout.go index ea3092a..8baf3f7 100644 --- a/pkg/output/stdout.go +++ b/pkg/output/stdout.go @@ -159,7 +159,7 @@ func (s *Stdoutput) printResult(resp ffuf.Response) { if s.config.Quiet { s.resultQuiet(resp) } else { - if len(resp.Request.Input) > 1 { + if len(resp.Request.Input) > 1 || s.config.Verbose { // Print a multi-line result (when using multiple input keywords and wordlists) s.resultMultiline(resp) } else { @@ -199,9 +199,16 @@ func (s *Stdoutput) resultQuiet(resp ffuf.Response) { func (s *Stdoutput) resultMultiline(resp ffuf.Response) { var res_hdr, res_str string res_str = "%s%s * %s: %s\n" - res_hdr = fmt.Sprintf("%s[Status: %d, Size: %d, Words: %d, Lines: %d%s]", TERMINAL_CLEAR_LINE, resp.StatusCode, resp.ContentLength, resp.ContentWords, resp.ContentLines, s.addRedirectLocation(resp)) + res_hdr = fmt.Sprintf("%s[Status: %d, Size: %d, Words: %d, Lines: %d]", TERMINAL_CLEAR_LINE, resp.StatusCode, resp.ContentLength, resp.ContentWords, resp.ContentLines) res_hdr = s.colorize(res_hdr, resp.StatusCode) reslines := "" + if s.config.Verbose { + reslines = fmt.Sprintf("%s%s| URL | %s\n", reslines, TERMINAL_CLEAR_LINE, resp.Request.Url) + redirectLocation := resp.GetRedirectLocation() + if redirectLocation != "" { + reslines = fmt.Sprintf("%s%s| --> | %s\n", reslines, TERMINAL_CLEAR_LINE, redirectLocation) + } + } for k, v := range resp.Request.Input { if inSlice(k, s.config.CommandKeywords) { // If we're using external command for input, display the position instead of input @@ -211,26 +218,15 @@ func (s *Stdoutput) resultMultiline(resp ffuf.Response) { reslines = fmt.Sprintf(res_str, reslines, TERMINAL_CLEAR_LINE, k, v) } } - fmt.Printf("%s\n%s", res_hdr, reslines) + fmt.Printf("%s\n%s\n", res_hdr, reslines) } func (s *Stdoutput) resultNormal(resp ffuf.Response) { var res_str string - res_str = fmt.Sprintf("%s%-23s [Status: %s, Size: %d, Words: %d, Lines: %d%s]", TERMINAL_CLEAR_LINE, s.prepareInputsOneLine(resp), s.colorize(fmt.Sprintf("%d", resp.StatusCode), resp.StatusCode), resp.ContentLength, resp.ContentWords, resp.ContentLines, s.addRedirectLocation(resp)) + res_str = fmt.Sprintf("%s%-23s [Status: %s, Size: %d, Words: %d, Lines: %d]", TERMINAL_CLEAR_LINE, s.prepareInputsOneLine(resp), s.colorize(fmt.Sprintf("%d", resp.StatusCode), resp.StatusCode), resp.ContentLength, resp.ContentWords, resp.ContentLines) fmt.Println(res_str) } -// addRedirectLocation returns a formatted string containing the Redirect location or returns an empty string -func (s *Stdoutput) addRedirectLocation(resp ffuf.Response) string { - if s.config.ShowRedirectLocation == true { - redirectLocation := resp.GetRedirectLocation() - if redirectLocation != "" { - return fmt.Sprintf(", ->: %s", redirectLocation) - } - } - return "" -} - func (s *Stdoutput) colorize(input string, status int64) string { if !s.config.Colors { return fmt.Sprintf("%s", input)