mirror of
https://github.com/ffuf/ffuf
synced 2024-11-10 06:04:17 +00:00
Fix the multi wordlist bug and add a new mode of operation (#93)
* Fix the multi wordlist bug and add a new mode * Add a README entry
This commit is contained in:
parent
73922822f9
commit
ac141e5e34
7 changed files with 114 additions and 15 deletions
|
@ -144,6 +144,8 @@ Usage of ./ffuf:
|
|||
Match HTTP status codes from respose, use "all" to match every response code. (default "200,204,301,302,307,401,403")
|
||||
-ml string
|
||||
Match amount of lines in response
|
||||
-mode string
|
||||
Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork (default "clusterbomb")
|
||||
-mr string
|
||||
Match regexp
|
||||
-ms string
|
||||
|
@ -190,7 +192,9 @@ The only dependency of ffuf is Go 1.11. No dependencies outside of Go standard l
|
|||
|
||||
- master
|
||||
- New
|
||||
- Added a new flag to select a multi wordlist operation mode: `--mode`, possible values: `clusterbomb` and `pitchfork`.
|
||||
- Changed
|
||||
- Fixed a bug in the default multi wordlist mode
|
||||
|
||||
- v0.11
|
||||
|
||||
|
|
8
main.go
8
main.go
|
@ -80,6 +80,7 @@ func main() {
|
|||
flag.BoolVar(&ignored, "compressed", true, "Dummy flag for copy as curl functionality (ignored)")
|
||||
flag.Var(&opts.inputcommands, "input-cmd", "Command producing the input. --input-num is required when using this input method. Overrides -w.")
|
||||
flag.IntVar(&conf.InputNum, "input-num", 100, "Number of inputs to test. Used in conjunction with --input-cmd.")
|
||||
flag.StringVar(&conf.InputMode, "mode", "clusterbomb", "Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork")
|
||||
flag.BoolVar(&ignored, "i", true, "Dummy flag for copy as curl functionality (ignored)")
|
||||
flag.Var(&opts.cookies, "b", "Cookie data `\"NAME1=VALUE1; NAME2=VALUE2\"` for copy as curl functionality.\nResults unpredictable when combined with -H \"Cookie: ...\"")
|
||||
flag.Var(&opts.cookies, "cookie", "Cookie data (alias of -b)")
|
||||
|
@ -150,7 +151,10 @@ func main() {
|
|||
func prepareJob(conf *ffuf.Config) (*ffuf.Job, error) {
|
||||
errs := ffuf.NewMultierror()
|
||||
var err error
|
||||
inputprovider := input.NewInputProvider(conf)
|
||||
inputprovider, err := input.NewInputProvider(conf)
|
||||
if err != nil {
|
||||
errs.Add(err)
|
||||
}
|
||||
// TODO: implement error handling for runnerprovider and outputprovider
|
||||
// We only have http runner right now
|
||||
runprovider := runner.NewRunnerByName("http", conf)
|
||||
|
@ -158,7 +162,7 @@ func prepareJob(conf *ffuf.Config) (*ffuf.Job, error) {
|
|||
for _, v := range conf.InputProviders {
|
||||
err = inputprovider.AddProvider(v)
|
||||
if err != nil {
|
||||
errs.Add(fmt.Errorf("%s", err))
|
||||
errs.Add(err)
|
||||
}
|
||||
}
|
||||
// We only have stdout outputprovider right now
|
||||
|
|
|
@ -28,6 +28,7 @@ type Config struct {
|
|||
InputProviders []InputProviderConfig
|
||||
CommandKeywords []string
|
||||
InputNum int
|
||||
InputMode string
|
||||
OutputFile string
|
||||
OutputFormat string
|
||||
StopOn403 bool
|
||||
|
@ -71,6 +72,7 @@ func NewConfig(ctx context.Context) Config {
|
|||
conf.InputProviders = make([]InputProviderConfig, 0)
|
||||
conf.CommandKeywords = make([]string, 0)
|
||||
conf.InputNum = 0
|
||||
conf.InputMode = "clusterbomb"
|
||||
conf.ProxyURL = http.ProxyFromEnvironment
|
||||
conf.Filters = make([]FilterProvider, 0)
|
||||
conf.Delay = optRange{0, 0, false, false}
|
||||
|
|
|
@ -27,6 +27,7 @@ type InternalInputProvider interface {
|
|||
Next() bool
|
||||
Position() int
|
||||
ResetPosition()
|
||||
IncrementPosition()
|
||||
Value() []byte
|
||||
Total() int
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func NewCommandInput(keyword string, value string, conf *ffuf.Config) (*CommandI
|
|||
var cmd CommandInput
|
||||
cmd.keyword = keyword
|
||||
cmd.config = conf
|
||||
cmd.count = -1
|
||||
cmd.count = 0
|
||||
cmd.command = value
|
||||
return &cmd, nil
|
||||
}
|
||||
|
@ -40,9 +40,13 @@ func (c *CommandInput) ResetPosition() {
|
|||
c.count = 0
|
||||
}
|
||||
|
||||
//IncrementPosition increments the current position in the inputprovider
|
||||
func (c *CommandInput) IncrementPosition() {
|
||||
c.count += 1
|
||||
}
|
||||
|
||||
//Next will increment the cursor position, and return a boolean telling if there's iterations left
|
||||
func (c *CommandInput) Next() bool {
|
||||
c.count++
|
||||
if c.count >= c.config.InputNum {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
package input
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ffuf/ffuf/pkg/ffuf"
|
||||
)
|
||||
|
||||
type MainInputProvider struct {
|
||||
Providers []ffuf.InternalInputProvider
|
||||
Config *ffuf.Config
|
||||
position int
|
||||
Providers []ffuf.InternalInputProvider
|
||||
Config *ffuf.Config
|
||||
position int
|
||||
msbIterator int
|
||||
}
|
||||
|
||||
func NewInputProvider(conf *ffuf.Config) ffuf.InputProvider {
|
||||
return &MainInputProvider{Config: conf}
|
||||
func NewInputProvider(conf *ffuf.Config) (ffuf.InputProvider, error) {
|
||||
validmode := false
|
||||
for _, mode := range []string{"clusterbomb", "pitchfork"} {
|
||||
if conf.InputMode == mode {
|
||||
validmode = true
|
||||
}
|
||||
}
|
||||
if !validmode {
|
||||
return &MainInputProvider{}, fmt.Errorf("Input mode (-mode) %s not recognized", conf.InputMode)
|
||||
}
|
||||
return &MainInputProvider{Config: conf, msbIterator: 0}, nil
|
||||
}
|
||||
|
||||
func (i *MainInputProvider) AddProvider(provider ffuf.InputProviderConfig) error {
|
||||
|
@ -43,8 +55,21 @@ func (i *MainInputProvider) Next() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
//Value returns a map of keyword:value pairs including all inputs
|
||||
//Value returns a map of inputs for keywords
|
||||
func (i *MainInputProvider) Value() map[string][]byte {
|
||||
retval := make(map[string][]byte)
|
||||
if i.Config.InputMode == "clusterbomb" {
|
||||
retval = i.clusterbombValue()
|
||||
}
|
||||
if i.Config.InputMode == "pitchfork" {
|
||||
retval = i.pitchforkValue()
|
||||
}
|
||||
return retval
|
||||
}
|
||||
|
||||
//pitchforkValue returns a map of keyword:value pairs including all inputs.
|
||||
//This mode will iterate through wordlists in lockstep.
|
||||
func (i *MainInputProvider) pitchforkValue() map[string][]byte {
|
||||
values := make(map[string][]byte)
|
||||
for _, p := range i.Providers {
|
||||
if !p.Next() {
|
||||
|
@ -52,15 +77,70 @@ func (i *MainInputProvider) Value() map[string][]byte {
|
|||
p.ResetPosition()
|
||||
}
|
||||
values[p.Keyword()] = p.Value()
|
||||
p.IncrementPosition()
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
//clusterbombValue returns map of keyword:value pairs including all inputs.
|
||||
//this mode will iterate through all possible combinations.
|
||||
func (i *MainInputProvider) clusterbombValue() map[string][]byte {
|
||||
values := make(map[string][]byte)
|
||||
// Should we signal the next InputProvider in the slice to increment
|
||||
signalNext := false
|
||||
first := true
|
||||
for index, p := range i.Providers {
|
||||
if signalNext {
|
||||
p.IncrementPosition()
|
||||
signalNext = false
|
||||
}
|
||||
if !p.Next() {
|
||||
// No more inputs in this inputprovider
|
||||
if index == i.msbIterator {
|
||||
// Reset all previous wordlists and increment the msb counter
|
||||
i.msbIterator += 1
|
||||
i.clusterbombIteratorReset()
|
||||
// Start again
|
||||
return i.clusterbombValue()
|
||||
}
|
||||
p.ResetPosition()
|
||||
signalNext = true
|
||||
}
|
||||
values[p.Keyword()] = p.Value()
|
||||
if first {
|
||||
p.IncrementPosition()
|
||||
first = false
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func (i *MainInputProvider) clusterbombIteratorReset() {
|
||||
for index, p := range i.Providers {
|
||||
if index < i.msbIterator {
|
||||
p.ResetPosition()
|
||||
}
|
||||
if index == i.msbIterator {
|
||||
p.IncrementPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Total returns the amount of input combinations available
|
||||
func (i *MainInputProvider) Total() int {
|
||||
count := 1
|
||||
for _, p := range i.Providers {
|
||||
count = count * p.Total()
|
||||
count := 0
|
||||
if i.Config.InputMode == "pitchfork" {
|
||||
for _, p := range i.Providers {
|
||||
if p.Total() > count {
|
||||
count = p.Total()
|
||||
}
|
||||
}
|
||||
}
|
||||
if i.Config.InputMode == "clusterbomb" {
|
||||
count = 1
|
||||
for _, p := range i.Providers {
|
||||
count = count * p.Total()
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func NewWordlistInput(keyword string, value string, conf *ffuf.Config) (*Wordlis
|
|||
var wl WordlistInput
|
||||
wl.keyword = keyword
|
||||
wl.config = conf
|
||||
wl.position = -1
|
||||
wl.position = 0
|
||||
var valid bool
|
||||
var err error
|
||||
// stdin?
|
||||
|
@ -56,13 +56,17 @@ func (w *WordlistInput) Keyword() string {
|
|||
|
||||
//Next will increment the cursor position, and return a boolean telling if there's words left in the list
|
||||
func (w *WordlistInput) Next() bool {
|
||||
w.position++
|
||||
if w.position >= len(w.data) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//IncrementPosition will increment the current position in the inputprovider data slice
|
||||
func (w *WordlistInput) IncrementPosition() {
|
||||
w.position += 1
|
||||
}
|
||||
|
||||
//Value returns the value from wordlist at current cursor position
|
||||
func (w *WordlistInput) Value() []byte {
|
||||
return w.data[w.position]
|
||||
|
|
Loading…
Reference in a new issue