diff --git a/.golangci.yml b/.golangci.yml index 652f142..450fafe 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,6 +13,7 @@ linters: - godot - goerr113 - gofumpt + - exhaustivestruct issues: exclude-rules: diff --git a/cli/cmd/dir.go b/cli/cmd/dir.go index 9086ffa..df6a4e3 100644 --- a/cli/cmd/dir.go +++ b/cli/cmd/dir.go @@ -1,6 +1,7 @@ package cmd import ( + "errors" "fmt" "log" @@ -25,8 +26,9 @@ func runDir(cmd *cobra.Command, args []string) error { } if err := cli.Gobuster(mainContext, globalopts, plugin); err != nil { - if goberr, ok := err.(*gobusterdir.ErrWildcard); ok { - return fmt.Errorf("%s. To continue please exclude the status code, the length or use the --wildcard switch", goberr.Error()) + var wErr *gobusterdir.ErrWildcard + if errors.As(err, &wErr) { + return fmt.Errorf("%w. To continue please exclude the status code, the length or use the --wildcard switch", wErr) } return fmt.Errorf("error on running gobuster: %w", err) } diff --git a/cli/cmd/http.go b/cli/cmd/http.go index 3ddce54..ba7eaea 100644 --- a/cli/cmd/http.go +++ b/cli/cmd/http.go @@ -52,7 +52,11 @@ func parseBasicHTTPOptions(cmd *cobra.Command) (libgobuster.BasicHTTPOptions, er return options, fmt.Errorf("invalid value for random-agent: %w", err) } if randomUA { - options.UserAgent = helper.GetRandomUserAgent() + ua, err := helper.GetRandomUserAgent() + if err != nil { + return options, err + } + options.UserAgent = ua } options.Proxy, err = cmd.Flags().GetString("proxy") diff --git a/cli/cmd/root.go b/cli/cmd/root.go index c613c93..1959128 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -47,7 +47,7 @@ func Execute() { // Once before and once after the help output. Not sure if // this is going to be needed to output other errors that // aren't automatically outputted. - //fmt.Println(err) + // fmt.Println(err) os.Exit(1) } } diff --git a/go.mod b/go.mod index 8c36ba2..b8c68a4 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ module github.com/OJ/gobuster/v3 require ( - github.com/google/uuid v1.1.2 + github.com/google/uuid v1.1.4 github.com/spf13/cobra v1.1.1 - golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 // indirect + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/sys v0.0.0-20210104204734-6f8348627aad // indirect + golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect ) go 1.15 diff --git a/go.sum b/go.sum index 436c285..abfda8c 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.4 h1:0ecGp3skIrHWPNGPJDaBIghfA6Sp7Ruo2Io8eLKzWm0= +github.com/google/uuid v1.1.4/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -178,8 +178,8 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -234,8 +234,14 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad h1:MCsdmFSdEd4UEa5TKS5JztCRHK/WtvNei1edOj5RSRo= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/helper/useragents.go b/helper/useragents.go index 284d629..64d51fe 100644 --- a/helper/useragents.go +++ b/helper/useragents.go @@ -1,8 +1,8 @@ package helper import ( - "math/rand" - "time" + "crypto/rand" + "math/big" ) var userAgents = [...]string{ @@ -4666,7 +4666,10 @@ var userAgents = [...]string{ } // GetRandomUserAgent picks a random user agent from a predefined list -func GetRandomUserAgent() string { - rand.Seed(time.Now().Unix()) - return userAgents[rand.Intn(len(userAgents))] +func GetRandomUserAgent() (string, error) { + n, err := rand.Int(rand.Reader, big.NewInt(int64(len(userAgents)))) + if err != nil { + return "", err + } + return userAgents[n.Int64()], err } diff --git a/libgobuster/helpers.go b/libgobuster/helpers.go index 64eba6a..d8ec090 100644 --- a/libgobuster/helpers.go +++ b/libgobuster/helpers.go @@ -2,6 +2,7 @@ package libgobuster import ( "bytes" + "errors" "fmt" "io" "sort" @@ -116,7 +117,7 @@ func lineCounter(r io.Reader) (int, error) { count += bytes.Count(buf[:c], lineSep) switch { - case err == io.EOF: + case errors.Is(err, io.EOF): return count, nil case err != nil: diff --git a/libgobuster/http.go b/libgobuster/http.go index 6b27641..d574d5c 100644 --- a/libgobuster/http.go +++ b/libgobuster/http.go @@ -29,6 +29,7 @@ type HTTPClient struct { headers []HTTPHeader cookies string method string + host string } // RequestOptions is used to pass options to a single individual request @@ -87,6 +88,13 @@ func NewHTTPClient(c context.Context, opt *HTTPOptions) (*HTTPClient, error) { if client.method == "" { client.method = http.MethodGet } + // Host header needs to be set separately + for _, h := range opt.Headers { + if h.Name == "Host" { + client.host = h.Value + break + } + } return &client, nil } @@ -136,8 +144,11 @@ func (client *HTTPClient) makeRequest(fullURL, host string, data io.Reader) (*ht req.Header.Set("Cookie", client.cookies) } + // Use host for VHOST mode on a per request basis, otherwise the one provided from headers if host != "" { req.Host = host + } else if client.host != "" { + req.Host = client.host } if client.userAgent != "" { diff --git a/libgobuster/http_test.go b/libgobuster/http_test.go index 62d588a..4a17945 100644 --- a/libgobuster/http_test.go +++ b/libgobuster/http_test.go @@ -3,8 +3,9 @@ package libgobuster import ( "bytes" "context" + "crypto/rand" "fmt" - "math/rand" + "math/big" "net/http" "net/http/httptest" "testing" @@ -26,19 +27,26 @@ func httpServerT(t *testing.T, content string) *httptest.Server { return ts } -func randomString(length int) string { +func randomString(length int) (string, error) { var letter = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") letterLen := len(letter) b := make([]byte, length) for i := range b { - b[i] = letter[rand.Intn(letterLen)] + n, err := rand.Int(rand.Reader, big.NewInt(int64(letterLen))) + if err != nil { + return "", err + } + b[i] = letter[n.Int64()] } - return string(b) + return string(b), nil } func TestRequest(t *testing.T) { - ret := randomString(100) + ret, err := randomString(100) + if err != nil { + t.Fatal(err) + } h := httpServerT(t, ret) defer h.Close() var o HTTPOptions @@ -62,7 +70,11 @@ func TestRequest(t *testing.T) { } func BenchmarkRequestWithoutBody(b *testing.B) { - h := httpServerB(b, randomString(10000)) + r, err := randomString(10000) + if err != nil { + b.Fatal(err) + } + h := httpServerB(b, r) defer h.Close() var o HTTPOptions c, err := NewHTTPClient(context.Background(), &o) @@ -78,7 +90,11 @@ func BenchmarkRequestWithoutBody(b *testing.B) { } func BenchmarkRequestWitBody(b *testing.B) { - h := httpServerB(b, randomString(10000)) + r, err := randomString(10000) + if err != nil { + b.Fatal(err) + } + h := httpServerB(b, r) defer h.Close() var o HTTPOptions c, err := NewHTTPClient(context.Background(), &o) @@ -94,7 +110,11 @@ func BenchmarkRequestWitBody(b *testing.B) { } func BenchmarkNewHTTPClient(b *testing.B) { - h := httpServerB(b, randomString(500)) + r, err := randomString(500) + if err != nil { + b.Fatal(err) + } + h := httpServerB(b, r) defer h.Close() var o HTTPOptions for x := 0; x < b.N; x++ {