diff --git a/cli/cmd/dir.go b/cli/cmd/dir.go deleted file mode 100644 index 3dc4a3c..0000000 --- a/cli/cmd/dir.go +++ /dev/null @@ -1,188 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "log" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobusterdir" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdDir *cobra.Command - -func runDir(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseDirOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobusterdir.NewGobusterDir(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobusterdir: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - var wErr *gobusterdir.ErrWildcard - if errors.As(err, &wErr) { - return fmt.Errorf("%w. To continue please exclude the status code or the length", wErr) - } - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseDirOptions() (*libgobuster.Options, *gobusterdir.OptionsDir, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - - pluginOpts := gobusterdir.NewOptionsDir() - - httpOpts, err := parseCommonHTTPOptions(cmdDir) - if err != nil { - return nil, nil, err - } - pluginOpts.Password = httpOpts.Password - pluginOpts.URL = httpOpts.URL - pluginOpts.UserAgent = httpOpts.UserAgent - pluginOpts.Username = httpOpts.Username - pluginOpts.Proxy = httpOpts.Proxy - pluginOpts.Cookies = httpOpts.Cookies - pluginOpts.Timeout = httpOpts.Timeout - pluginOpts.FollowRedirect = httpOpts.FollowRedirect - pluginOpts.NoTLSValidation = httpOpts.NoTLSValidation - pluginOpts.Headers = httpOpts.Headers - pluginOpts.Method = httpOpts.Method - pluginOpts.RetryOnTimeout = httpOpts.RetryOnTimeout - pluginOpts.RetryAttempts = httpOpts.RetryAttempts - pluginOpts.TLSCertificate = httpOpts.TLSCertificate - pluginOpts.NoCanonicalizeHeaders = httpOpts.NoCanonicalizeHeaders - - pluginOpts.Extensions, err = cmdDir.Flags().GetString("extensions") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for extensions: %w", err) - } - - ret, err := libgobuster.ParseExtensions(pluginOpts.Extensions) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for extensions: %w", err) - } - pluginOpts.ExtensionsParsed = ret - - pluginOpts.ExtensionsFile, err = cmdDir.Flags().GetString("extensions-file") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for extensions file: %w", err) - } - - if pluginOpts.ExtensionsFile != "" { - extensions, err := libgobuster.ParseExtensionsFile(pluginOpts.ExtensionsFile) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for extensions file: %w", err) - } - pluginOpts.ExtensionsParsed.AddRange(extensions) - } - - // parse normal status codes - pluginOpts.StatusCodes, err = cmdDir.Flags().GetString("status-codes") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err) - } - ret2, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.StatusCodes) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err) - } - pluginOpts.StatusCodesParsed = ret2 - - // blacklist will override the normal status codes - pluginOpts.StatusCodesBlacklist, err = cmdDir.Flags().GetString("status-codes-blacklist") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for status-codes-blacklist: %w", err) - } - ret3, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.StatusCodesBlacklist) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for status-codes-blacklist: %w", err) - } - pluginOpts.StatusCodesBlacklistParsed = ret3 - - if pluginOpts.StatusCodes != "" && pluginOpts.StatusCodesBlacklist != "" { - return nil, nil, fmt.Errorf("status-codes (%q) and status-codes-blacklist (%q) are both set - please set only one. status-codes-blacklist is set by default so you might want to disable it by supplying an empty string.", - pluginOpts.StatusCodes, pluginOpts.StatusCodesBlacklist) - } - - if pluginOpts.StatusCodes == "" && pluginOpts.StatusCodesBlacklist == "" { - return nil, nil, fmt.Errorf("status-codes and status-codes-blacklist are both not set, please set one") - } - - pluginOpts.UseSlash, err = cmdDir.Flags().GetBool("add-slash") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for add-slash: %w", err) - } - - pluginOpts.Expanded, err = cmdDir.Flags().GetBool("expanded") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for expanded: %w", err) - } - - pluginOpts.NoStatus, err = cmdDir.Flags().GetBool("no-status") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for no-status: %w", err) - } - - pluginOpts.HideLength, err = cmdDir.Flags().GetBool("hide-length") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for hide-length: %w", err) - } - - pluginOpts.DiscoverBackup, err = cmdDir.Flags().GetBool("discover-backup") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for discover-backup: %w", err) - } - - pluginOpts.ExcludeLength, err = cmdDir.Flags().GetString("exclude-length") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - ret4, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.ExcludeLength) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - pluginOpts.ExcludeLengthParsed = ret4 - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdDir = &cobra.Command{ - Use: "dir", - Short: "Uses directory/file enumeration mode", - RunE: runDir, - } - - if err := addCommonHTTPOptions(cmdDir); err != nil { - log.Fatalf("%v", err) - } - cmdDir.Flags().StringP("status-codes", "s", "", "Positive status codes (will be overwritten with status-codes-blacklist if set). Can also handle ranges like 200,300-400,404.") - cmdDir.Flags().StringP("status-codes-blacklist", "b", "404", "Negative status codes (will override status-codes if set). Can also handle ranges like 200,300-400,404.") - cmdDir.Flags().StringP("extensions", "x", "", "File extension(s) to search for") - cmdDir.Flags().StringP("extensions-file", "X", "", "Read file extension(s) to search from the file") - cmdDir.Flags().BoolP("expanded", "e", false, "Expanded mode, print full URLs") - cmdDir.Flags().BoolP("no-status", "n", false, "Don't print status codes") - cmdDir.Flags().Bool("hide-length", false, "Hide the length of the body in the output") - cmdDir.Flags().BoolP("add-slash", "f", false, "Append / to each request") - cmdDir.Flags().BoolP("discover-backup", "d", false, "Also search for backup files by appending multiple backup extensions") - cmdDir.Flags().String("exclude-length", "", "exclude the following content lengths (completely ignores the status). You can separate multiple lengths by comma and it also supports ranges like 203-206") - - cmdDir.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdDir) -} diff --git a/cli/cmd/dir_test.go b/cli/cmd/dir_test.go deleted file mode 100644 index 7a0f41b..0000000 --- a/cli/cmd/dir_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "os" - "testing" - "time" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobusterdir" - "git.sual.in/casual/gobuster-lib/libgobuster" -) - -func httpServer(b *testing.B, content string) *httptest.Server { - b.Helper() - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, content) - })) - return ts -} -func BenchmarkDirMode(b *testing.B) { - h := httpServer(b, "test") - defer h.Close() - - pluginopts := gobusterdir.NewOptionsDir() - pluginopts.URL = h.URL - pluginopts.Timeout = 10 * time.Second - - pluginopts.Extensions = ".php,.csv" - tmpExt, err := libgobuster.ParseExtensions(pluginopts.Extensions) - if err != nil { - b.Fatalf("could not parse extensions: %v", err) - } - pluginopts.ExtensionsParsed = tmpExt - - pluginopts.StatusCodes = "200,204,301,302,307,401,403" - tmpStat, err := libgobuster.ParseCommaSeparatedInt(pluginopts.StatusCodes) - if err != nil { - b.Fatalf("could not parse status codes: %v", err) - } - pluginopts.StatusCodesParsed = tmpStat - - wordlist, err := os.CreateTemp("", "") - if err != nil { - b.Fatalf("could not create tempfile: %v", err) - } - defer os.Remove(wordlist.Name()) - for w := 0; w < 1000; w++ { - _, _ = wordlist.WriteString(fmt.Sprintf("%d\n", w)) - } - wordlist.Close() - - globalopts := libgobuster.Options{ - Threads: 10, - Wordlist: wordlist.Name(), - NoProgress: true, - } - - ctx := context.Background() - oldStdout := os.Stdout - oldStderr := os.Stderr - defer func(out, err *os.File) { os.Stdout = out; os.Stderr = err }(oldStdout, oldStderr) - devnull, err := os.Open(os.DevNull) - if err != nil { - b.Fatalf("could not get devnull %v", err) - } - defer devnull.Close() - log := libgobuster.NewLogger(false) - - // Run the real benchmark - for x := 0; x < b.N; x++ { - os.Stdout = devnull - os.Stderr = devnull - plugin, err := gobusterdir.NewGobusterDir(&globalopts, pluginopts) - if err != nil { - b.Fatalf("error on creating gobusterdir: %v", err) - } - - if err := cli.Gobuster(ctx, &globalopts, plugin, log); err != nil { - b.Fatalf("error on running gobuster: %v", err) - } - os.Stdout = oldStdout - os.Stderr = oldStderr - } -} diff --git a/cli/cmd/dns.go b/cli/cmd/dns.go deleted file mode 100644 index d1579c1..0000000 --- a/cli/cmd/dns.go +++ /dev/null @@ -1,115 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "log" - "runtime" - "time" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobusterdns" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdDNS *cobra.Command - -func runDNS(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseDNSOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobusterdns.NewGobusterDNS(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobusterdns: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - var wErr *gobusterdns.ErrWildcard - if errors.As(err, &wErr) { - return fmt.Errorf("%w. To force processing of Wildcard DNS, specify the '--wildcard' switch", wErr) - } - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseDNSOptions() (*libgobuster.Options, *gobusterdns.OptionsDNS, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - pluginOpts := gobusterdns.NewOptionsDNS() - - pluginOpts.Domain, err = cmdDNS.Flags().GetString("domain") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for domain: %w", err) - } - - pluginOpts.ShowIPs, err = cmdDNS.Flags().GetBool("show-ips") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for show-ips: %w", err) - } - - pluginOpts.ShowCNAME, err = cmdDNS.Flags().GetBool("show-cname") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for show-cname: %w", err) - } - - pluginOpts.WildcardForced, err = cmdDNS.Flags().GetBool("wildcard") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for wildcard: %w", err) - } - - pluginOpts.Timeout, err = cmdDNS.Flags().GetDuration("timeout") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for timeout: %w", err) - } - - pluginOpts.Resolver, err = cmdDNS.Flags().GetString("resolver") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for resolver: %w", err) - } - - pluginOpts.NoFQDN, err = cmdDNS.Flags().GetBool("no-fqdn") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for no-fqdn: %w", err) - } - - if pluginOpts.Resolver != "" && runtime.GOOS == "windows" { - return nil, nil, fmt.Errorf("currently can not set custom dns resolver on windows. See https://golang.org/pkg/net/#hdr-Name_Resolution") - } - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdDNS = &cobra.Command{ - Use: "dns", - Short: "Uses DNS subdomain enumeration mode", - RunE: runDNS, - } - - cmdDNS.Flags().StringP("domain", "d", "", "The target domain") - cmdDNS.Flags().BoolP("show-ips", "i", false, "Show IP addresses") - cmdDNS.Flags().BoolP("show-cname", "c", false, "Show CNAME records (cannot be used with '-i' option)") - cmdDNS.Flags().DurationP("timeout", "", time.Second, "DNS resolver timeout") - cmdDNS.Flags().BoolP("wildcard", "", false, "Force continued operation when wildcard found") - cmdDNS.Flags().BoolP("no-fqdn", "", false, "Do not automatically add a trailing dot to the domain, so the resolver uses the DNS search domain") - cmdDNS.Flags().StringP("resolver", "r", "", "Use custom DNS server (format server.com or server.com:port)") - if err := cmdDNS.MarkFlagRequired("domain"); err != nil { - log.Fatalf("error on marking flag as required: %v", err) - } - - cmdDNS.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdDNS) -} diff --git a/cli/cmd/fuzz.go b/cli/cmd/fuzz.go deleted file mode 100644 index 0e9d1d2..0000000 --- a/cli/cmd/fuzz.go +++ /dev/null @@ -1,148 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - "log" - "strings" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobusterfuzz" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdFuzz *cobra.Command - -func runFuzz(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseFuzzOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - if !containsFuzzKeyword(*pluginopts) { - return fmt.Errorf("please provide the %s keyword", gobusterfuzz.FuzzKeyword) - } - - plugin, err := gobusterfuzz.NewGobusterFuzz(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobusterfuzz: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - var wErr *gobusterfuzz.ErrWildcard - if errors.As(err, &wErr) { - return fmt.Errorf("%w. To continue please exclude the status code or the length", wErr) - } - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseFuzzOptions() (*libgobuster.Options, *gobusterfuzz.OptionsFuzz, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - - pluginOpts := gobusterfuzz.NewOptionsFuzz() - - httpOpts, err := parseCommonHTTPOptions(cmdFuzz) - if err != nil { - return nil, nil, err - } - pluginOpts.Password = httpOpts.Password - pluginOpts.URL = httpOpts.URL - pluginOpts.UserAgent = httpOpts.UserAgent - pluginOpts.Username = httpOpts.Username - pluginOpts.Proxy = httpOpts.Proxy - pluginOpts.Cookies = httpOpts.Cookies - pluginOpts.Timeout = httpOpts.Timeout - pluginOpts.FollowRedirect = httpOpts.FollowRedirect - pluginOpts.NoTLSValidation = httpOpts.NoTLSValidation - pluginOpts.Headers = httpOpts.Headers - pluginOpts.Method = httpOpts.Method - pluginOpts.RetryOnTimeout = httpOpts.RetryOnTimeout - pluginOpts.RetryAttempts = httpOpts.RetryAttempts - pluginOpts.TLSCertificate = httpOpts.TLSCertificate - pluginOpts.NoCanonicalizeHeaders = httpOpts.NoCanonicalizeHeaders - - // blacklist will override the normal status codes - pluginOpts.ExcludedStatusCodes, err = cmdFuzz.Flags().GetString("excludestatuscodes") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for excludestatuscodes: %w", err) - } - ret, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.ExcludedStatusCodes) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for excludestatuscodes: %w", err) - } - pluginOpts.ExcludedStatusCodesParsed = ret - - pluginOpts.ExcludeLength, err = cmdFuzz.Flags().GetString("exclude-length") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - ret2, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.ExcludeLength) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - pluginOpts.ExcludeLengthParsed = ret2 - - pluginOpts.RequestBody, err = cmdFuzz.Flags().GetString("body") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for body: %w", err) - } - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdFuzz = &cobra.Command{ - Use: "fuzz", - Short: fmt.Sprintf("Uses fuzzing mode. Replaces the keyword %s in the URL, Headers and the request body", gobusterfuzz.FuzzKeyword), - RunE: runFuzz, - } - - if err := addCommonHTTPOptions(cmdFuzz); err != nil { - log.Fatalf("%v", err) - } - cmdFuzz.Flags().StringP("excludestatuscodes", "b", "", "Excluded status codes. Can also handle ranges like 200,300-400,404.") - cmdFuzz.Flags().String("exclude-length", "", "exclude the following content lengths (completely ignores the status). You can separate multiple lengths by comma and it also supports ranges like 203-206") - cmdFuzz.Flags().StringP("body", "B", "", "Request body") - - cmdFuzz.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdFuzz) -} - -func containsFuzzKeyword(pluginopts gobusterfuzz.OptionsFuzz) bool { - if strings.Contains(pluginopts.URL, gobusterfuzz.FuzzKeyword) { - return true - } - - if strings.Contains(pluginopts.RequestBody, gobusterfuzz.FuzzKeyword) { - return true - } - - for _, h := range pluginopts.Headers { - if strings.Contains(h.Name, gobusterfuzz.FuzzKeyword) || strings.Contains(h.Value, gobusterfuzz.FuzzKeyword) { - return true - } - } - - if strings.Contains(pluginopts.Username, gobusterfuzz.FuzzKeyword) { - return true - } - - if strings.Contains(pluginopts.Password, gobusterfuzz.FuzzKeyword) { - return true - } - - return false -} diff --git a/cli/cmd/gcs.go b/cli/cmd/gcs.go deleted file mode 100644 index 43f846e..0000000 --- a/cli/cmd/gcs.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd - -import ( - "fmt" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobustergcs" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdGCS *cobra.Command - -func runGCS(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseGCSOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobustergcs.NewGobusterGCS(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobustergcs: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseGCSOptions() (*libgobuster.Options, *gobustergcs.OptionsGCS, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - - pluginopts := gobustergcs.NewOptionsGCS() - - httpOpts, err := parseBasicHTTPOptions(cmdGCS) - if err != nil { - return nil, nil, err - } - - pluginopts.UserAgent = httpOpts.UserAgent - pluginopts.Proxy = httpOpts.Proxy - pluginopts.Timeout = httpOpts.Timeout - pluginopts.NoTLSValidation = httpOpts.NoTLSValidation - pluginopts.RetryOnTimeout = httpOpts.RetryOnTimeout - pluginopts.RetryAttempts = httpOpts.RetryAttempts - pluginopts.TLSCertificate = httpOpts.TLSCertificate - - pluginopts.MaxFilesToList, err = cmdGCS.Flags().GetInt("maxfiles") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for maxfiles: %w", err) - } - - return globalopts, pluginopts, nil -} - -// nolint:gochecknoinits -func init() { - cmdGCS = &cobra.Command{ - Use: "gcs", - Short: "Uses gcs bucket enumeration mode", - RunE: runGCS, - } - - addBasicHTTPOptions(cmdGCS) - cmdGCS.Flags().IntP("maxfiles", "m", 5, "max files to list when listing buckets (only shown in verbose mode)") - - cmdGCS.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdGCS) -} diff --git a/cli/cmd/http.go b/cli/cmd/http.go deleted file mode 100644 index ddfe966..0000000 --- a/cli/cmd/http.go +++ /dev/null @@ -1,258 +0,0 @@ -package cmd - -import ( - "crypto/tls" - "encoding/pem" - "fmt" - "os" - "regexp" - "strconv" - "strings" - "syscall" - "time" - - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" - "golang.org/x/crypto/pkcs12" - "golang.org/x/term" -) - -func addBasicHTTPOptions(cmd *cobra.Command) { - cmd.Flags().StringP("useragent", "a", libgobuster.DefaultUserAgent(), "Set the User-Agent string") - cmd.Flags().BoolP("random-agent", "", false, "Use a random User-Agent string") - cmd.Flags().StringP("proxy", "", "", "Proxy to use for requests [http(s)://host:port] or [socks5://host:port]") - cmd.Flags().DurationP("timeout", "", 10*time.Second, "HTTP Timeout") - cmd.Flags().BoolP("no-tls-validation", "k", false, "Skip TLS certificate verification") - cmd.Flags().BoolP("retry", "", false, "Should retry on request timeout") - cmd.Flags().IntP("retry-attempts", "", 3, "Times to retry on request timeout") - // client certificates, either pem or p12 - cmd.Flags().StringP("client-cert-pem", "", "", "public key in PEM format for optional TLS client certificates") - cmd.Flags().StringP("client-cert-pem-key", "", "", "private key in PEM format for optional TLS client certificates (this key needs to have no password)") - cmd.Flags().StringP("client-cert-p12", "", "", "a p12 file to use for options TLS client certificates") - cmd.Flags().StringP("client-cert-p12-password", "", "", "the password to the p12 file") -} - -func addCommonHTTPOptions(cmd *cobra.Command) error { - addBasicHTTPOptions(cmd) - cmd.Flags().StringP("url", "u", "", "The target URL") - cmd.Flags().StringP("cookies", "c", "", "Cookies to use for the requests") - cmd.Flags().StringP("username", "U", "", "Username for Basic Auth") - cmd.Flags().StringP("password", "P", "", "Password for Basic Auth") - cmd.Flags().BoolP("follow-redirect", "r", false, "Follow redirects") - cmd.Flags().StringArrayP("headers", "H", []string{""}, "Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'") - cmd.Flags().BoolP("no-canonicalize-headers", "", false, "Do not canonicalize HTTP header names. If set header names are sent as is.") - cmd.Flags().StringP("method", "m", "GET", "Use the following HTTP method") - - if err := cmd.MarkFlagRequired("url"); err != nil { - return fmt.Errorf("error on marking flag as required: %w", err) - } - - return nil -} - -func parseBasicHTTPOptions(cmd *cobra.Command) (libgobuster.BasicHTTPOptions, error) { - options := libgobuster.BasicHTTPOptions{} - var err error - - options.UserAgent, err = cmd.Flags().GetString("useragent") - if err != nil { - return options, fmt.Errorf("invalid value for useragent: %w", err) - } - randomUA, err := cmd.Flags().GetBool("random-agent") - if err != nil { - return options, fmt.Errorf("invalid value for random-agent: %w", err) - } - if randomUA { - ua, err := libgobuster.GetRandomUserAgent() - if err != nil { - return options, err - } - options.UserAgent = ua - } - - options.Proxy, err = cmd.Flags().GetString("proxy") - if err != nil { - return options, fmt.Errorf("invalid value for proxy: %w", err) - } - - options.Timeout, err = cmd.Flags().GetDuration("timeout") - if err != nil { - return options, fmt.Errorf("invalid value for timeout: %w", err) - } - - options.RetryOnTimeout, err = cmd.Flags().GetBool("retry") - if err != nil { - return options, fmt.Errorf("invalid value for retry: %w", err) - } - - options.RetryAttempts, err = cmd.Flags().GetInt("retry-attempts") - if err != nil { - return options, fmt.Errorf("invalid value for retry-attempts: %w", err) - } - - options.NoTLSValidation, err = cmd.Flags().GetBool("no-tls-validation") - if err != nil { - return options, fmt.Errorf("invalid value for no-tls-validation: %w", err) - } - - pemFile, err := cmd.Flags().GetString("client-cert-pem") - if err != nil { - return options, fmt.Errorf("invalid value for client-cert-pem: %w", err) - } - pemKeyFile, err := cmd.Flags().GetString("client-cert-pem-key") - if err != nil { - return options, fmt.Errorf("invalid value for client-cert-pem-key: %w", err) - } - p12File, err := cmd.Flags().GetString("client-cert-p12") - if err != nil { - return options, fmt.Errorf("invalid value for client-cert-p12: %w", err) - } - p12Pass, err := cmd.Flags().GetString("client-cert-p12-password") - if err != nil { - return options, fmt.Errorf("invalid value for client-cert-p12-password: %w", err) - } - - if pemFile != "" && p12File != "" { - return options, fmt.Errorf("please supply either a pem or a p12, not both") - } - - if pemFile != "" { - cert, err := tls.LoadX509KeyPair(pemFile, pemKeyFile) - if err != nil { - return options, fmt.Errorf("could not load supplied pem key: %w", err) - } - options.TLSCertificate = &cert - } else if p12File != "" { - p12Content, err := os.ReadFile(p12File) - if err != nil { - return options, fmt.Errorf("could not read p12 %s: %w", p12File, err) - } - blocks, err := pkcs12.ToPEM(p12Content, p12Pass) - if err != nil { - return options, fmt.Errorf("could not load P12: %w", err) - } - var pemData []byte - for _, b := range blocks { - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - cert, err := tls.X509KeyPair(pemData, pemData) - if err != nil { - return options, fmt.Errorf("could not load certificate from P12: %w", err) - } - options.TLSCertificate = &cert - } - - return options, nil -} - -func parseCommonHTTPOptions(cmd *cobra.Command) (libgobuster.HTTPOptions, error) { - options := libgobuster.HTTPOptions{} - var err error - - basic, err := parseBasicHTTPOptions(cmd) - if err != nil { - return options, err - } - options.Proxy = basic.Proxy - options.Timeout = basic.Timeout - options.UserAgent = basic.UserAgent - options.NoTLSValidation = basic.NoTLSValidation - options.RetryOnTimeout = basic.RetryOnTimeout - options.RetryAttempts = basic.RetryAttempts - options.TLSCertificate = basic.TLSCertificate - - options.URL, err = cmd.Flags().GetString("url") - if err != nil { - return options, fmt.Errorf("invalid value for url: %w", err) - } - - if !strings.HasPrefix(options.URL, "http") { - // check to see if a port was specified - re := regexp.MustCompile(`^[^/]+:(\d+)`) - match := re.FindStringSubmatch(options.URL) - - if len(match) < 2 { - // no port, default to http on 80 - options.URL = fmt.Sprintf("http://%s", options.URL) - } else { - port, err2 := strconv.Atoi(match[1]) - if err2 != nil || (port != 80 && port != 443) { - return options, fmt.Errorf("url scheme not specified") - } else if port == 80 { - options.URL = fmt.Sprintf("http://%s", options.URL) - } else { - options.URL = fmt.Sprintf("https://%s", options.URL) - } - } - } - - options.Cookies, err = cmd.Flags().GetString("cookies") - if err != nil { - return options, fmt.Errorf("invalid value for cookies: %w", err) - } - - options.Username, err = cmd.Flags().GetString("username") - if err != nil { - return options, fmt.Errorf("invalid value for username: %w", err) - } - - options.Password, err = cmd.Flags().GetString("password") - if err != nil { - return options, fmt.Errorf("invalid value for password: %w", err) - } - - options.FollowRedirect, err = cmd.Flags().GetBool("follow-redirect") - if err != nil { - return options, fmt.Errorf("invalid value for follow-redirect: %w", err) - } - - options.Method, err = cmd.Flags().GetString("method") - if err != nil { - return options, fmt.Errorf("invalid value for method: %w", err) - } - - headers, err := cmd.Flags().GetStringArray("headers") - if err != nil { - return options, fmt.Errorf("invalid value for headers: %w", 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) - } - - noCanonHeaders, err := cmd.Flags().GetBool("no-canonicalize-headers") - if err != nil { - return options, fmt.Errorf("invalid value for no-canonicalize-headers: %w", err) - } - options.NoCanonicalizeHeaders = noCanonHeaders - - // Prompt for PW if not provided - if options.Username != "" && options.Password == "" { - fmt.Printf("[?] Auth Password: ") - // please don't remove the int cast here as it is sadly needed on windows :/ - passBytes, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert - // print a newline to simulate the newline that was entered - // this means that formatting/printing after doesn't look bad. - fmt.Println("") - if err != nil { - return options, fmt.Errorf("username given but reading of password failed") - } - options.Password = string(passBytes) - } - // if it's still empty bail out - if options.Username != "" && options.Password == "" { - return options, fmt.Errorf("username was provided but password is missing") - } - - return options, nil -} diff --git a/cli/cmd/root.go b/cli/cmd/root.go deleted file mode 100644 index 1534fae..0000000 --- a/cli/cmd/root.go +++ /dev/null @@ -1,194 +0,0 @@ -package cmd - -import ( - "bufio" - "context" - "fmt" - "log" - "os" - "os/signal" - - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var rootCmd = &cobra.Command{ - Use: "gobuster", - SilenceUsage: true, -} - -// nolint:gochecknoglobals -var mainContext context.Context - -// Execute is the main cobra method -func Execute() { - var cancel context.CancelFunc - mainContext, cancel = context.WithCancel(context.Background()) - defer cancel() - - signalChan := make(chan os.Signal, 1) - signal.Notify(signalChan, os.Interrupt) - defer func() { - signal.Stop(signalChan) - cancel() - }() - go func() { - select { - case <-signalChan: - // caught CTRL+C - fmt.Println("\n[!] Keyboard interrupt detected, terminating.") - cancel() - case <-mainContext.Done(): - } - }() - - if err := rootCmd.Execute(); err != nil { - // Leaving this in results in the same error appearing twice - // 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) - os.Exit(1) - } -} - -func parseGlobalOptions() (*libgobuster.Options, error) { - globalopts := libgobuster.NewOptions() - - threads, err := rootCmd.Flags().GetInt("threads") - if err != nil { - return nil, fmt.Errorf("invalid value for threads: %w", err) - } - - if threads <= 0 { - return nil, fmt.Errorf("threads must be bigger than 0") - } - globalopts.Threads = threads - - delay, err := rootCmd.Flags().GetDuration("delay") - if err != nil { - return nil, fmt.Errorf("invalid value for delay: %w", err) - } - - if delay < 0 { - return nil, fmt.Errorf("delay must be positive") - } - globalopts.Delay = delay - - globalopts.Wordlist, err = rootCmd.Flags().GetString("wordlist") - if err != nil { - return nil, fmt.Errorf("invalid value for wordlist: %w", err) - } - - if globalopts.Wordlist == "-" { - // STDIN - } else if _, err2 := os.Stat(globalopts.Wordlist); os.IsNotExist(err2) { - return nil, fmt.Errorf("wordlist file %q does not exist: %w", globalopts.Wordlist, err2) - } - - offset, err := rootCmd.Flags().GetInt("wordlist-offset") - if err != nil { - return nil, fmt.Errorf("invalid value for wordlist-offset: %w", err) - } - - if offset < 0 { - return nil, fmt.Errorf("wordlist-offset must be bigger or equal to 0") - } - globalopts.WordlistOffset = offset - - if globalopts.Wordlist == "-" && globalopts.WordlistOffset > 0 { - return nil, fmt.Errorf("wordlist-offset is not supported when reading from STDIN") - } - - globalopts.PatternFile, err = rootCmd.Flags().GetString("pattern") - if err != nil { - return nil, fmt.Errorf("invalid value for pattern: %w", err) - } - - if globalopts.PatternFile != "" { - if _, err = os.Stat(globalopts.PatternFile); os.IsNotExist(err) { - return nil, fmt.Errorf("pattern file %q does not exist: %w", globalopts.PatternFile, err) - } - patternFile, err := os.Open(globalopts.PatternFile) - if err != nil { - return nil, fmt.Errorf("could not open pattern file %q: %w", globalopts.PatternFile, err) - } - defer patternFile.Close() - - scanner := bufio.NewScanner(patternFile) - for scanner.Scan() { - globalopts.Patterns = append(globalopts.Patterns, scanner.Text()) - } - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("could not read pattern file %q: %w", globalopts.PatternFile, err) - } - } - - globalopts.OutputFilename, err = rootCmd.Flags().GetString("output") - if err != nil { - return nil, fmt.Errorf("invalid value for output filename: %w", err) - } - - globalopts.Verbose, err = rootCmd.Flags().GetBool("verbose") - if err != nil { - return nil, fmt.Errorf("invalid value for verbose: %w", err) - } - - globalopts.Quiet, err = rootCmd.Flags().GetBool("quiet") - if err != nil { - return nil, fmt.Errorf("invalid value for quiet: %w", err) - } - - globalopts.NoProgress, err = rootCmd.Flags().GetBool("no-progress") - if err != nil { - return nil, fmt.Errorf("invalid value for no-progress: %w", err) - } - - globalopts.NoError, err = rootCmd.Flags().GetBool("no-error") - if err != nil { - return nil, fmt.Errorf("invalid value for no-error: %w", err) - } - - noColor, err := rootCmd.Flags().GetBool("no-color") - if err != nil { - return nil, fmt.Errorf("invalid value for no-color: %w", err) - } - if noColor { - color.NoColor = true - } - - globalopts.Debug, err = rootCmd.Flags().GetBool("debug") - if err != nil { - return nil, fmt.Errorf("invalid value for debug: %w", err) - } - - return globalopts, nil -} - -// This has to be called as part of the pre-run for sub commands. Including -// this in the init() function results in the built-in `help` command not -// working as intended. The required flags should only be marked as required -// on the global flags when one of the non-help commands is used. -func configureGlobalOptions() { - if err := rootCmd.MarkPersistentFlagRequired("wordlist"); err != nil { - log.Fatalf("error on marking flag as required: %v", err) - } -} - -// nolint:gochecknoinits -func init() { - rootCmd.PersistentFlags().DurationP("delay", "", 0, "Time each thread waits between requests (e.g. 1500ms)") - rootCmd.PersistentFlags().IntP("threads", "t", 10, "Number of concurrent threads") - rootCmd.PersistentFlags().StringP("wordlist", "w", "", "Path to the wordlist. Set to - to use STDIN.") - rootCmd.PersistentFlags().IntP("wordlist-offset", "", 0, "Resume from a given position in the wordlist (defaults to 0)") - rootCmd.PersistentFlags().StringP("output", "o", "", "Output file to write results to (defaults to stdout)") - rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Verbose output (errors)") - rootCmd.PersistentFlags().BoolP("quiet", "q", false, "Don't print the banner and other noise") - rootCmd.PersistentFlags().BoolP("no-progress", "z", false, "Don't display progress") - rootCmd.PersistentFlags().Bool("no-error", false, "Don't display errors") - rootCmd.PersistentFlags().StringP("pattern", "p", "", "File containing replacement patterns") - rootCmd.PersistentFlags().Bool("no-color", false, "Disable color output") - rootCmd.PersistentFlags().Bool("debug", false, "Enable debug output") -} diff --git a/cli/cmd/s3.go b/cli/cmd/s3.go deleted file mode 100644 index a2935de..0000000 --- a/cli/cmd/s3.go +++ /dev/null @@ -1,79 +0,0 @@ -package cmd - -import ( - "fmt" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobusters3" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdS3 *cobra.Command - -func runS3(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseS3Options() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobusters3.NewGobusterS3(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobusters3: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseS3Options() (*libgobuster.Options, *gobusters3.OptionsS3, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - - pluginOpts := gobusters3.NewOptionsS3() - - httpOpts, err := parseBasicHTTPOptions(cmdS3) - if err != nil { - return nil, nil, err - } - - pluginOpts.UserAgent = httpOpts.UserAgent - pluginOpts.Proxy = httpOpts.Proxy - pluginOpts.Timeout = httpOpts.Timeout - pluginOpts.NoTLSValidation = httpOpts.NoTLSValidation - pluginOpts.RetryOnTimeout = httpOpts.RetryOnTimeout - pluginOpts.RetryAttempts = httpOpts.RetryAttempts - pluginOpts.TLSCertificate = httpOpts.TLSCertificate - - pluginOpts.MaxFilesToList, err = cmdS3.Flags().GetInt("maxfiles") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for maxfiles: %w", err) - } - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdS3 = &cobra.Command{ - Use: "s3", - Short: "Uses aws bucket enumeration mode", - RunE: runS3, - } - - addBasicHTTPOptions(cmdS3) - cmdS3.Flags().IntP("maxfiles", "m", 5, "max files to list when listing buckets (only shown in verbose mode)") - - cmdS3.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdS3) -} diff --git a/cli/cmd/tftp.go b/cli/cmd/tftp.go deleted file mode 100644 index cb8f52e..0000000 --- a/cli/cmd/tftp.go +++ /dev/null @@ -1,80 +0,0 @@ -package cmd - -import ( - "fmt" - "log" - "strings" - "time" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobustertftp" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdTFTP *cobra.Command - -func runTFTP(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseTFTPOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobustertftp.NewGobusterTFTP(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobustertftp: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseTFTPOptions() (*libgobuster.Options, *gobustertftp.OptionsTFTP, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - pluginOpts := gobustertftp.NewOptionsTFTP() - - pluginOpts.Server, err = cmdTFTP.Flags().GetString("server") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for domain: %w", err) - } - - if !strings.Contains(pluginOpts.Server, ":") { - pluginOpts.Server = fmt.Sprintf("%s:69", pluginOpts.Server) - } - - pluginOpts.Timeout, err = cmdTFTP.Flags().GetDuration("timeout") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for timeout: %w", err) - } - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdTFTP = &cobra.Command{ - Use: "tftp", - Short: "Uses TFTP enumeration mode", - RunE: runTFTP, - } - - cmdTFTP.Flags().StringP("server", "s", "", "The target TFTP server") - cmdTFTP.Flags().DurationP("timeout", "", time.Second, "TFTP timeout") - if err := cmdTFTP.MarkFlagRequired("server"); err != nil { - log.Fatalf("error on marking flag as required: %v", err) - } - - cmdTFTP.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdTFTP) -} diff --git a/cli/cmd/version.go b/cli/cmd/version.go deleted file mode 100644 index 8d55edf..0000000 --- a/cli/cmd/version.go +++ /dev/null @@ -1,27 +0,0 @@ -package cmd - -import ( - "fmt" - - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdVersion *cobra.Command - -func runVersion(cmd *cobra.Command, args []string) error { - fmt.Println(libgobuster.VERSION) - return nil -} - -// nolint:gochecknoinits -func init() { - cmdVersion = &cobra.Command{ - Use: "version", - Short: "shows the current version", - RunE: runVersion, - } - - rootCmd.AddCommand(cmdVersion) -} diff --git a/cli/cmd/vhost.go b/cli/cmd/vhost.go deleted file mode 100644 index 1ea6dd6..0000000 --- a/cli/cmd/vhost.go +++ /dev/null @@ -1,105 +0,0 @@ -package cmd - -import ( - "fmt" - "log" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobustervhost" - "git.sual.in/casual/gobuster-lib/libgobuster" - "github.com/spf13/cobra" -) - -// nolint:gochecknoglobals -var cmdVhost *cobra.Command - -func runVhost(cmd *cobra.Command, args []string) error { - globalopts, pluginopts, err := parseVhostOptions() - if err != nil { - return fmt.Errorf("error on parsing arguments: %w", err) - } - - plugin, err := gobustervhost.NewGobusterVhost(globalopts, pluginopts) - if err != nil { - return fmt.Errorf("error on creating gobustervhost: %w", err) - } - - log := libgobuster.NewLogger(globalopts.Debug) - if _,err := cli.Gobuster(mainContext, globalopts, plugin, log); err != nil { - log.Debugf("%#v", err) - return fmt.Errorf("error on running gobuster: %w", err) - } - return nil -} - -func parseVhostOptions() (*libgobuster.Options, *gobustervhost.OptionsVhost, error) { - globalopts, err := parseGlobalOptions() - if err != nil { - return nil, nil, err - } - - pluginOpts := gobustervhost.NewOptionsVhost() - - httpOpts, err := parseCommonHTTPOptions(cmdVhost) - if err != nil { - return nil, nil, err - } - pluginOpts.Password = httpOpts.Password - pluginOpts.URL = httpOpts.URL - pluginOpts.UserAgent = httpOpts.UserAgent - pluginOpts.Username = httpOpts.Username - pluginOpts.Proxy = httpOpts.Proxy - pluginOpts.Cookies = httpOpts.Cookies - pluginOpts.Timeout = httpOpts.Timeout - pluginOpts.FollowRedirect = httpOpts.FollowRedirect - pluginOpts.NoTLSValidation = httpOpts.NoTLSValidation - pluginOpts.Headers = httpOpts.Headers - pluginOpts.Method = httpOpts.Method - pluginOpts.RetryOnTimeout = httpOpts.RetryOnTimeout - pluginOpts.RetryAttempts = httpOpts.RetryAttempts - pluginOpts.TLSCertificate = httpOpts.TLSCertificate - pluginOpts.NoCanonicalizeHeaders = httpOpts.NoCanonicalizeHeaders - - pluginOpts.AppendDomain, err = cmdVhost.Flags().GetBool("append-domain") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for append-domain: %w", err) - } - - pluginOpts.ExcludeLength, err = cmdVhost.Flags().GetString("exclude-length") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - ret, err := libgobuster.ParseCommaSeparatedInt(pluginOpts.ExcludeLength) - if err != nil { - return nil, nil, fmt.Errorf("invalid value for exclude-length: %w", err) - } - pluginOpts.ExcludeLengthParsed = ret - - pluginOpts.Domain, err = cmdVhost.Flags().GetString("domain") - if err != nil { - return nil, nil, fmt.Errorf("invalid value for domain: %w", err) - } - - return globalopts, pluginOpts, nil -} - -// nolint:gochecknoinits -func init() { - cmdVhost = &cobra.Command{ - Use: "vhost", - Short: "Uses VHOST enumeration mode (you most probably want to use the IP address as the URL parameter)", - RunE: runVhost, - } - if err := addCommonHTTPOptions(cmdVhost); err != nil { - log.Fatalf("%v", err) - } - cmdVhost.Flags().BoolP("append-domain", "", false, "Append main domain from URL to words from wordlist. Otherwise the fully qualified domains need to be specified in the wordlist.") - cmdVhost.Flags().String("exclude-length", "", "exclude the following content lengths (completely ignores the status). You can separate multiple lengths by comma and it also supports ranges like 203-206") - cmdVhost.Flags().String("domain", "", "the domain to append when using an IP address as URL. If left empty and you specify a domain based URL the hostname from the URL is extracted") - - cmdVhost.PersistentPreRun = func(cmd *cobra.Command, args []string) { - configureGlobalOptions() - } - - rootCmd.AddCommand(cmdVhost) -} diff --git a/cli/cmd/vhost_test.go b/cli/cmd/vhost_test.go deleted file mode 100644 index ebc2037..0000000 --- a/cli/cmd/vhost_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package cmd - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "git.sual.in/casual/gobuster-lib/cli" - "git.sual.in/casual/gobuster-lib/gobustervhost" - "git.sual.in/casual/gobuster-lib/libgobuster" -) - -func BenchmarkVhostMode(b *testing.B) { - h := httpServer(b, "test") - defer h.Close() - - pluginopts := gobustervhost.NewOptionsVhost() - pluginopts.URL = h.URL - pluginopts.Timeout = 10 * time.Second - - wordlist, err := os.CreateTemp("", "") - if err != nil { - b.Fatalf("could not create tempfile: %v", err) - } - defer os.Remove(wordlist.Name()) - for w := 0; w < 1000; w++ { - _, _ = wordlist.WriteString(fmt.Sprintf("%d\n", w)) - } - wordlist.Close() - - globalopts := libgobuster.Options{ - Threads: 10, - Wordlist: wordlist.Name(), - NoProgress: true, - } - - ctx := context.Background() - oldStdout := os.Stdout - oldStderr := os.Stderr - defer func(out, err *os.File) { os.Stdout = out; os.Stderr = err }(oldStdout, oldStderr) - devnull, err := os.Open(os.DevNull) - if err != nil { - b.Fatalf("could not get devnull %v", err) - } - defer devnull.Close() - log := libgobuster.NewLogger(false) - - // Run the real benchmark - for x := 0; x < b.N; x++ { - os.Stdout = devnull - os.Stderr = devnull - plugin, err := gobustervhost.NewGobusterVhost(&globalopts, pluginopts) - if err != nil { - b.Fatalf("error on creating gobusterdir: %v", err) - } - - if err := cli.Gobuster(ctx, &globalopts, plugin, log); err != nil { - b.Fatalf("error on running gobuster: %v", err) - } - os.Stdout = oldStdout - os.Stderr = oldStderr - } -} diff --git a/go.mod b/go.mod index 3dd0619..fa779d7 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module git.sual.in/casual/gobuster-lib +module git.sual.in/casual/gobusterlib go 1.22.5 @@ -7,16 +7,11 @@ require ( github.com/fatih/color v1.17.0 github.com/google/uuid v1.6.0 github.com/pin/tftp/v3 v3.1.0 - github.com/spf13/cobra v1.8.1 - golang.org/x/crypto v0.26.0 - golang.org/x/term v0.23.0 ) require ( - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.25.0 // indirect ) diff --git a/go.sum b/go.sum index 649c3c7..9a1f3d7 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,9 @@ git.sual.in/casual/gobuster-lib/libgobuster v0.0.0-20240904201007-8210f5ee7e12 h1:+CnxZE3aMK45ZGtoSNv/5J2VSL2igm0/Iytvbxcatog= git.sual.in/casual/gobuster-lib/libgobuster v0.0.0-20240904201007-8210f5ee7e12/go.mod h1:bkuQXxQgSQ+tO2Qs6PiRKRLXd8g5izddsxraLjHzrD8= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -14,14 +11,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pin/tftp/v3 v3.1.0 h1:rQaxd4pGwcAJnpId8zC+O2NX3B2/NscjDZQaqEjuE7c= github.com/pin/tftp/v3 v3.1.0/go.mod h1:xwQaN4viYL019tM4i8iecm++5cGxSqen6AJEOEyEI0w= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= @@ -30,8 +20,4 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/libgobuster/go.mod b/libgobuster/go.mod deleted file mode 100644 index c64f9bf..0000000 --- a/libgobuster/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module git.sual.in/casual/gobuster-lib/libgobuster - -go 1.22.5 - -require github.com/fatih/color v1.17.0 - -require ( - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - golang.org/x/sys v0.18.0 // indirect -) diff --git a/libgobuster/go.sum b/libgobuster/go.sum deleted file mode 100644 index 4ddf511..0000000 --- a/libgobuster/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go.bak similarity index 100% rename from main.go rename to main.go.bak