Merge pull request #7 from ffuf/stop403

New flag to stop on spurious 403 responses
This commit is contained in:
Joona Hoikkala 2019-03-30 01:08:01 +02:00 committed by GitHub
commit 14ce9943a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 7 deletions

View File

@ -66,6 +66,7 @@ func main() {
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.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.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.") flag.IntVar(&conf.Threads, "t", 40, "Number of concurrent threads.")
flag.BoolVar(&opts.showVersion, "V", false, "Show version information.") flag.BoolVar(&opts.showVersion, "V", false, "Show version information.")
flag.Parse() flag.Parse()

View File

@ -25,6 +25,7 @@ type Config struct {
Quiet bool Quiet bool
Colors bool Colors bool
Wordlist string Wordlist string
StopOn403 bool
Delay optRange Delay optRange
Filters []FilterProvider Filters []FilterProvider
Matchers []FilterProvider Matchers []FilterProvider
@ -43,6 +44,7 @@ func NewConfig(ctx context.Context) Config {
conf.TLSSkipVerify = false conf.TLSSkipVerify = false
conf.Data = "" conf.Data = ""
conf.Quiet = false conf.Quiet = false
conf.StopOn403 = false
conf.ProxyURL = http.ProxyFromEnvironment conf.ProxyURL = http.ProxyFromEnvironment
conf.Filters = make([]FilterProvider, 0) conf.Filters = make([]FilterProvider, 0)
conf.Delay = optRange{0, 0, false, false} conf.Delay = optRange{0, 0, false, false}

View File

@ -23,6 +23,8 @@ type InputProvider interface {
type OutputProvider interface { type OutputProvider interface {
Banner() error Banner() error
Finalize() error Finalize() error
Progress(status string)
Error(errstring string) Error(errstring string)
Warning(warnstring string)
Result(resp Response) bool Result(resp Response) bool
} }

View File

@ -16,6 +16,8 @@ type Job struct {
Counter int Counter int
Total int Total int
Running bool Running bool
Count403 int
Error string
startTime time.Time startTime time.Time
} }
@ -42,6 +44,12 @@ func (j *Job) Start() {
//Limiter blocks after reaching the buffer, ensuring limited concurrency //Limiter blocks after reaching the buffer, ensuring limited concurrency
limiter := make(chan bool, j.Config.Threads) limiter := make(chan bool, j.Config.Threads)
for j.Input.Next() { for j.Input.Next() {
// Check if we should stop the process
j.CheckStop()
if !j.Running {
defer j.Output.Warning(j.Error)
break
}
limiter <- true limiter <- true
nextInput := j.Input.Value() nextInput := j.Input.Value()
wg.Add(1) wg.Add(1)
@ -73,6 +81,9 @@ func (j *Job) runProgress(wg *sync.WaitGroup) {
j.startTime = time.Now() j.startTime = time.Now()
totalProgress := j.Input.Total() totalProgress := j.Input.Total()
for j.Counter <= totalProgress { for j.Counter <= totalProgress {
if !j.Running {
break
}
j.updateProgress() j.updateProgress()
if j.Counter == totalProgress { if j.Counter == totalProgress {
return return
@ -82,11 +93,6 @@ func (j *Job) runProgress(wg *sync.WaitGroup) {
} }
func (j *Job) updateProgress() { func (j *Job) updateProgress() {
//TODO: refactor to use a defined progress struct for future output modules
if j.Config.Quiet {
// Do not print progress status in silent mode
return
}
runningSecs := int((time.Now().Sub(j.startTime)) / time.Second) runningSecs := int((time.Now().Sub(j.startTime)) / time.Second)
var reqRate int var reqRate int
if runningSecs > 0 { if runningSecs > 0 {
@ -101,7 +107,7 @@ func (j *Job) updateProgress() {
dur -= mins * time.Minute dur -= mins * time.Minute
secs := dur / time.Second 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] ::", j.Counter, j.Total, int(reqRate), hours, mins, secs)
j.Output.Error(progString) j.Output.Progress(progString)
} }
func (j *Job) runTask(input []byte) { func (j *Job) runTask(input []byte) {
@ -115,6 +121,12 @@ func (j *Job) runTask(input []byte) {
j.Output.Error(fmt.Sprintf("Error in runner: %s\n", err)) j.Output.Error(fmt.Sprintf("Error in runner: %s\n", err))
return return
} }
if j.Config.StopOn403 {
// Incremnt Forbidden counter if we encountered one
if resp.StatusCode == 403 {
j.Count403 += 1
}
}
if j.Output.Result(resp) { if j.Output.Result(resp) {
// Refresh the progress indicator as we printed something out // Refresh the progress indicator as we printed something out
j.updateProgress() j.updateProgress()
@ -122,6 +134,17 @@ func (j *Job) runTask(input []byte) {
return return
} }
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()
}
}
}
//Stop the execution of the Job //Stop the execution of the Job
func (j *Job) Stop() { func (j *Job) Stop() {
j.Running = false j.Running = false

View File

@ -43,11 +43,36 @@ func (s *Stdoutput) Banner() error {
return nil return nil
} }
func (s *Stdoutput) Progress(status string) {
if s.config.Quiet {
// No progress for quiet mode
return
} else {
fmt.Fprintf(os.Stderr, "%s%s", TERMINAL_CLEAR_LINE, status)
}
}
func (s *Stdoutput) Error(errstring string) { func (s *Stdoutput) Error(errstring string) {
if s.config.Quiet { if s.config.Quiet {
fmt.Fprintf(os.Stderr, "%s", errstring) fmt.Fprintf(os.Stderr, "%s", errstring)
} else { } else {
fmt.Fprintf(os.Stderr, "%s%s", TERMINAL_CLEAR_LINE, errstring) if !s.config.Colors {
fmt.Fprintf(os.Stderr, "%s[ERR] %s\n", TERMINAL_CLEAR_LINE, errstring)
} else {
fmt.Fprintf(os.Stderr, "%s[%sERR%s] %s\n", TERMINAL_CLEAR_LINE, ANSI_RED, ANSI_CLEAR, errstring)
}
}
}
func (s *Stdoutput) Warning(warnstring string) {
if s.config.Quiet {
fmt.Fprintf(os.Stderr, "%s", warnstring)
} else {
if !s.config.Colors {
fmt.Fprintf(os.Stderr, "%s[WARN] %s", TERMINAL_CLEAR_LINE, warnstring)
} else {
fmt.Fprintf(os.Stderr, "%s[%sWARN%s] %s\n", TERMINAL_CLEAR_LINE, ANSI_RED, ANSI_CLEAR, warnstring)
}
} }
} }