diff --git a/.golangci.yml b/.golangci.yml index 877e5a8..1690c7a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,7 +14,7 @@ issues: linters: - gocritic - - path: cli\\cmd\\.+\.go + - path: cli\cmd\.+\.go linters: - gochecknoinits - gochecknoglobals diff --git a/cli/cmd/dir.go b/cli/cmd/dir.go index 3b6aef1..0cd1a30 100644 --- a/cli/cmd/dir.go +++ b/cli/cmd/dir.go @@ -54,6 +54,7 @@ func parseDirOptions() (*libgobuster.Options, *gobusterdir.OptionsDir, error) { plugin.Timeout = httpOpts.Timeout plugin.FollowRedirect = httpOpts.FollowRedirect plugin.InsecureSSL = httpOpts.InsecureSSL + plugin.Headers = httpOpts.Headers plugin.Extensions, err = cmdDir.Flags().GetString("extensions") if err != nil { diff --git a/cli/cmd/http.go b/cli/cmd/http.go index 9001c60..0b1a021 100644 --- a/cli/cmd/http.go +++ b/cli/cmd/http.go @@ -23,6 +23,7 @@ func addCommonHTTPOptions(cmd *cobra.Command) error { cmd.Flags().DurationP("timeout", "", 10*time.Second, "HTTP Timeout") cmd.Flags().BoolP("followredirect", "r", false, "Follow redirects") cmd.Flags().BoolP("insecuressl", "k", false, "Skip SSL certificate verification") + cmd.Flags().StringArrayP("headers", "H", []string{""}, "Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'") if err := cmdDir.MarkFlagRequired("url"); err != nil { return fmt.Errorf("error on marking flag as required: %v", err) @@ -100,6 +101,25 @@ func parseCommonHTTPOptions(cmd *cobra.Command) (libgobuster.OptionsHTTP, error) return options, fmt.Errorf("invalid value for insecuressl: %v", err) } + headers, err := cmd.Flags().GetStringArray("headers") + if err != nil { + return options, fmt.Errorf("invalid value for headers: %v", err) + } + + for _, h := range headers { + keyAndValue := strings.SplitN(h, ":", 2) + if len(keyAndValue) != 2 { + return options, fmt.Errorf("invalid header format for header %q", h) + } + key := strings.TrimSpace(keyAndValue[0]) + value := strings.TrimSpace(keyAndValue[1]) + if len(key) == 0 { + return options, fmt.Errorf("invalid header format for header %q - name is empty", h) + } + header := libgobuster.HTTPHeader{Name: key, Value: value} + options.Headers = append(options.Headers, header) + } + // Prompt for PW if not provided if options.Username != "" && options.Password == "" { fmt.Printf("[?] Auth Password: ") diff --git a/cli/cmd/vhost.go b/cli/cmd/vhost.go index db96a08..c2a981d 100644 --- a/cli/cmd/vhost.go +++ b/cli/cmd/vhost.go @@ -49,6 +49,7 @@ func parseVhostOptions() (*libgobuster.Options, *gobustervhost.OptionsVhost, err plugin.Timeout = httpOpts.Timeout plugin.FollowRedirect = httpOpts.FollowRedirect plugin.InsecureSSL = httpOpts.InsecureSSL + plugin.Headers = httpOpts.Headers return globalopts, &plugin, nil } diff --git a/gobusterdir/gobusterdir.go b/gobusterdir/gobusterdir.go index 3475ddf..36b119b 100644 --- a/gobusterdir/gobusterdir.go +++ b/gobusterdir/gobusterdir.go @@ -52,6 +52,7 @@ func NewGobusterDir(cont context.Context, globalopts *libgobuster.Options, opts Username: opts.Username, Password: opts.Password, UserAgent: opts.UserAgent, + Headers: opts.Headers, } h, err := libgobuster.NewHTTPClient(cont, &httpOpts) diff --git a/gobustervhost/gobustervhost.go b/gobustervhost/gobustervhost.go index 6860018..6765961 100644 --- a/gobustervhost/gobustervhost.go +++ b/gobustervhost/gobustervhost.go @@ -46,6 +46,7 @@ func NewGobusterVhost(cont context.Context, globalopts *libgobuster.Options, opt Username: opts.Username, Password: opts.Password, UserAgent: opts.UserAgent, + Headers: opts.Headers, } h, err := libgobuster.NewHTTPClient(cont, &httpOpts) diff --git a/libgobuster/http.go b/libgobuster/http.go index 7d37826..ffeaf29 100644 --- a/libgobuster/http.go +++ b/libgobuster/http.go @@ -13,6 +13,11 @@ import ( "unicode/utf8" ) +type HTTPHeader struct { + Name string + Value string +} + // HTTPClient represents a http object type HTTPClient struct { client *http.Client @@ -21,6 +26,7 @@ type HTTPClient struct { defaultUserAgent string username string password string + headers []HTTPHeader includeLength bool } @@ -30,6 +36,7 @@ type HTTPOptions struct { Username string Password string UserAgent string + Headers []HTTPHeader Timeout time.Duration FollowRedirect bool InsecureSSL bool @@ -80,6 +87,7 @@ func NewHTTPClient(c context.Context, opt *HTTPOptions) (*HTTPClient, error) { client.includeLength = opt.IncludeLength client.userAgent = opt.UserAgent client.defaultUserAgent = DefaultUserAgent() + client.headers = opt.Headers return &client, nil } @@ -195,6 +203,11 @@ func (client *HTTPClient) makeRequest(method, fullURL, host, cookie string, data req.Header.Set("User-Agent", client.defaultUserAgent) } + // add custom headers + for _, h := range client.headers { + req.Header.Set(h.Name, h.Value) + } + if client.username != "" { req.SetBasicAuth(client.username, client.password) } diff --git a/libgobuster/options_http.go b/libgobuster/options_http.go index 747634d..a831e80 100644 --- a/libgobuster/options_http.go +++ b/libgobuster/options_http.go @@ -12,6 +12,7 @@ type OptionsHTTP struct { Username string Proxy string Cookies string + Headers []HTTPHeader Timeout time.Duration FollowRedirect bool InsecureSSL bool