Skip to content

Commit

Permalink
first work for #298
Browse files Browse the repository at this point in the history
allow for a totalrequests change from within a plugin
  • Loading branch information
firefart committed Sep 28, 2021
1 parent 998257f commit c249daa
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 57 deletions.
14 changes: 7 additions & 7 deletions cli/gobuster.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup,
defer f.Close()
}

for r := range g.Results() {
for r := range g.Progress.ResultChan {
s, err := r.ResultToString()
if err != nil {
g.LogError.Fatal(err)
Expand Down Expand Up @@ -81,7 +81,7 @@ func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup,
func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup, output *outputType) {
defer wg.Done()

for e := range g.Errors() {
for e := range g.Progress.ErrorChan {
if !g.Opts.Quiet && !g.Opts.NoError {
output.Mu.Lock()
s := fmt.Sprintf("[!] %s\n", e.Error())
Expand Down Expand Up @@ -109,16 +109,17 @@ func progressWorker(ctx context.Context, g *libgobuster.Gobuster, wg *sync.WaitG
select {
case <-tick.C:
if !g.Opts.Quiet && !g.Opts.NoProgress {
g.RequestsCountMutex.RLock()
requestsIssued := g.Progress.RequestsIssued()
requestsExpected := g.Progress.RequestsExpected()
output.Mu.Lock()
var charsWritten int
if g.Opts.Wordlist == "-" {
s := fmt.Sprintf("\rProgress: %d", g.RequestsIssued)
s := fmt.Sprintf("\rProgress: %d", requestsIssued)
s = rightPad(s, " ", output.MaxCharsWritten)
charsWritten, _ = fmt.Fprint(os.Stderr, s)
// only print status if we already read in the wordlist
} else if g.RequestsExpected > 0 {
s := fmt.Sprintf("\rProgress: %d / %d (%3.2f%%)", g.RequestsIssued, g.RequestsExpected, float32(g.RequestsIssued)*100.0/float32(g.RequestsExpected))
} else if requestsExpected > 0 {
s := fmt.Sprintf("\rProgress: %d / %d (%3.2f%%)", requestsIssued, requestsExpected, float32(requestsIssued)*100.0/float32(requestsExpected))
s = rightPad(s, " ", output.MaxCharsWritten)
charsWritten, _ = fmt.Fprint(os.Stderr, s)
}
Expand All @@ -127,7 +128,6 @@ func progressWorker(ctx context.Context, g *libgobuster.Gobuster, wg *sync.WaitG
}

output.Mu.Unlock()
g.RequestsCountMutex.RUnlock()
}
case <-ctx.Done():
return
Expand Down
4 changes: 2 additions & 2 deletions gobusterdir/gobusterdir.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func (d *GobusterDir) AdditionalWords(word string) []string {
}

// ProcessWord is the process implementation of gobusterdir
func (d *GobusterDir) ProcessWord(ctx context.Context, word string, resChannel chan<- libgobuster.Result) error {
func (d *GobusterDir) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
suffix := ""
if d.options.UseSlash {
suffix = "/"
Expand Down Expand Up @@ -212,7 +212,7 @@ func (d *GobusterDir) ProcessWord(ctx context.Context, word string, resChannel c
}

if (resultStatus && !helper.SliceContains(d.options.ExcludeLength, int(size))) || d.globalopts.Verbose {
resChannel <- Result{
progress.ResultChan <- Result{
URL: d.options.URL,
Path: entity,
Verbose: d.globalopts.Verbose,
Expand Down
6 changes: 3 additions & 3 deletions gobusterdns/gobusterdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (d *GobusterDNS) PreRun(ctx context.Context) error {
}

// ProcessWord is the process implementation of gobusterdns
func (d *GobusterDNS) ProcessWord(ctx context.Context, word string, resChannel chan<- libgobuster.Result) error {
func (d *GobusterDNS) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
subdomain := fmt.Sprintf("%s.%s", word, d.options.Domain)
ips, err := d.dnsLookup(ctx, subdomain)
if err == nil {
Expand All @@ -121,10 +121,10 @@ func (d *GobusterDNS) ProcessWord(ctx context.Context, word string, resChannel c
result.CNAME = cname
}
}
resChannel <- result
progress.ResultChan <- result
}
} else if d.globalopts.Verbose {
resChannel <- Result{
progress.ResultChan <- Result{
Subdomain: subdomain,
Found: false,
ShowIPs: d.options.ShowIPs,
Expand Down
4 changes: 2 additions & 2 deletions gobusterfuzz/gobusterfuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (d *GobusterFuzz) PreRun(ctx context.Context) error {
}

// ProcessWord is the process implementation of gobusterfuzz
func (d *GobusterFuzz) ProcessWord(ctx context.Context, word string, resChannel chan<- libgobuster.Result) error {
func (d *GobusterFuzz) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
url := strings.ReplaceAll(d.options.URL, "FUZZ", word)

tries := 1
Expand Down Expand Up @@ -124,7 +124,7 @@ func (d *GobusterFuzz) ProcessWord(ctx context.Context, word string, resChannel
}

if resultStatus || d.globalopts.Verbose {
resChannel <- Result{
progress.ResultChan <- Result{
Verbose: d.globalopts.Verbose,
Found: resultStatus,
Path: url,
Expand Down
4 changes: 2 additions & 2 deletions gobusters3/gobusters3.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (s *GobusterS3) PreRun(ctx context.Context) error {
}

// ProcessWord is the process implementation of GobusterS3
func (s *GobusterS3) ProcessWord(ctx context.Context, word string, resChannel chan<- libgobuster.Result) error {
func (s *GobusterS3) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
// only check for valid bucket names
if !s.isValidBucketName(word) {
return nil
Expand Down Expand Up @@ -156,7 +156,7 @@ func (s *GobusterS3) ProcessWord(ctx context.Context, word string, resChannel ch
}
}

resChannel <- Result{
progress.ResultChan <- Result{
Found: found,
BucketName: word,
Status: extraStr,
Expand Down
4 changes: 2 additions & 2 deletions gobustervhost/gobustervhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (v *GobusterVhost) PreRun(ctx context.Context) error {
}

// ProcessWord is the process implementation of gobusterdir
func (v *GobusterVhost) ProcessWord(ctx context.Context, word string, resChannel chan<- libgobuster.Result) error {
func (v *GobusterVhost) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
var subdomain string
if v.options.AppendDomain {
subdomain = fmt.Sprintf("%s.%s", word, v.domain)
Expand Down Expand Up @@ -148,7 +148,7 @@ func (v *GobusterVhost) ProcessWord(ctx context.Context, word string, resChannel
if found {
resultStatus = true
}
resChannel <- Result{
progress.ResultChan <- Result{
Found: resultStatus,
Vhost: subdomain,
StatusCode: *statusCode,
Expand Down
2 changes: 1 addition & 1 deletion libgobuster/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "context"
type GobusterPlugin interface {
Name() string
PreRun(context.Context) error
ProcessWord(context.Context, string, chan<- Result) error
ProcessWord(context.Context, string, *Progress) error
AdditionalWords(string) []string
GetConfigString() (string, error)
}
Expand Down
53 changes: 15 additions & 38 deletions libgobuster/libgobuster.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,47 +25,25 @@ type ResultToStringFunc func(*Gobuster, *Result) (*string, error)

// Gobuster is the main object when creating a new run
type Gobuster struct {
Opts *Options
RequestsExpected int
RequestsIssued int
RequestsCountMutex *sync.RWMutex
plugin GobusterPlugin
resultChan chan Result
errorChan chan error
LogInfo *log.Logger
LogError *log.Logger
Opts *Options
plugin GobusterPlugin
LogInfo *log.Logger
LogError *log.Logger
Progress *Progress
}

// NewGobuster returns a new Gobuster object
func NewGobuster(opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
var g Gobuster
g.Opts = opts
g.plugin = plugin
g.RequestsCountMutex = new(sync.RWMutex)
g.resultChan = make(chan Result)
g.errorChan = make(chan error)
g.LogInfo = log.New(os.Stdout, "", log.LstdFlags)
g.LogError = log.New(os.Stderr, "\r[ERROR] ", log.LstdFlags)
g.Progress = NewProgress()

return &g, nil
}

// Results returns a channel of Results
func (g *Gobuster) Results() <-chan Result {
return g.resultChan
}

// Errors returns a channel of errors
func (g *Gobuster) Errors() <-chan error {
return g.errorChan
}

func (g *Gobuster) incrementRequests() {
g.RequestsCountMutex.Lock()
g.RequestsIssued++
g.RequestsCountMutex.Unlock()
}

func (g *Gobuster) worker(ctx context.Context, wordChan <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for {
Expand All @@ -77,7 +55,7 @@ func (g *Gobuster) worker(ctx context.Context, wordChan <-chan string, wg *sync.
if !ok {
return
}
g.incrementRequests()
g.Progress.incrementRequests()

wordCleaned := strings.TrimSpace(word)
// Skip "comment" (starts with #), as well as empty lines
Expand All @@ -86,10 +64,10 @@ func (g *Gobuster) worker(ctx context.Context, wordChan <-chan string, wg *sync.
}

// Mode-specific processing
err := g.plugin.ProcessWord(ctx, wordCleaned, g.resultChan)
err := g.plugin.ProcessWord(ctx, wordCleaned, g.Progress)
if err != nil {
// do not exit and continue
g.errorChan <- err
g.Progress.ErrorChan <- err
continue
}

Expand Down Expand Up @@ -117,17 +95,16 @@ func (g *Gobuster) getWordlist() (*bufio.Scanner, error) {
return nil, fmt.Errorf("failed to get number of lines: %w", err)
}

g.RequestsIssued = 0

// calcutate expected requests
g.RequestsExpected = lines
g.Progress.IncrementTotalRequests(lines)

// call the function once with a dummy entry to receive the number
// of custom words per wordlist word
customWordsLen := len(g.plugin.AdditionalWords("dummy"))
if customWordsLen > 0 {
// + 1 for the initial word
g.RequestsExpected *= (customWordsLen + 1)
origExpected := g.Progress.RequestsExpected()
inc := origExpected * customWordsLen
g.Progress.IncrementTotalRequests(inc)
}

// rewind wordlist
Expand All @@ -141,8 +118,8 @@ func (g *Gobuster) getWordlist() (*bufio.Scanner, error) {
// Run the busting of the website with the given
// set of settings from the command line.
func (g *Gobuster) Run(ctx context.Context) error {
defer close(g.resultChan)
defer close(g.errorChan)
defer close(g.Progress.ResultChan)
defer close(g.Progress.ErrorChan)

if err := g.plugin.PreRun(ctx); err != nil {
return err
Expand Down
46 changes: 46 additions & 0 deletions libgobuster/progress.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package libgobuster

import "sync"

type Progress struct {
requestsExpectedMutex *sync.RWMutex
requestsExpected int
requestsCountMutex *sync.RWMutex
requestsIssued int
ResultChan chan Result
ErrorChan chan error
}

func NewProgress() *Progress {
var p Progress
p.requestsIssued = 0
p.requestsExpectedMutex = new(sync.RWMutex)
p.requestsCountMutex = new(sync.RWMutex)
p.ResultChan = make(chan Result)
p.ErrorChan = make(chan error)
return &p
}

func (p *Progress) RequestsExpected() int {
p.requestsExpectedMutex.RLock()
defer p.requestsExpectedMutex.RUnlock()
return p.requestsExpected
}

func (p *Progress) RequestsIssued() int {
p.requestsCountMutex.RLock()
defer p.requestsCountMutex.RUnlock()
return p.requestsIssued
}

func (p *Progress) incrementRequests() {
p.requestsCountMutex.Lock()
p.requestsIssued++
p.requestsCountMutex.Unlock()
}

func (p *Progress) IncrementTotalRequests(by int) {
p.requestsCountMutex.Lock()
p.requestsExpected += by
p.requestsCountMutex.Unlock()
}

0 comments on commit c249daa

Please sign in to comment.