Add delay option to ffuf
This commit is contained in:
parent
66c46c35c0
commit
4563a93b46
@ -71,9 +71,9 @@ ffuf -w /path/to/postdata.txt -X POST -d "username=admin\&password=FUZZ" https:/
|
|||||||
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
|
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
|
||||||
|
|
||||||
```
|
```
|
||||||
Usage of ./ffuf:
|
|
||||||
-H "Name: Value"
|
-H "Name: Value"
|
||||||
Header "Name: Value", separated by colon. Multiple -H flags are accepted.
|
Header "Name: Value", separated by colon. Multiple -H flags are accepted.
|
||||||
|
-V Show version information.
|
||||||
-X string
|
-X string
|
||||||
HTTP method to use. (default "GET")
|
HTTP method to use. (default "GET")
|
||||||
-c Colorize output.
|
-c Colorize output.
|
||||||
@ -89,13 +89,15 @@ Usage of ./ffuf:
|
|||||||
Filter by amount of words in response
|
Filter by amount of words in response
|
||||||
-k Skip TLS identity verification (insecure)
|
-k Skip TLS identity verification (insecure)
|
||||||
-mc string
|
-mc string
|
||||||
Match HTTP status codes from respose (default "200,204,301,302,307,401")
|
Match HTTP status codes from respose (default "200,204,301,302,307,401,403")
|
||||||
-mr string
|
-mr string
|
||||||
Match regexp
|
Match regexp
|
||||||
-ms string
|
-ms string
|
||||||
Match HTTP response size
|
Match HTTP response size
|
||||||
-mw string
|
-mw string
|
||||||
Match amount of words in response
|
Match amount of words in response
|
||||||
|
-p delay
|
||||||
|
Seconds of delay between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
|
||||||
-s Do not print additional information (silent mode)
|
-s Do not print additional information (silent mode)
|
||||||
-t int
|
-t int
|
||||||
Number of concurrent threads. (default 40)
|
Number of concurrent threads. (default 40)
|
||||||
|
|||||||
37
main.go
37
main.go
@ -5,6 +5,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ffuf/ffuf/pkg/ffuf"
|
"github.com/ffuf/ffuf/pkg/ffuf"
|
||||||
@ -15,6 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type cliOptions struct {
|
type cliOptions struct {
|
||||||
|
delay string
|
||||||
filterStatus string
|
filterStatus string
|
||||||
filterSize string
|
filterSize string
|
||||||
filterRegexp string
|
filterRegexp string
|
||||||
@ -23,18 +25,18 @@ type cliOptions struct {
|
|||||||
matcherSize string
|
matcherSize string
|
||||||
matcherRegexp string
|
matcherRegexp string
|
||||||
matcherWords string
|
matcherWords string
|
||||||
headers headerFlags
|
headers multiStringFlag
|
||||||
showVersion bool
|
showVersion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type headerFlags []string
|
type multiStringFlag []string
|
||||||
|
|
||||||
func (h *headerFlags) String() string {
|
func (m *multiStringFlag) String() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *headerFlags) Set(value string) error {
|
func (m *multiStringFlag) Set(value string) error {
|
||||||
*h = append(*h, value)
|
*m = append(*m, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ func main() {
|
|||||||
flag.StringVar(&conf.Url, "u", "", "Target URL")
|
flag.StringVar(&conf.Url, "u", "", "Target URL")
|
||||||
flag.StringVar(&conf.Wordlist, "w", "", "Wordlist path")
|
flag.StringVar(&conf.Wordlist, "w", "", "Wordlist path")
|
||||||
flag.BoolVar(&conf.TLSSkipVerify, "k", false, "Skip TLS identity verification (insecure)")
|
flag.BoolVar(&conf.TLSSkipVerify, "k", false, "Skip TLS identity verification (insecure)")
|
||||||
|
flag.StringVar(&opts.delay, "p", "", "Seconds of `delay` between requests, or a range of random delay. For example \"0.1\" or \"0.1-2.0\"")
|
||||||
flag.StringVar(&opts.filterStatus, "fc", "", "Filter HTTP status codes from response")
|
flag.StringVar(&opts.filterStatus, "fc", "", "Filter HTTP status codes from response")
|
||||||
flag.StringVar(&opts.filterSize, "fs", "", "Filter HTTP response size")
|
flag.StringVar(&opts.filterSize, "fs", "", "Filter HTTP response size")
|
||||||
flag.StringVar(&opts.filterRegexp, "fr", "", "Filter regexp")
|
flag.StringVar(&opts.filterRegexp, "fr", "", "Filter regexp")
|
||||||
@ -111,6 +114,9 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||||||
//TODO: refactor in a proper flag library that can handle things like required flags
|
//TODO: refactor in a proper flag library that can handle things like required flags
|
||||||
errs := ffuf.NewMultierror()
|
errs := ffuf.NewMultierror()
|
||||||
foundkeyword := false
|
foundkeyword := false
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var err2 error
|
||||||
if len(conf.Url) == 0 {
|
if len(conf.Url) == 0 {
|
||||||
errs.Add(fmt.Errorf("-u flag is required"))
|
errs.Add(fmt.Errorf("-u flag is required"))
|
||||||
}
|
}
|
||||||
@ -138,6 +144,27 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||||||
errs.Add(fmt.Errorf("Header defined by -H needs to have a value. \":\" should be used as a separator"))
|
errs.Add(fmt.Errorf("Header defined by -H needs to have a value. \":\" should be used as a separator"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Prepare delay
|
||||||
|
d := strings.Split(parseOpts.delay, "-")
|
||||||
|
if len(d) > 2 {
|
||||||
|
errs.Add(fmt.Errorf("Delay needs to be either a single float: \"0.1\" or a range of floats, delimited by dash: \"0.1-0.8\""))
|
||||||
|
} else if len(d) == 2 {
|
||||||
|
conf.Delay.IsRange = true
|
||||||
|
conf.Delay.HasDelay = true
|
||||||
|
conf.Delay.Min, err = strconv.ParseFloat(d[0], 64)
|
||||||
|
conf.Delay.Max, err2 = strconv.ParseFloat(d[1], 64)
|
||||||
|
if err != nil || err2 != nil {
|
||||||
|
errs.Add(fmt.Errorf("Delay range min and max values need to be valid floats. For example: 0.1-0.5"))
|
||||||
|
}
|
||||||
|
} else if len(parseOpts.delay) > 0 {
|
||||||
|
conf.Delay.IsRange = false
|
||||||
|
conf.Delay.HasDelay = true
|
||||||
|
conf.Delay.Min, err = strconv.ParseFloat(parseOpts.delay, 64)
|
||||||
|
if err != nil {
|
||||||
|
errs.Add(fmt.Errorf("Delay needs to be either a single float: \"0.1\" or a range of floats, delimited by dash: \"0.1-0.8\""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//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
|
||||||
|
|||||||
@ -4,6 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//optRange stores either a single float, in which case the value is stored in min and IsRange is false,
|
||||||
|
//or a range of floats, in which case IsRange is true
|
||||||
|
type optRange struct {
|
||||||
|
Min float64
|
||||||
|
Max float64
|
||||||
|
IsRange bool
|
||||||
|
HasDelay bool
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
StaticHeaders map[string]string
|
StaticHeaders map[string]string
|
||||||
FuzzHeaders map[string]string
|
FuzzHeaders map[string]string
|
||||||
@ -14,6 +23,7 @@ type Config struct {
|
|||||||
Quiet bool
|
Quiet bool
|
||||||
Colors bool
|
Colors bool
|
||||||
Wordlist string
|
Wordlist string
|
||||||
|
Delay optRange
|
||||||
Filters []FilterProvider
|
Filters []FilterProvider
|
||||||
Matchers []FilterProvider
|
Matchers []FilterProvider
|
||||||
Threads int
|
Threads int
|
||||||
@ -31,5 +41,6 @@ func NewConfig(ctx context.Context) Config {
|
|||||||
conf.Data = ""
|
conf.Data = ""
|
||||||
conf.Quiet = false
|
conf.Quiet = false
|
||||||
conf.Filters = make([]FilterProvider, 0)
|
conf.Filters = make([]FilterProvider, 0)
|
||||||
|
conf.Delay = optRange{0, 0, false, false}
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package ffuf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -27,6 +28,7 @@ func NewJob(conf *Config) Job {
|
|||||||
|
|
||||||
//Start the execution of the Job
|
//Start the execution of the Job
|
||||||
func (j *Job) Start() {
|
func (j *Job) Start() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
j.Total = j.Input.Total()
|
j.Total = j.Input.Total()
|
||||||
defer j.Stop()
|
defer j.Stop()
|
||||||
//Show banner if not running in silent mode
|
//Show banner if not running in silent mode
|
||||||
@ -48,6 +50,16 @@ func (j *Job) Start() {
|
|||||||
defer func() { <-limiter }()
|
defer func() { <-limiter }()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
j.runTask([]byte(nextInput))
|
j.runTask([]byte(nextInput))
|
||||||
|
if j.Config.Delay.HasDelay {
|
||||||
|
var sleepDurationMS time.Duration
|
||||||
|
if j.Config.Delay.IsRange {
|
||||||
|
sTime := j.Config.Delay.Min + rand.Float64()*(j.Config.Delay.Max-j.Config.Delay.Min)
|
||||||
|
sleepDurationMS = time.Duration(sTime * 1000)
|
||||||
|
} else {
|
||||||
|
sleepDurationMS = time.Duration(j.Config.Delay.Min * 1000)
|
||||||
|
}
|
||||||
|
time.Sleep(sleepDurationMS * time.Millisecond)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user