1
1
Fork 0
mirror of https://github.com/OJ/gobuster.git synced 2024-05-04 22:46:07 +02:00
gobuster/cli/cmd/dir.go
Christian Mehlmauer 6a2b40ff86
Cherry-picked the gomodules code from #117
This was cherry-picked from the gomod branch instead of being merged as
a PR for two reasons:

1) The vhost plugin addition isn't yet ready for merging, as there's
   a lot of code duplication.
2) This code can technically be merged as is without the mods to the
   vhost plugin.

When/If we're ready to merge the vhost plugin we'll fix that side up.
2019-01-09 11:17:46 +10:00

226 lines
6.9 KiB
Go

package cmd
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/OJ/gobuster/v3/cli"
"github.com/OJ/gobuster/v3/gobusterdir"
"github.com/OJ/gobuster/v3/libgobuster"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)
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: %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
plugin, err := gobusterdir.NewGobusterDir(ctx, globalopts, pluginopts)
if err != nil {
return fmt.Errorf("error on creating gobusterdir: %v", err)
}
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
for range signalChan {
// caught CTRL+C
if !globalopts.Quiet {
fmt.Println("\n[!] Keyboard interrupt detected, terminating.")
}
cancel()
}
}()
if err := cli.Gobuster(ctx, globalopts, plugin); err != nil {
return fmt.Errorf("error on running goubster: %v", err)
}
return nil
}
func parseDirOptions() (*libgobuster.Options, *gobusterdir.OptionsDir, error) {
globalopts, err := parseGlobalOptions()
if err != nil {
return nil, nil, err
}
plugin := gobusterdir.NewOptionsDir()
plugin.URL, err = cmdDir.Flags().GetString("url")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for url: %v", err)
}
if !strings.HasPrefix(plugin.URL, "http") {
// check to see if a port was specified
re := regexp.MustCompile(`^[^/]+:(\d+)`)
match := re.FindStringSubmatch(plugin.URL)
if len(match) < 2 {
// no port, default to http on 80
plugin.URL = fmt.Sprintf("http://%s", plugin.URL)
} else {
port, err2 := strconv.Atoi(match[1])
if err2 != nil || (port != 80 && port != 443) {
return nil, nil, fmt.Errorf("url scheme not specified")
} else if port == 80 {
plugin.URL = fmt.Sprintf("http://%s", plugin.URL)
} else {
plugin.URL = fmt.Sprintf("https://%s", plugin.URL)
}
}
}
plugin.StatusCodes, err = cmdDir.Flags().GetString("statuscodes")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for statuscodes: %v", err)
}
if err = plugin.ParseStatusCodes(); err != nil {
return nil, nil, fmt.Errorf("invalid value for statuscodes: %v", err)
}
plugin.Cookies, err = cmdDir.Flags().GetString("cookies")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for cookies: %v", err)
}
plugin.Username, err = cmdDir.Flags().GetString("username")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for username: %v", err)
}
plugin.Password, err = cmdDir.Flags().GetString("password")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for password: %v", err)
}
plugin.Extensions, err = cmdDir.Flags().GetString("extensions")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for extensions: %v", err)
}
if plugin.Extensions != "" {
if err = plugin.ParseExtensions(); err != nil {
return nil, nil, fmt.Errorf("invalid value for extensions: %v", err)
}
}
plugin.UserAgent, err = cmdDir.Flags().GetString("useragent")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for useragent: %v", err)
}
plugin.Proxy, err = cmdDir.Flags().GetString("proxy")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for proxy: %v", err)
}
plugin.Timeout, err = cmdDir.Flags().GetDuration("timeout")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for timeout: %v", err)
}
plugin.FollowRedirect, err = cmdDir.Flags().GetBool("followredirect")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for followredirect: %v", err)
}
plugin.Expanded, err = cmdDir.Flags().GetBool("expanded")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for expanded: %v", err)
}
plugin.NoStatus, err = cmdDir.Flags().GetBool("nostatus")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for nostatus: %v", err)
}
plugin.IncludeLength, err = cmdDir.Flags().GetBool("includelength")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for includelength: %v", err)
}
plugin.InsecureSSL, err = cmdDir.Flags().GetBool("insecuressl")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for insecuressl: %v", err)
}
plugin.WildcardForced, err = cmdDir.Flags().GetBool("wildcard")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for wildcard: %v", err)
}
plugin.UseSlash, err = cmdDir.Flags().GetBool("addslash")
if err != nil {
return nil, nil, fmt.Errorf("invalid value for addslash: %v", err)
}
// Prompt for PW if not provided
if plugin.Username != "" && plugin.Password == "" {
fmt.Printf("[?] Auth Password: ")
passBytes, err := terminal.ReadPassword(int(syscall.Stdin))
// 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 nil, nil, fmt.Errorf("username given but reading of password failed")
}
plugin.Password = string(passBytes)
}
// if it's still empty bail out
if plugin.Username != "" && plugin.Password == "" {
return nil, nil, fmt.Errorf("username was provided but password is missing")
}
return globalopts, plugin, nil
}
func init() {
cmdDir = &cobra.Command{
Use: "dir",
Short: "Uses directory/file brutceforcing mode",
RunE: runDir,
}
cmdDir.Flags().StringP("url", "u", "", "The target URL")
cmdDir.Flags().StringP("statuscodes", "s", "200,204,301,302,307,401,403", "Positive status codes")
cmdDir.Flags().StringP("cookies", "c", "", "Cookies to use for the requests")
cmdDir.Flags().StringP("username", "U", "", "Username for Basic Auth")
cmdDir.Flags().StringP("password", "P", "", "Password for Basic Auth")
cmdDir.Flags().StringP("extensions", "x", "", "File extension(s) to search for")
cmdDir.Flags().StringP("useragent", "a", libgobuster.DefaultUserAgent(), "Set the User-Agent string")
cmdDir.Flags().StringP("proxy", "p", "", "Proxy to use for requests [http(s)://host:port]")
cmdDir.Flags().DurationP("timeout", "", 10*time.Second, "HTTP Timeout")
cmdDir.Flags().BoolP("followredirect", "r", false, "Follow redirects")
cmdDir.Flags().BoolP("expanded", "e", false, "Expanded mode, print full URLs")
cmdDir.Flags().BoolP("nostatus", "n", false, "Don't print status codes")
cmdDir.Flags().BoolP("includelength", "l", false, "Include the length of the body in the output")
cmdDir.Flags().BoolP("insecuressl", "k", false, "Skip SSL certificate verification")
cmdDir.Flags().BoolP("addslash", "f", false, "Apped / to each request")
cmdDir.Flags().BoolP("wildcard", "", false, "Force continued operation when wildcard found")
if err := cmdDir.MarkFlagRequired("url"); err != nil {
log.Fatalf("error on marking flag as required: %v", err)
}
cmdDir.PersistentPreRun = func(cmd *cobra.Command, args []string) {
configureGlobalOptions()
}
rootCmd.AddCommand(cmdDir)
}