Progress and duration
This commit is contained in:
parent
d869393b81
commit
cee6b2f25b
2
main.go
2
main.go
@ -53,7 +53,7 @@ func main() {
|
|||||||
flag.StringVar(&conf.Data, "d", "", "POST data.")
|
flag.StringVar(&conf.Data, "d", "", "POST data.")
|
||||||
//flag.StringVar(&opts.filterRegex, "fr", "", "Filter regex")
|
//flag.StringVar(&opts.filterRegex, "fr", "", "Filter regex")
|
||||||
//flag.StringVar(&opts.filterReflect, "fref", "", "Filter reflected payload")
|
//flag.StringVar(&opts.filterReflect, "fref", "", "Filter reflected payload")
|
||||||
flag.StringVar(&opts.matcherStatus, "mc", "200,204,301,302,307", "Match HTTP status codes from respose")
|
flag.StringVar(&opts.matcherStatus, "mc", "200,204,301,302,307,401", "Match HTTP status codes from respose")
|
||||||
flag.StringVar(&opts.matcherSize, "ms", "", "Match HTTP response size")
|
flag.StringVar(&opts.matcherSize, "ms", "", "Match HTTP response size")
|
||||||
//flag.StringVar(&opts.matcherRegex, "mr", "", "Match regex")
|
//flag.StringVar(&opts.matcherRegex, "mr", "", "Match regex")
|
||||||
flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use.")
|
flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use.")
|
||||||
|
|||||||
@ -16,10 +16,13 @@ type RunnerProvider interface {
|
|||||||
type InputProvider interface {
|
type InputProvider interface {
|
||||||
Next() bool
|
Next() bool
|
||||||
Value() []byte
|
Value() []byte
|
||||||
|
Total() int
|
||||||
}
|
}
|
||||||
|
|
||||||
//OutputProvider is responsible of providing output from the RunnerProvider
|
//OutputProvider is responsible of providing output from the RunnerProvider
|
||||||
type OutputProvider interface {
|
type OutputProvider interface {
|
||||||
Banner() error
|
Banner() error
|
||||||
Result(resp Response)
|
Finalize() error
|
||||||
|
Error(errstring string)
|
||||||
|
Result(resp Response) bool
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,20 @@ package ffuf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Job ties together Config, Runner, Input and Output
|
//Job ties together Config, Runner, Input and Output
|
||||||
type Job struct {
|
type Job struct {
|
||||||
Config *Config
|
Config *Config
|
||||||
Input InputProvider
|
Input InputProvider
|
||||||
Runner RunnerProvider
|
Runner RunnerProvider
|
||||||
Output OutputProvider
|
Output OutputProvider
|
||||||
Counter int
|
Counter int
|
||||||
Running bool
|
Total int
|
||||||
|
Running bool
|
||||||
|
startTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJob(conf *Config) Job {
|
func NewJob(conf *Config) Job {
|
||||||
@ -25,18 +27,23 @@ 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() {
|
||||||
|
j.Total = j.Input.Total()
|
||||||
defer j.Stop()
|
defer j.Stop()
|
||||||
|
//Show banner if not running in silent mode
|
||||||
if !j.Config.Quiet {
|
if !j.Config.Quiet {
|
||||||
j.Output.Banner()
|
j.Output.Banner()
|
||||||
}
|
}
|
||||||
j.Running = true
|
j.Running = true
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go j.runProgress(&wg)
|
||||||
//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() {
|
||||||
limiter <- true
|
limiter <- true
|
||||||
wg.Add(1)
|
|
||||||
nextInput := j.Input.Value()
|
nextInput := j.Input.Value()
|
||||||
|
wg.Add(1)
|
||||||
|
j.Counter++
|
||||||
go func() {
|
go func() {
|
||||||
defer func() { <-limiter }()
|
defer func() { <-limiter }()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@ -44,21 +51,50 @@ func (j *Job) Start() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
j.updateProgress()
|
||||||
|
j.Output.Finalize()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *Job) runProgress(wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
j.startTime = time.Now()
|
||||||
|
totalProgress := j.Input.Total()
|
||||||
|
for j.Counter <= totalProgress {
|
||||||
|
j.updateProgress()
|
||||||
|
if j.Counter == totalProgress {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Job) updateProgress() {
|
||||||
|
dur := time.Now().Sub(j.startTime)
|
||||||
|
hours := dur / time.Hour
|
||||||
|
dur -= hours * time.Hour
|
||||||
|
mins := dur / time.Minute
|
||||||
|
dur -= mins * time.Minute
|
||||||
|
secs := dur / time.Second
|
||||||
|
progString := fmt.Sprintf(":: Progress: [%d/%d] :: Duration: [%d:%02d:%02d] ::", j.Counter, j.Total, hours, mins, secs)
|
||||||
|
j.Output.Error(progString)
|
||||||
|
}
|
||||||
|
|
||||||
func (j *Job) runTask(input []byte) {
|
func (j *Job) runTask(input []byte) {
|
||||||
req, err := j.Runner.Prepare(input)
|
req, err := j.Runner.Prepare(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Encountered error while preparing request: %s\n", err)
|
j.Output.Error(fmt.Sprintf("Encountered error while preparing request: %s\n", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := j.Runner.Execute(&req)
|
resp, err := j.Runner.Execute(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error in runner: %s\n", err)
|
j.Output.Error(fmt.Sprintf("Error in runner: %s\n", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
j.Output.Result(resp)
|
if j.Output.Result(resp) {
|
||||||
|
// Refresh the progress indicator as we printed something out
|
||||||
|
j.updateProgress()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,18 +27,25 @@ func NewWordlistInput(conf *ffuf.Config) (*WordlistInput, error) {
|
|||||||
return &wl, err
|
return &wl, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Next will increment the cursor position, and return a boolean telling if there's words left in the list
|
||||||
func (w *WordlistInput) Next() bool {
|
func (w *WordlistInput) Next() bool {
|
||||||
w.position++
|
w.position++
|
||||||
if w.position >= len(w.data)-1 {
|
if w.position >= len(w.data) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Value returns the value from wordlist at current cursor position
|
||||||
func (w *WordlistInput) Value() []byte {
|
func (w *WordlistInput) Value() []byte {
|
||||||
return w.data[w.position]
|
return w.data[w.position]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Total returns the size of wordlist
|
||||||
|
func (w *WordlistInput) Total() int {
|
||||||
|
return len(w.data)
|
||||||
|
}
|
||||||
|
|
||||||
//validFile checks that the wordlist file exists and can be read
|
//validFile checks that the wordlist file exists and can be read
|
||||||
func (w *WordlistInput) validFile(path string) (bool, error) {
|
func (w *WordlistInput) validFile(path string) (bool, error) {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
|
|||||||
7
pkg/output/const.go
Normal file
7
pkg/output/const.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package output
|
||||||
|
|
||||||
|
const (
|
||||||
|
TERMINAL_CLEAR_LINE = "\r\x1b[2K"
|
||||||
|
)
|
||||||
7
pkg/output/const_windows.go
Normal file
7
pkg/output/const_windows.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package output
|
||||||
|
|
||||||
|
const (
|
||||||
|
TERMINAL_CLEAR_LINE = "\r\r"
|
||||||
|
)
|
||||||
@ -2,6 +2,7 @@ package output
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/ffuf/ffuf/pkg/ffuf"
|
"github.com/ffuf/ffuf/pkg/ffuf"
|
||||||
)
|
)
|
||||||
@ -42,7 +43,20 @@ func (s *Stdoutput) Banner() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stdoutput) Result(resp ffuf.Response) {
|
func (s *Stdoutput) Error(errstring string) {
|
||||||
|
if s.config.Quiet {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s", errstring)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s%s", TERMINAL_CLEAR_LINE, errstring)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stdoutput) Finalize() error {
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stdoutput) Result(resp ffuf.Response) bool {
|
||||||
matched := false
|
matched := false
|
||||||
for _, m := range s.config.Matchers {
|
for _, m := range s.config.Matchers {
|
||||||
match, err := m.Filter(&resp)
|
match, err := m.Filter(&resp)
|
||||||
@ -55,7 +69,7 @@ func (s *Stdoutput) Result(resp ffuf.Response) {
|
|||||||
}
|
}
|
||||||
// The response was not matched, return before running filters
|
// The response was not matched, return before running filters
|
||||||
if !matched {
|
if !matched {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
for _, f := range s.config.Filters {
|
for _, f := range s.config.Filters {
|
||||||
fv, err := f.Filter(&resp)
|
fv, err := f.Filter(&resp)
|
||||||
@ -63,11 +77,12 @@ func (s *Stdoutput) Result(resp ffuf.Response) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if fv {
|
if fv {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Response survived the filtering, output the result
|
// Response survived the filtering, output the result
|
||||||
s.printResult(resp)
|
s.printResult(resp)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stdoutput) printResult(resp ffuf.Response) {
|
func (s *Stdoutput) printResult(resp ffuf.Response) {
|
||||||
@ -83,9 +98,10 @@ func (s *Stdoutput) resultQuiet(resp ffuf.Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stdoutput) resultNormal(resp ffuf.Response) {
|
func (s *Stdoutput) resultNormal(resp ffuf.Response) {
|
||||||
res_str := fmt.Sprintf("%-23s [Status: %d, Size: %d]", resp.Request.Input, resp.StatusCode, resp.ContentLength)
|
res_str := fmt.Sprintf("%s%-23s [Status: %d, Size: %d]", TERMINAL_CLEAR_LINE, resp.Request.Input, resp.StatusCode, resp.ContentLength)
|
||||||
fmt.Println(res_str)
|
fmt.Println(res_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printOption(name []byte, value []byte) {
|
func printOption(name []byte, value []byte) {
|
||||||
fmt.Printf(" :: %-12s : %s\n", name, value)
|
fmt.Printf(" :: %-12s : %s\n", name, value)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user