Fix JSON output regression and Stdout race condition issues (#94)

* Fix json output regression and improve stdout printing

* Add changelog entry
This commit is contained in:
Joona Hoikkala 2019-11-16 01:48:00 +02:00 committed by GitHub
parent ac141e5e34
commit 7aad9c6051
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 11 deletions

View file

@ -155,7 +155,7 @@ Usage of ./ffuf:
-o string
Write output to file
-of string
Output file format. Available formats: json, html, md, csv, ecsv (default "json")
Output file format. Available formats: json, ejson, html, md, csv, ecsv (default "json")
-p delay
Seconds of delay between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
-r Follow redirects
@ -193,8 +193,10 @@ The only dependency of ffuf is Go 1.11. No dependencies outside of Go standard l
- master
- New
- Added a new flag to select a multi wordlist operation mode: `--mode`, possible values: `clusterbomb` and `pitchfork`.
- Added a new output file format eJSON, for always base64 encoding the input data.
- Changed
- Fixed a bug in the default multi wordlist mode
- Fixed JSON output regression, where all the input data was always encoded in base64
- v0.11

View file

@ -92,7 +92,7 @@ func main() {
flag.StringVar(&opts.proxyURL, "x", "", "HTTP Proxy URL")
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, html, md, csv, ecsv")
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")
@ -333,7 +333,7 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
//Check the output file format option
if conf.OutputFile != "" {
//No need to check / error out if output file isn't defined
outputFormats := []string{"json", "html", "md", "csv", "ecsv"}
outputFormats := []string{"json", "ejson", "html", "md", "csv", "ecsv"}
found := false
for _, f := range outputFormats {
if f == parseOpts.outputFormat {

View file

@ -8,19 +8,68 @@ import (
"github.com/ffuf/ffuf/pkg/ffuf"
)
type jsonFileOutput struct {
type ejsonFileOutput struct {
CommandLine string `json:"commandline"`
Time string `json:"time"`
Results []Result `json:"results"`
}
func writeJSON(config *ffuf.Config, res []Result) error {
type JsonResult struct {
Input map[string]string `json:"input"`
Position int `json:"position"`
StatusCode int64 `json:"status"`
ContentLength int64 `json:"length"`
ContentWords int64 `json:"words"`
ContentLines int64 `json:"lines"`
}
type jsonFileOutput struct {
CommandLine string `json:"commandline"`
Time string `json:"time"`
Results []JsonResult `json:"results"`
}
func writeEJSON(config *ffuf.Config, res []Result) error {
t := time.Now()
outJSON := jsonFileOutput{
outJSON := ejsonFileOutput{
CommandLine: config.CommandLine,
Time: t.Format(time.RFC3339),
Results: res,
}
outBytes, err := json.Marshal(outJSON)
if err != nil {
return err
}
err = ioutil.WriteFile(config.OutputFile, outBytes, 0644)
if err != nil {
return err
}
return nil
}
func writeJSON(config *ffuf.Config, res []Result) error {
t := time.Now()
jsonRes := make([]JsonResult, 0)
for _, r := range res {
strinput := make(map[string]string)
for k, v := range r.Input {
strinput[k] = string(v)
}
jsonRes = append(jsonRes, JsonResult{
Input: strinput,
Position: r.Position,
StatusCode: r.StatusCode,
ContentLength: r.ContentLength,
ContentWords: r.ContentWords,
ContentLines: r.ContentLines,
})
}
outJSON := jsonFileOutput{
CommandLine: config.CommandLine,
Time: t.Format(time.RFC3339),
Results: jsonRes,
}
outBytes, err := json.Marshal(outJSON)
if err != nil {
return err

View file

@ -110,6 +110,8 @@ func (s *Stdoutput) Finalize() error {
if s.config.OutputFile != "" {
if s.config.OutputFormat == "json" {
err = writeJSON(s.config, s.Results)
} else if s.config.OutputFormat == "ejson" {
err = writeEJSON(s.config, s.Results)
} else if s.config.OutputFormat == "html" {
err = writeHTML(s.config, s.Results)
} else if s.config.OutputFormat == "md" {
@ -192,19 +194,20 @@ 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\n"
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))
fmt.Println(s.colorize(res_hdr, resp.StatusCode))
res_hdr = s.colorize(res_hdr, resp.StatusCode)
reslines := ""
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
fmt.Printf(res_str, TERMINAL_CLEAR_LINE, k, strconv.Itoa(resp.Request.Position))
reslines = fmt.Sprintf(res_str, reslines, TERMINAL_CLEAR_LINE, k, strconv.Itoa(resp.Request.Position))
} else {
// Wordlist input
fmt.Printf(res_str, TERMINAL_CLEAR_LINE, k, v)
reslines = fmt.Sprintf(res_str, reslines, TERMINAL_CLEAR_LINE, k, v)
}
}
fmt.Printf("%s\n%s", res_hdr, reslines)
}
func (s *Stdoutput) resultNormal(resp ffuf.Response) {