diff --git a/.dockerignore b/.dockerignore index 12912e0b..0961e363 100644 --- a/.dockerignore +++ b/.dockerignore @@ -6,3 +6,4 @@ .vscode/ gobuster .git/ +mitm* diff --git a/.gitignore b/.gitignore index 2b2ce47f..365f52e3 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ .vscode/ gobuster + +mitm* diff --git a/cli/dir/dir.go b/cli/dir/dir.go index 2dbbbf44..ac94cc32 100644 --- a/cli/dir/dir.go +++ b/cli/dir/dir.go @@ -104,12 +104,13 @@ func run(c *cli.Context) error { return err } - plugin, err := gobusterdir.New(&globalOpts, pluginOpts) + log := libgobuster.NewLogger(globalOpts.Debug) + + plugin, err := gobusterdir.New(&globalOpts, pluginOpts, log) if err != nil { return fmt.Errorf("error on creating gobusterdir: %w", err) } - log := libgobuster.NewLogger(globalOpts.Debug) if err := internalcli.Gobuster(c.Context, &globalOpts, plugin, log); err != nil { var wErr *gobusterdir.ErrWildcard if errors.As(err, &wErr) { diff --git a/cli/dir/dir_test.go b/cli/dir/dir_test.go index 0bd934a2..e27f2b15 100644 --- a/cli/dir/dir_test.go +++ b/cli/dir/dir_test.go @@ -74,7 +74,7 @@ func BenchmarkDirMode(b *testing.B) { for x := 0; x < b.N; x++ { os.Stdout = devnull os.Stderr = devnull - plugin, err := gobusterdir.New(&globalopts, pluginopts) + plugin, err := gobusterdir.New(&globalopts, pluginopts, log) if err != nil { b.Fatalf("error on creating gobusterdir: %v", err) } diff --git a/cli/fuzz/fuzz.go b/cli/fuzz/fuzz.go index a25eeba4..111b62e1 100644 --- a/cli/fuzz/fuzz.go +++ b/cli/fuzz/fuzz.go @@ -67,12 +67,13 @@ func run(c *cli.Context) error { return fmt.Errorf("please provide the %s keyword", gobusterfuzz.FuzzKeyword) } - plugin, err := gobusterfuzz.New(&globalOpts, pluginOpts) + log := libgobuster.NewLogger(globalOpts.Debug) + + plugin, err := gobusterfuzz.New(&globalOpts, pluginOpts, log) if err != nil { return fmt.Errorf("error on creating gobusterfuzz: %w", err) } - log := libgobuster.NewLogger(globalOpts.Debug) if err := internalcli.Gobuster(c.Context, &globalOpts, plugin, log); err != nil { log.Debugf("%#v", err) return fmt.Errorf("error on running gobuster on %s: %w", pluginOpts.URL, err) diff --git a/cli/gcs/gcs.go b/cli/gcs/gcs.go index 12347888..b9def8ae 100644 --- a/cli/gcs/gcs.go +++ b/cli/gcs/gcs.go @@ -47,12 +47,13 @@ func run(c *cli.Context) error { return err } - plugin, err := gobustergcs.New(&globalOpts, pluginOpts) + log := libgobuster.NewLogger(globalOpts.Debug) + + plugin, err := gobustergcs.New(&globalOpts, pluginOpts, log) if err != nil { return fmt.Errorf("error on creating gobustergcs: %w", err) } - log := libgobuster.NewLogger(globalOpts.Debug) if err := internalcli.Gobuster(c.Context, &globalOpts, plugin, log); err != nil { log.Debugf("%#v", err) return fmt.Errorf("error on running gobuster: %w", err) diff --git a/cli/gobuster.go b/cli/gobuster.go index 20121af6..20fa8040 100644 --- a/cli/gobuster.go +++ b/cli/gobuster.go @@ -127,7 +127,7 @@ func writeToFile(f *os.File, output string) error { } // Gobuster is the main entry point for the CLI -func Gobuster(ctx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin, log libgobuster.Logger) error { +func Gobuster(ctx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin, log *libgobuster.Logger) error { // Sanity checks if opts == nil { return fmt.Errorf("please provide valid options") diff --git a/cli/s3/s3.go b/cli/s3/s3.go index 7376cfe6..decabbef 100644 --- a/cli/s3/s3.go +++ b/cli/s3/s3.go @@ -47,12 +47,13 @@ func run(c *cli.Context) error { return err } - plugin, err := gobusters3.New(&globalOpts, pluginOpts) + log := libgobuster.NewLogger(globalOpts.Debug) + + plugin, err := gobusters3.New(&globalOpts, pluginOpts, log) if err != nil { return fmt.Errorf("error on creating gobusters3: %w", err) } - log := libgobuster.NewLogger(globalOpts.Debug) if err := internalcli.Gobuster(c.Context, &globalOpts, plugin, log); err != nil { log.Debugf("%#v", err) return fmt.Errorf("error on running gobuster: %w", err) diff --git a/cli/vhost/vhost.go b/cli/vhost/vhost.go index 17024c1e..f6341bf5 100644 --- a/cli/vhost/vhost.go +++ b/cli/vhost/vhost.go @@ -64,12 +64,13 @@ func run(c *cli.Context) error { return err } - plugin, err := gobustervhost.New(&globalOpts, pluginOpts) + log := libgobuster.NewLogger(globalOpts.Debug) + + plugin, err := gobustervhost.New(&globalOpts, pluginOpts, log) if err != nil { return fmt.Errorf("error on creating gobustervhost: %w", err) } - log := libgobuster.NewLogger(globalOpts.Debug) if err := internalcli.Gobuster(c.Context, &globalOpts, plugin, log); err != nil { log.Debugf("%#v", err) return fmt.Errorf("error on running gobuster on %s: %w", pluginOpts.URL, err) diff --git a/cli/vhost/vhost_test.go b/cli/vhost/vhost_test.go index 2b563eab..6fd4424d 100644 --- a/cli/vhost/vhost_test.go +++ b/cli/vhost/vhost_test.go @@ -61,7 +61,7 @@ func BenchmarkVhostMode(b *testing.B) { for x := 0; x < b.N; x++ { os.Stdout = devnull os.Stderr = devnull - plugin, err := gobustervhost.New(&globalopts, pluginopts) + plugin, err := gobustervhost.New(&globalopts, pluginopts, log) if err != nil { b.Fatalf("error on creating gobusterdir: %v", err) } diff --git a/gobusterdir/gobusterdir.go b/gobusterdir/gobusterdir.go index 5336b7e9..11527e88 100644 --- a/gobusterdir/gobusterdir.go +++ b/gobusterdir/gobusterdir.go @@ -51,7 +51,7 @@ type GobusterDir struct { } // New creates a new initialized GobusterDir -func New(globalopts *libgobuster.Options, opts *OptionsDir) (*GobusterDir, error) { +func New(globalopts *libgobuster.Options, opts *OptionsDir, logger *libgobuster.Logger) (*GobusterDir, error) { if globalopts == nil { return nil, fmt.Errorf("please provide valid global options") } @@ -86,7 +86,7 @@ func New(globalopts *libgobuster.Options, opts *OptionsDir) (*GobusterDir, error Method: opts.Method, } - h, err := libgobuster.NewHTTPClient(&httpOpts) + h, err := libgobuster.NewHTTPClient(&httpOpts, globalopts.Debug, logger) if err != nil { return nil, err } diff --git a/gobusterfuzz/gobusterfuzz.go b/gobusterfuzz/gobusterfuzz.go index 1d49cd2c..e7babaee 100644 --- a/gobusterfuzz/gobusterfuzz.go +++ b/gobusterfuzz/gobusterfuzz.go @@ -37,7 +37,7 @@ type GobusterFuzz struct { } // New creates a new initialized GobusterFuzz -func New(globalopts *libgobuster.Options, opts *OptionsFuzz) (*GobusterFuzz, error) { +func New(globalopts *libgobuster.Options, opts *OptionsFuzz, logger *libgobuster.Logger) (*GobusterFuzz, error) { if globalopts == nil { return nil, fmt.Errorf("please provide valid global options") } @@ -72,7 +72,7 @@ func New(globalopts *libgobuster.Options, opts *OptionsFuzz) (*GobusterFuzz, err Method: opts.Method, } - h, err := libgobuster.NewHTTPClient(&httpOpts) + h, err := libgobuster.NewHTTPClient(&httpOpts, globalopts.Debug, logger) if err != nil { return nil, err } @@ -99,6 +99,12 @@ func (d *GobusterFuzz) ProcessWord(ctx context.Context, word string, progress *l if len(d.options.Headers) > 0 { requestOptions.ModifiedHeaders = make([]libgobuster.HTTPHeader, len(d.options.Headers)) for i := range d.options.Headers { + // Host header can't be set via Headers, needs to be a separate field + if d.options.Headers[i].Name == "Host" { + requestOptions.Host = strings.ReplaceAll(d.options.Headers[i].Value, FuzzKeyword, word) + continue + } + requestOptions.ModifiedHeaders[i] = libgobuster.HTTPHeader{ Name: strings.ReplaceAll(d.options.Headers[i].Name, FuzzKeyword, word), Value: strings.ReplaceAll(d.options.Headers[i].Value, FuzzKeyword, word), diff --git a/gobustergcs/gobustersgcs.go b/gobustergcs/gobustersgcs.go index db8e15d7..c2d71a4d 100644 --- a/gobustergcs/gobustersgcs.go +++ b/gobustergcs/gobustersgcs.go @@ -27,7 +27,7 @@ type GobusterGCS struct { } // New creates a new initialized GobusterGCS -func New(globalopts *libgobuster.Options, opts *OptionsGCS) (*GobusterGCS, error) { +func New(globalopts *libgobuster.Options, opts *OptionsGCS, logger *libgobuster.Logger) (*GobusterGCS, error) { if globalopts == nil { return nil, fmt.Errorf("please provide valid global options") } @@ -57,7 +57,7 @@ func New(globalopts *libgobuster.Options, opts *OptionsGCS) (*GobusterGCS, error FollowRedirect: true, } - h, err := libgobuster.NewHTTPClient(&httpOpts) + h, err := libgobuster.NewHTTPClient(&httpOpts, globalopts.Debug, logger) if err != nil { return nil, err } diff --git a/gobusters3/gobusters3.go b/gobusters3/gobusters3.go index f692eb75..9cc5e12e 100644 --- a/gobusters3/gobusters3.go +++ b/gobusters3/gobusters3.go @@ -27,7 +27,7 @@ type GobusterS3 struct { } // New creates a new initialized GobusterS3 -func New(globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) { +func New(globalopts *libgobuster.Options, opts *OptionsS3, logger *libgobuster.Logger) (*GobusterS3, error) { if globalopts == nil { return nil, fmt.Errorf("please provide valid global options") } @@ -57,7 +57,7 @@ func New(globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) FollowRedirect: true, } - h, err := libgobuster.NewHTTPClient(&httpOpts) + h, err := libgobuster.NewHTTPClient(&httpOpts, globalopts.Debug, logger) if err != nil { return nil, err } diff --git a/gobustervhost/gobustervhost.go b/gobustervhost/gobustervhost.go index 09867faa..029f30bf 100644 --- a/gobustervhost/gobustervhost.go +++ b/gobustervhost/gobustervhost.go @@ -31,7 +31,7 @@ type GobusterVhost struct { } // New creates a new initialized GobusterDir -func New(globalopts *libgobuster.Options, opts *OptionsVhost) (*GobusterVhost, error) { +func New(globalopts *libgobuster.Options, opts *OptionsVhost, logger *libgobuster.Logger) (*GobusterVhost, error) { if globalopts == nil { return nil, fmt.Errorf("please provide valid global options") } @@ -66,7 +66,7 @@ func New(globalopts *libgobuster.Options, opts *OptionsVhost) (*GobusterVhost, e Method: opts.Method, } - h, err := libgobuster.NewHTTPClient(&httpOpts) + h, err := libgobuster.NewHTTPClient(&httpOpts, globalopts.Debug, logger) if err != nil { return nil, err } diff --git a/libgobuster/http.go b/libgobuster/http.go index a0696934..cf4d7c8e 100644 --- a/libgobuster/http.go +++ b/libgobuster/http.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "net/http/httputil" "net/url" "strings" ) @@ -29,6 +30,8 @@ type HTTPClient struct { cookies string method string host string + debug bool + logger *Logger } // RequestOptions is used to pass options to a single individual request @@ -42,7 +45,7 @@ type RequestOptions struct { } // NewHTTPClient returns a new HTTPClient -func NewHTTPClient(opt *HTTPOptions) (*HTTPClient, error) { +func NewHTTPClient(opt *HTTPOptions, debug bool, logger *Logger) (*HTTPClient, error) { var proxyURLFunc func(*http.Request) (*url.URL, error) var client HTTPClient proxyURLFunc = http.ProxyFromEnvironment @@ -104,6 +107,8 @@ func NewHTTPClient(opt *HTTPOptions) (*HTTPClient, error) { break } } + client.debug = debug + client.logger = logger return &client, nil } @@ -171,6 +176,11 @@ func (client *HTTPClient) makeRequest(ctx context.Context, fullURL string, opts // currently only relevant on fuzzing if len(opts.ModifiedHeaders) > 0 { for _, h := range opts.ModifiedHeaders { + // empty headers are not valid + if h.Name == "" { + continue + } + if client.noCanonicalizeHeaders { // https://stackoverflow.com/questions/26351716/how-to-keep-key-case-sensitive-in-request-header-using-golang req.Header[h.Name] = []string{h.Value} @@ -195,6 +205,14 @@ func (client *HTTPClient) makeRequest(ctx context.Context, fullURL string, opts req.SetBasicAuth(client.username, client.password) } + if client.debug { + dump, err := httputil.DumpRequestOut(req, false) + if err != nil { + return nil, err + } + client.logger.Debugf("%s", dump) + } + resp, err := client.client.Do(req) if err != nil { var ue *url.Error diff --git a/libgobuster/http_test.go b/libgobuster/http_test.go index 3cc3612e..245f07c3 100644 --- a/libgobuster/http_test.go +++ b/libgobuster/http_test.go @@ -51,7 +51,8 @@ func TestRequest(t *testing.T) { h := httpServerT(t, ret) defer h.Close() var o HTTPOptions - c, err := NewHTTPClient(&o) + log := NewLogger(false) + c, err := NewHTTPClient(&o, false, log) if err != nil { t.Fatalf("Got Error: %v", err) } @@ -78,7 +79,8 @@ func BenchmarkRequestWithoutBody(b *testing.B) { h := httpServerB(b, r) defer h.Close() var o HTTPOptions - c, err := NewHTTPClient(&o) + log := NewLogger(false) + c, err := NewHTTPClient(&o, false, log) if err != nil { b.Fatalf("Got Error: %v", err) } @@ -98,7 +100,8 @@ func BenchmarkRequestWitBody(b *testing.B) { h := httpServerB(b, r) defer h.Close() var o HTTPOptions - c, err := NewHTTPClient(&o) + log := NewLogger(false) + c, err := NewHTTPClient(&o, false, log) if err != nil { b.Fatalf("Got Error: %v", err) } @@ -118,8 +121,9 @@ func BenchmarkNewHTTPClient(b *testing.B) { h := httpServerB(b, r) defer h.Close() var o HTTPOptions + log := NewLogger(false) for x := 0; x < b.N; x++ { - _, err := NewHTTPClient(&o) + _, err := NewHTTPClient(&o, false, log) if err != nil { b.Fatalf("Got Error: %v", err) } diff --git a/libgobuster/libgobuster.go b/libgobuster/libgobuster.go index b579585d..d6c93179 100644 --- a/libgobuster/libgobuster.go +++ b/libgobuster/libgobuster.go @@ -25,13 +25,13 @@ type ResultToStringFunc func(*Gobuster, *Result) (*string, error) // Gobuster is the main object when creating a new run type Gobuster struct { Opts *Options - Logger Logger + Logger *Logger plugin GobusterPlugin Progress *Progress } // NewGobuster returns a new Gobuster object -func NewGobuster(opts *Options, plugin GobusterPlugin, logger Logger) (*Gobuster, error) { +func NewGobuster(opts *Options, plugin GobusterPlugin, logger *Logger) (*Gobuster, error) { var g Gobuster g.Opts = opts g.plugin = plugin diff --git a/libgobuster/logger.go b/libgobuster/logger.go index 28647147..6990a457 100644 --- a/libgobuster/logger.go +++ b/libgobuster/logger.go @@ -16,8 +16,8 @@ type Logger struct { debug bool } -func NewLogger(debug bool) Logger { - return Logger{ +func NewLogger(debug bool) *Logger { + return &Logger{ log: log.New(os.Stdout, "", 0), errorLog: log.New(os.Stderr, color.New(color.FgRed).Sprint("[ERROR] "), 0), debugLog: log.New(os.Stderr, color.New(color.FgBlue).Sprint("[DEBUG] "), 0),