Connection error handling, and options to stop execution (#15)

This commit is contained in:
Joona Hoikkala 2019-04-03 23:11:49 +03:00 committed by GitHub
parent d5fe00e330
commit b9c9c92418
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 22 deletions

View file

@ -69,7 +69,6 @@ ffuf -w /path/to/postdata.txt -X POST -d "username=admin\&password=FUZZ" https:/
## Usage
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
```
-H "Name: Value"
Header "Name: Value", separated by colon. Multiple -H flags are accepted.
@ -104,6 +103,10 @@ To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-
Seconds of delay between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
-r Follow redirects
-s Do not print additional information (silent mode)
-sa
Stop on all error cases. Implies -sf and -se
-se
Stop on spurious errors
-sf
Stop when > 90% of responses return 403 Forbidden
-t int
@ -132,6 +135,9 @@ The only dependency of ffuf is Go 1.11. No dependencies outside of Go standard l
- New
- New output file formats: CSV and eCSV (CSV with base64 encoded input field to avoid CSV breakage with payloads containing a comma)
- New CLI flag to follow redirects
- Erroring connections will be retried once
- Error counter in status bar
- New CLI flags: -se (stop on spurious errors) and -sa (stop on all errors, implies -se and -sf)
- v0.8
- New
- New CLI flag to write output to a file in JSON format

View file

@ -70,6 +70,8 @@ func main() {
flag.StringVar(&opts.outputFormat, "of", "json", "Output file format. Available formats: json, csv, ecsv")
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.StopOnErrors, "se", false, "Stop on spurious errors")
flag.BoolVar(&conf.StopOnAll, "sa", false, "Stop on all error cases. Implies -sf and -se")
flag.BoolVar(&conf.FollowRedirects, "r", false, "Follow redirects")
flag.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.")
flag.BoolVar(&opts.showVersion, "V", false, "Show version information.")

View file

@ -28,6 +28,8 @@ type Config struct {
OutputFile string
OutputFormat string
StopOn403 bool
StopOnErrors bool
StopOnAll bool
FollowRedirects bool
Delay optRange
Filters []FilterProvider
@ -49,6 +51,8 @@ func NewConfig(ctx context.Context) Config {
conf.Data = ""
conf.Quiet = false
conf.StopOn403 = false
conf.StopOnErrors = false
conf.StopOnAll = false
conf.FollowRedirects = false
conf.ProxyURL = http.ProxyFromEnvironment
conf.Filters = make([]FilterProvider, 0)

View file

@ -9,25 +9,52 @@ import (
//Job ties together Config, Runner, Input and Output
type Job struct {
Config *Config
Input InputProvider
Runner RunnerProvider
Output OutputProvider
Counter int
Total int
Running bool
Count403 int
Error string
startTime time.Time
Config *Config
ErrorMutex sync.Mutex
Input InputProvider
Runner RunnerProvider
Output OutputProvider
Counter int
ErrorCounter int
SpuriousErrorCounter int
Total int
Running bool
Count403 int
Error string
startTime time.Time
}
func NewJob(conf *Config) Job {
var j Job
j.Counter = 0
j.ErrorCounter = 0
j.SpuriousErrorCounter = 0
j.Running = false
return j
}
//incError increments the error counter
func (j *Job) incError() {
j.ErrorMutex.Lock()
defer j.ErrorMutex.Unlock()
j.ErrorCounter++
j.SpuriousErrorCounter++
}
//inc403 increments the 403 response counter
func (j *Job) inc403() {
j.ErrorMutex.Lock()
defer j.ErrorMutex.Unlock()
j.Count403++
}
//resetSpuriousErrors resets the spurious error counter
func (j *Job) resetSpuriousErrors() {
j.ErrorMutex.Lock()
defer j.ErrorMutex.Unlock()
j.SpuriousErrorCounter = 0
}
//Start the execution of the Job
func (j *Job) Start() {
rand.Seed(time.Now().UnixNano())
@ -57,7 +84,7 @@ func (j *Job) Start() {
go func() {
defer func() { <-limiter }()
defer wg.Done()
j.runTask([]byte(nextInput))
j.runTask([]byte(nextInput), false)
if j.Config.Delay.HasDelay {
var sleepDurationMS time.Duration
if j.Config.Delay.IsRange {
@ -106,25 +133,33 @@ func (j *Job) updateProgress() {
mins := dur / time.Minute
dur -= mins * time.Minute
secs := dur / time.Second
progString := fmt.Sprintf(":: Progress: [%d/%d] :: %d req/sec :: Duration: [%d:%02d:%02d] ::", j.Counter, j.Total, int(reqRate), hours, mins, secs)
progString := fmt.Sprintf(":: Progress: [%d/%d] :: %d req/sec :: Duration: [%d:%02d:%02d] :: Errors: %d ::", j.Counter, j.Total, int(reqRate), hours, mins, secs, j.ErrorCounter)
j.Output.Progress(progString)
}
func (j *Job) runTask(input []byte) {
func (j *Job) runTask(input []byte, retried bool) {
req, err := j.Runner.Prepare(input)
if err != nil {
j.Output.Error(fmt.Sprintf("Encountered error while preparing request: %s\n", err))
j.Output.Error(fmt.Sprintf("Encountered an error while preparing request: %s\n", err))
j.incError()
return
}
resp, err := j.Runner.Execute(&req)
if err != nil {
j.Output.Error(fmt.Sprintf("Error in runner: %s\n", err))
if retried {
j.incError()
} else {
j.runTask(input, true)
}
return
}
if j.Config.StopOn403 {
if j.SpuriousErrorCounter > 0 {
j.resetSpuriousErrors()
}
if j.Config.StopOn403 || j.Config.StopOnAll {
// Incremnt Forbidden counter if we encountered one
if resp.StatusCode == 403 {
j.Count403 += 1
j.inc403()
}
}
if j.Output.Result(resp) {
@ -137,10 +172,20 @@ func (j *Job) runTask(input []byte) {
func (j *Job) CheckStop() {
if j.Counter > 50 {
// We have enough samples
if float64(j.Count403)/float64(j.Counter) > 0.95 {
// Over 95% of requests are 403
j.Error = "Getting unusual amount of 403 responses, exiting."
j.Stop()
if j.Config.StopOn403 || j.Config.StopOnAll {
if float64(j.Count403)/float64(j.Counter) > 0.95 {
// Over 95% of requests are 403
j.Error = "Getting an unusual amount of 403 responses, exiting."
j.Stop()
}
}
if j.Config.StopOnErrors || j.Config.StopOnAll {
if j.SpuriousErrorCounter > j.Config.Threads*2 {
// Most of the requests are erroring
j.Error = "Recieving spurious errors, exiting."
j.Stop()
}
}
}
}