Merge pull request #7 from ffuf/stop403
New flag to stop on spurious 403 responses
This commit is contained in:
commit
14ce9943a0
1
main.go
1
main.go
@ -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()
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user