mirror of
https://github.com/OJ/gobuster.git
synced 2026-10-12 02:07:17 +00:00
Add support for cookies, status codes, and more
* Cookies can now be added in case you want to brute force something when authenticated. Use the -c flag. * If you want to add a trailing slash to the DIR busting, you can now set -f=true. * -v=true now says "output verbose" which shows the result for every request that is made (except the first). * You can specify your own positive status codes on the command line now through the -s=... parameter.
This commit is contained in:
@@ -18,18 +18,27 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Shim type for "set"
|
||||
type IntSet struct {
|
||||
set map[int]bool
|
||||
}
|
||||
|
||||
// Contains settings that are read in from the command
|
||||
// line when the program is invoked.
|
||||
type Settings struct {
|
||||
Threads int
|
||||
Wordlist string
|
||||
Url string
|
||||
Extensions []string
|
||||
ShowAll bool
|
||||
Threads int
|
||||
Wordlist string
|
||||
Url string
|
||||
Cookies string
|
||||
Extensions []string
|
||||
StatusCodes IntSet
|
||||
ShowAll bool
|
||||
UseSlash bool
|
||||
}
|
||||
|
||||
// A single result which comes from an individual web
|
||||
@@ -39,9 +48,41 @@ type Result struct {
|
||||
Status int
|
||||
}
|
||||
|
||||
// Add an element to a set
|
||||
func (set *IntSet) Add(i int) bool {
|
||||
_, found := set.set[i]
|
||||
set.set[i] = true
|
||||
return !found
|
||||
}
|
||||
|
||||
// Test if an element is in a set
|
||||
func (set *IntSet) Contains(i int) bool {
|
||||
_, found := set.set[i]
|
||||
return found
|
||||
}
|
||||
|
||||
// Stringify the set
|
||||
func (set *IntSet) Stringify() string {
|
||||
values := []string{}
|
||||
for s, _ := range set.set {
|
||||
values = append(values, strconv.Itoa(s))
|
||||
}
|
||||
return strings.Join(values, ",")
|
||||
}
|
||||
|
||||
// Make a request to the given URL.
|
||||
func MakeRequest(url string) *int {
|
||||
resp, err := http.Get(url)
|
||||
func MakeRequest(client *http.Client, url, cookie string) *int {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cookie != "" {
|
||||
req.Header.Set("Cookie", cookie)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -54,22 +95,27 @@ func MakeRequest(url string) *int {
|
||||
|
||||
// Small helper to combine URL with URI then make a
|
||||
// request to the generated location.
|
||||
func GoGet(url, uri string) *int {
|
||||
return MakeRequest(url + uri)
|
||||
func GoGet(client *http.Client, url, uri, cookie string) *int {
|
||||
return MakeRequest(client, url+uri, cookie)
|
||||
}
|
||||
|
||||
// Parse all the command line options into a settings
|
||||
// instance for future use.
|
||||
func ParseCmdLine() *Settings {
|
||||
var extensions string
|
||||
s := Settings{}
|
||||
var codes string
|
||||
|
||||
s := Settings{StatusCodes: IntSet{set: map[int]bool{}}}
|
||||
|
||||
// Set up the variables we're interested in parsing.
|
||||
flag.IntVar(&s.Threads, "t", 10, "Number of concurrent threads")
|
||||
flag.StringVar(&s.Wordlist, "w", "", "Path to the wordlist")
|
||||
flag.StringVar(&codes, "s", "200,204,301,302,307", "Positive status codes")
|
||||
flag.StringVar(&s.Url, "u", "", "The target URL")
|
||||
flag.StringVar(&s.Cookies, "c", "", "Cookies to use for the requests")
|
||||
flag.StringVar(&extensions, "x", "", "File extension(s) to search for")
|
||||
flag.BoolVar(&s.ShowAll, "s", false, "Show all results (not just positives)")
|
||||
flag.BoolVar(&s.ShowAll, "v", false, "Show all results (not just positives)")
|
||||
flag.BoolVar(&s.UseSlash, "f", false, "Append a forward-slash to each directory request")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
@@ -102,7 +148,19 @@ func ParseCmdLine() *Settings {
|
||||
s.Extensions = strings.Split(extensions, ",")
|
||||
}
|
||||
|
||||
if GoGet(s.Url, "") == nil {
|
||||
// status codes are comma seaprated
|
||||
if codes != "" {
|
||||
for _, c := range strings.Split(codes, ",") {
|
||||
i, err := strconv.Atoi(c)
|
||||
if err != nil {
|
||||
panic("Invalid status code given")
|
||||
}
|
||||
s.StatusCodes.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
if GoGet(client, s.Url, "", s.Cookies) == nil {
|
||||
fmt.Println("Url (-u): Unable to connect", s.Url)
|
||||
}
|
||||
|
||||
@@ -126,6 +184,13 @@ func Process(s *Settings) {
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(s.Threads)
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
suffix := ""
|
||||
if s.UseSlash {
|
||||
suffix = "/"
|
||||
}
|
||||
|
||||
// Create goroutines for each of the number of threads
|
||||
// specified.
|
||||
for i := 0; i < s.Threads; i++ {
|
||||
@@ -138,11 +203,11 @@ func Process(s *Settings) {
|
||||
break
|
||||
}
|
||||
|
||||
// Try the DIR first.
|
||||
dirResp := GoGet(s.Url, word)
|
||||
// Try the DIR first
|
||||
dirResp := GoGet(client, s.Url, word+suffix, s.Cookies)
|
||||
if dirResp != nil {
|
||||
resultChan <- Result{
|
||||
Uri: word,
|
||||
Uri: word + suffix,
|
||||
Status: *dirResp,
|
||||
}
|
||||
}
|
||||
@@ -150,7 +215,7 @@ func Process(s *Settings) {
|
||||
// Follow up with files using each ext.
|
||||
for ext := range s.Extensions {
|
||||
file := word + s.Extensions[ext]
|
||||
fileResp := GoGet(s.Url, file)
|
||||
fileResp := GoGet(client, s.Url, file, s.Cookies)
|
||||
if fileResp != nil {
|
||||
resultChan <- Result{
|
||||
Uri: file,
|
||||
@@ -170,17 +235,14 @@ func Process(s *Settings) {
|
||||
// appear from the worker threads.
|
||||
go func() {
|
||||
for r := range resultChan {
|
||||
switch r.Status {
|
||||
// Only print results out if we find something
|
||||
// meaningful.
|
||||
case 200, 204, 301, 302, 307:
|
||||
if s.StatusCodes.Contains(r.Status) {
|
||||
// Only print results out if we find something
|
||||
// meaningful.
|
||||
fmt.Printf("Found: /%s (%d)\n", r.Uri, r.Status)
|
||||
default:
|
||||
} else if s.ShowAll {
|
||||
// Print out other results if the user wants to
|
||||
// see them.
|
||||
if s.ShowAll {
|
||||
fmt.Printf("Result: /%s (%d)\n", r.Uri, r.Status)
|
||||
}
|
||||
fmt.Printf("Result: /%s (%d)\n", r.Uri, r.Status)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -204,7 +266,7 @@ func Process(s *Settings) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Gobuster v0.1 (OJ Reeves @TheColonial)")
|
||||
fmt.Println("Gobuster v0.2 (OJ Reeves @TheColonial)")
|
||||
fmt.Println("======================================")
|
||||
|
||||
settings := ParseCmdLine()
|
||||
@@ -213,12 +275,25 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("[+] Url : %s\n", settings.Url)
|
||||
fmt.Printf("[+] Threads : %d\n", settings.Threads)
|
||||
fmt.Printf("[+] Wordlist : %s\n", settings.Wordlist)
|
||||
fmt.Printf("[+] Url : %s\n", settings.Url)
|
||||
fmt.Printf("[+] Threads : %d\n", settings.Threads)
|
||||
fmt.Printf("[+] Wordlist : %s\n", settings.Wordlist)
|
||||
fmt.Printf("[+] Status codes : %s\n", settings.StatusCodes.Stringify())
|
||||
|
||||
if settings.Cookies != "" {
|
||||
fmt.Printf("[+] Cookies : %s\n", settings.Cookies)
|
||||
}
|
||||
|
||||
if len(settings.Extensions) > 0 {
|
||||
fmt.Printf("[+] Extensions : %s\n", strings.Join(settings.Extensions, ","))
|
||||
fmt.Printf("[+] Extensions : %s\n", strings.Join(settings.Extensions, ","))
|
||||
}
|
||||
|
||||
if settings.UseSlash {
|
||||
fmt.Printf("[+] Add Slash : true\n")
|
||||
}
|
||||
|
||||
if settings.ShowAll {
|
||||
fmt.Printf("[+] Dislpay all : true\n")
|
||||
}
|
||||
|
||||
fmt.Println("======================================")
|
||||
|
||||
Reference in New Issue
Block a user