commit
19d3630b43
22
main.go
22
main.go
@ -28,6 +28,7 @@ type cliOptions struct {
|
|||||||
matcherRegexp string
|
matcherRegexp string
|
||||||
matcherWords string
|
matcherWords string
|
||||||
proxyURL string
|
proxyURL string
|
||||||
|
outputFormat string
|
||||||
headers multiStringFlag
|
headers multiStringFlag
|
||||||
showVersion bool
|
showVersion bool
|
||||||
}
|
}
|
||||||
@ -65,6 +66,8 @@ func main() {
|
|||||||
flag.StringVar(&opts.matcherWords, "mw", "", "Match amount of words in response")
|
flag.StringVar(&opts.matcherWords, "mw", "", "Match amount of words in response")
|
||||||
flag.StringVar(&opts.proxyURL, "x", "", "HTTP Proxy URL")
|
flag.StringVar(&opts.proxyURL, "x", "", "HTTP Proxy URL")
|
||||||
flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use")
|
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")
|
||||||
flag.BoolVar(&conf.Quiet, "s", false, "Do not print additional information (silent mode)")
|
flag.BoolVar(&conf.Quiet, "s", false, "Do not print additional information (silent mode)")
|
||||||
flag.BoolVar(&conf.StopOn403, "sf", false, "Stop when > 90% of responses return 403 Forbidden")
|
flag.BoolVar(&conf.StopOn403, "sf", false, "Stop when > 90% of responses return 403 Forbidden")
|
||||||
flag.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.")
|
flag.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.")
|
||||||
@ -180,6 +183,24 @@ 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"}
|
||||||
|
found := false
|
||||||
|
for _, f := range outputFormats {
|
||||||
|
if f == parseOpts.outputFormat {
|
||||||
|
conf.OutputFormat = f
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
errs.Add(fmt.Errorf("Unknown output file format (-of): %s", parseOpts.outputFormat))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.CommandLine = strings.Join(os.Args, " ")
|
||||||
|
|
||||||
//Search for keyword from URL and POST data too
|
//Search for keyword from URL and POST data too
|
||||||
if strings.Index(conf.Url, "FUZZ") != -1 {
|
if strings.Index(conf.Url, "FUZZ") != -1 {
|
||||||
foundkeyword = true
|
foundkeyword = true
|
||||||
@ -191,6 +212,7 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||||||
if !foundkeyword {
|
if !foundkeyword {
|
||||||
errs.Add(fmt.Errorf("No FUZZ keyword(s) found in headers, URL or POST data, nothing to do"))
|
errs.Add(fmt.Errorf("No FUZZ keyword(s) found in headers, URL or POST data, nothing to do"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return errs.ErrorOrNil()
|
return errs.ErrorOrNil()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,8 @@ type Config struct {
|
|||||||
Quiet bool
|
Quiet bool
|
||||||
Colors bool
|
Colors bool
|
||||||
Wordlist string
|
Wordlist string
|
||||||
|
OutputFile string
|
||||||
|
OutputFormat string
|
||||||
StopOn403 bool
|
StopOn403 bool
|
||||||
Delay optRange
|
Delay optRange
|
||||||
Filters []FilterProvider
|
Filters []FilterProvider
|
||||||
@ -32,6 +34,7 @@ type Config struct {
|
|||||||
Threads int
|
Threads int
|
||||||
Context context.Context
|
Context context.Context
|
||||||
ProxyURL func(*http.Request) (*url.URL, error)
|
ProxyURL func(*http.Request) (*url.URL, error)
|
||||||
|
CommandLine string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(ctx context.Context) Config {
|
func NewConfig(ctx context.Context) Config {
|
||||||
|
|||||||
33
pkg/output/file_json.go
Normal file
33
pkg/output/file_json.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ffuf/ffuf/pkg/ffuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jsonFileOutput struct {
|
||||||
|
CommandLine string `json:"commandline"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
Results []Result `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJSON(config *ffuf.Config, res []Result) error {
|
||||||
|
t := time.Now()
|
||||||
|
outJSON := jsonFileOutput{
|
||||||
|
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
|
||||||
|
}
|
||||||
@ -20,12 +20,21 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Stdoutput struct {
|
type Stdoutput struct {
|
||||||
config *ffuf.Config
|
config *ffuf.Config
|
||||||
|
Results []Result
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
Input string `json:"input"`
|
||||||
|
StatusCode int64 `json:"status"`
|
||||||
|
ContentLength int64 `json:"length"`
|
||||||
|
ContentWords int64 `json:"words"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStdoutput(conf *ffuf.Config) *Stdoutput {
|
func NewStdoutput(conf *ffuf.Config) *Stdoutput {
|
||||||
var outp Stdoutput
|
var outp Stdoutput
|
||||||
outp.config = conf
|
outp.config = conf
|
||||||
|
outp.Results = []Result{}
|
||||||
return &outp
|
return &outp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +86,15 @@ func (s *Stdoutput) Warning(warnstring string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stdoutput) Finalize() error {
|
func (s *Stdoutput) Finalize() error {
|
||||||
|
var err error
|
||||||
|
if s.config.OutputFile != "" {
|
||||||
|
if s.config.OutputFormat == "json" {
|
||||||
|
err = writeJSON(s.config, s.Results)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.Error(fmt.Sprintf("%s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -107,6 +125,16 @@ func (s *Stdoutput) Result(resp ffuf.Response) bool {
|
|||||||
}
|
}
|
||||||
// Response survived the filtering, output the result
|
// Response survived the filtering, output the result
|
||||||
s.printResult(resp)
|
s.printResult(resp)
|
||||||
|
if s.config.OutputFile != "" {
|
||||||
|
// No need to store results if we're not going to use them later
|
||||||
|
sResult := Result{
|
||||||
|
Input: string(resp.Request.Input),
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
ContentLength: resp.ContentLength,
|
||||||
|
ContentWords: resp.ContentWords,
|
||||||
|
}
|
||||||
|
s.Results = append(s.Results, sResult)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user