Merge tag 'upstream/1.2' into kali/master
Upstream version 1.2
Sophie Brun
7 years ago
0 | Gobuster v1.1 (OJ Reeves @TheColonial) | |
0 | Gobuster v1.2 (OJ Reeves @TheColonial) | |
1 | 1 | ====================================== |
2 | 2 | |
3 | Alternative directory and file busting tool written in Go. DNS support recently added after inspiration and effort from [Peleus](https://twitter.com/0x42424242). | |
3 | Gobuster is a tool used to brute-force: | |
4 | ||
5 | * URIs (directories and files) in web sites. | |
6 | * DNS subdomains (with wildcard support). | |
4 | 7 | |
5 | 8 | ### Oh dear God.. WHY!? |
6 | 9 | |
18 | 21 | |
19 | 22 | ### But it's shit! And your implementation sucks! |
20 | 23 | |
21 | Yes, you're probably correct. Feel free to : | |
24 | Yes, you're probably correct. Feel free to: | |
22 | 25 | |
23 | 26 | * Not use it. |
24 | 27 | * Show me how to do it better. |
26 | 29 | ### Common Command line options |
27 | 30 | |
28 | 31 | * `-m <mode>` - which mode to use, either `dir` or `dns` (default: `dir`) |
32 | * `-q` - disables banner/underline output. | |
29 | 33 | * `-t <threads>` - number of threads to run (default: `10`). |
30 | 34 | * `-u <url/domain>` - full URL (including scheme), or base domain name. |
31 | 35 | * `-v` - verbose output (show all results). |
33 | 37 | |
34 | 38 | ### Command line options for `dns` mode |
35 | 39 | |
40 | * `-fw` - Force processing of a domain with wildcard DNS. | |
36 | 41 | * `-i` - show all IP addresses for the result. |
37 | 42 | |
38 | 43 | ### Command line options for `dir` mode |
39 | 44 | |
40 | 45 | * `-a <user agent string>` - specify a user agent string to send in the request header |
41 | 46 | * `-c <http cookies>` - use this to specify any cookies that you might need (simulating auth). |
47 | * `-e` - specify extended mode that renders the full URL. | |
42 | 48 | * `-f` - append `/` for directory brute forces. |
43 | 49 | * `-l` - show the length of the response. |
44 | 50 | * `-n` - "no status" mode, disables the output of the result's status code. |
45 | 51 | * `-p <proxy url>` - specify a proxy to use for all requests (scheme much match the URL scheme) |
46 | * `-q` - disables banner/underline output. | |
47 | 52 | * `-r` - follow redirects. |
48 | 53 | * `-s <status codes>` - comma-separated set of the list of status codes to be deemed a "positive" (default: `200,204,301,302,307`). |
49 | 54 | * `-x <extensions>` - list of extensions to check for, if any. |
55 | 60 | Since this tool is written in [Go](https://golang.org/) you need install the Go language/compiler/etc. Full details of installation and set up can be found [on the Go language website](https://golang.org/doc/install). Once installed you have two options. |
56 | 61 | |
57 | 62 | #### Compiling |
58 | ``` | |
59 | gobuster$ go build | |
60 | ``` | |
61 | This will create a `gobuster` binary for you. | |
63 | `gobuster` now has external dependencies, and so they need to be pulled in first: | |
64 | ``` | |
65 | gobuster $ go get && go build | |
66 | ``` | |
67 | This will create a `gobuster` binary for you. If you want to install it in the `$GOPATH/bin` folder you can run: | |
68 | ``` | |
69 | gobuster $ go install | |
70 | ``` | |
62 | 71 | |
63 | 72 | #### Running as a script |
64 | 73 | ``` |
65 | 74 | gobuster$ go run main.go <parameters> |
66 | 75 | ``` |
67 | 76 | |
77 | ### Wordlists via STDIN | |
78 | Wordlists can be piped into `gobuster` via stdin: | |
79 | ``` | |
80 | hashcat -a 3 --stdout ?l | gobuster -u https://mysite.com | |
81 | ``` | |
82 | Note: If the `-w` option is specified at the same time as piping from STDIN, an error will be shown and the program will terminate. | |
83 | ||
68 | 84 | ### Examples |
69 | 85 | |
70 | 86 | #### `dir` mode |
71 | 87 | |
72 | 88 | Command line might look like this: |
73 | 89 | ``` |
74 | $ ./gobuster -u https://mysite.com/path/to/folder -c 'session=123456' -t 50 -w common-files.txt -x .php,.html | |
90 | $ gobuster -u https://mysite.com/path/to/folder -c 'session=123456' -t 50 -w common-files.txt -x .php,.html | |
75 | 91 | ``` |
76 | 92 | Default options looks like this: |
77 | 93 | ``` |
78 | $ ./gobuster -u http://buffered.io/ -w words.txt | |
79 | ||
80 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
94 | $ gobuster -u http://buffered.io/ -w words.txt | |
95 | ||
96 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
81 | 97 | ===================================================== |
82 | 98 | [+] Mode : dir |
83 | 99 | [+] Url/Domain : http://buffered.io/ |
92 | 108 | ``` |
93 | 109 | Default options with status codes disabled looks like this: |
94 | 110 | ``` |
95 | $ ./gobuster -u http://buffered.io/ -w words.txt -n | |
96 | ||
97 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
111 | $ gobuster -u http://buffered.io/ -w words.txt -n | |
112 | ||
113 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
98 | 114 | ===================================================== |
99 | 115 | [+] Mode : dir |
100 | 116 | [+] Url/Domain : http://buffered.io/ |
110 | 126 | ``` |
111 | 127 | Verbose output looks like this: |
112 | 128 | ``` |
113 | $ ./gobuster -u http://buffered.io/ -w words.txt -v | |
114 | ||
115 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
129 | $ gobuster -u http://buffered.io/ -w words.txt -v | |
130 | ||
131 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
116 | 132 | ===================================================== |
117 | 133 | [+] Mode : dir |
118 | 134 | [+] Url/Domain : http://buffered.io/ |
129 | 145 | ``` |
130 | 146 | Example showing content length: |
131 | 147 | ``` |
132 | $ ./gobuster -u http://buffered.io/ -w words.txt -l | |
133 | ||
134 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
148 | $ gobuster -u http://buffered.io/ -w words.txt -l | |
149 | ||
150 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
135 | 151 | ===================================================== |
136 | 152 | [+] Mode : dir |
137 | 153 | [+] Url/Domain : http://buffered.io/ |
147 | 163 | ``` |
148 | 164 | Quiet output, with status disabled and expanded mode looks like this ("grep mode"): |
149 | 165 | ``` |
150 | $ ./gobuster -u http://buffered.io/ -w words.txt -q -n -e | |
166 | $ gobuster -u http://buffered.io/ -w words.txt -q -n -e | |
151 | 167 | http://buffered.io/posts |
152 | 168 | http://buffered.io/contact |
153 | 169 | http://buffered.io/index |
157 | 173 | |
158 | 174 | Command line might look like this: |
159 | 175 | ``` |
160 | $ ./gobuster -m dns -u mysite.com -t 50 -w common-names.txt | |
176 | $ gobuster -m dns -u mysite.com -t 50 -w common-names.txt | |
161 | 177 | ``` |
162 | 178 | Normal sample run goes like this: |
163 | 179 | ``` |
164 | $ ./gobuster -m dns -w subdomains.txt -u google.com | |
165 | ||
166 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
180 | $ gobuster -m dns -w subdomains.txt -u google.com | |
181 | ||
182 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
167 | 183 | ===================================================== |
168 | 184 | [+] Mode : dns |
169 | 185 | [+] Url/Domain : google.com |
192 | 208 | ``` |
193 | 209 | Show IP sample run goes like this: |
194 | 210 | ``` |
195 | $ ./gobuster -m dns -w subdomains.txt -u google.com -i | |
196 | ||
197 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
211 | $ gobuster -m dns -w subdomains.txt -u google.com -i | |
212 | ||
213 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
198 | 214 | ===================================================== |
199 | 215 | [+] Mode : dns |
200 | 216 | [+] Url/Domain : google.com |
224 | 240 | ``` |
225 | 241 | Base domain validation warning when the base domain fails to resolve. This is a warning rather than a failure in case the user fat-fingers while typing the domain. |
226 | 242 | ``` |
227 | $ ./gobuster -m dns -w subdomains.txt -u yp.to -i | |
228 | ||
229 | Gobuster v1.1 OJ Reeves (@TheColonial) | |
243 | $ gobuster -m dns -w subdomains.txt -u yp.to -i | |
244 | ||
245 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
230 | 246 | ===================================================== |
231 | 247 | [+] Mode : dns |
232 | 248 | [+] Url/Domain : yp.to |
233 | 249 | [+] Threads : 10 |
234 | 250 | [+] Wordlist : /tmp/test.txt |
235 | 251 | ===================================================== |
236 | [!] Unable to validate base domain: yp.to | |
252 | [-] Unable to validate base domain: yp.to | |
237 | 253 | Found: cr.yp.to [131.155.70.11, 131.155.70.13] |
238 | 254 | ===================================================== |
239 | 255 | ``` |
256 | Wildcard DNS is also detected properly: | |
257 | ``` | |
258 | $ gobuster -w subdomainsbig.txt -u doesntexist.com -m dns | |
259 | ||
260 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
261 | ===================================================== | |
262 | [+] Mode : dns | |
263 | [+] Url/Domain : doesntexist.com | |
264 | [+] Threads : 10 | |
265 | [+] Wordlist : subdomainsbig.txt | |
266 | ===================================================== | |
267 | [-] Wildcard DNS found. IP address(es): 123.123.123.123 | |
268 | [-] To force processing of Wildcard DNS, specify the '-fw' switch. | |
269 | ===================================================== | |
270 | ``` | |
271 | If the user wants to force processing of a domain that has wildcard entries, use `-fw`: | |
272 | ``` | |
273 | $ gobuster -w subdomainsbig.txt -u doesntexist.com -m dns -fw | |
274 | ||
275 | Gobuster v1.2 OJ Reeves (@TheColonial) | |
276 | ===================================================== | |
277 | [+] Mode : dns | |
278 | [+] Url/Domain : doesntexist.com | |
279 | [+] Threads : 10 | |
280 | [+] Wordlist : subdomainsbig.txt | |
281 | ===================================================== | |
282 | [-] Wildcard DNS found. IP address(es): 123.123.123.123 | |
283 | Found: email.doesntexist.com | |
284 | ^C[!] Keyboard interrupt detected, terminating. | |
285 | ===================================================== | |
240 | 286 | |
241 | 287 | ### License |
242 | 288 |
3 | 3 | @justinsteven - HTTP basic auth support |
4 | 4 | @kevinnz - custom user agent support |
5 | 5 | @viaMorgoth - base domain validation for DNS mode |
6 | @UID1K - DNS wildcard check support | |
6 | @UID1K - initial DNS wildcard check support | |
7 | @Ne0nd0g - STDIN support for wordlists |
20 | 20 | "crypto/tls" |
21 | 21 | "flag" |
22 | 22 | "fmt" |
23 | "github.com/satori/go.uuid" | |
23 | 24 | "golang.org/x/crypto/ssh/terminal" |
24 | 25 | "io/ioutil" |
25 | 26 | "net" |
26 | 27 | "net/http" |
27 | 28 | "net/url" |
28 | 29 | "os" |
30 | "os/signal" | |
29 | 31 | "strconv" |
30 | 32 | "strings" |
31 | 33 | "sync" |
46 | 48 | type ProcessorFunc func(s *State, entity string, resultChan chan<- Result) |
47 | 49 | type SetupFunc func(s *State) bool |
48 | 50 | |
49 | // Shim type for "set" | |
51 | // Shim type for "set" containing ints | |
50 | 52 | type IntSet struct { |
51 | 53 | set map[int]bool |
54 | } | |
55 | ||
56 | // Shim type for "set" containing strings | |
57 | type StringSet struct { | |
58 | set map[string]bool | |
52 | 59 | } |
53 | 60 | |
54 | 61 | // Contains State that are read in from the command |
77 | 84 | Username string |
78 | 85 | Verbose bool |
79 | 86 | Wordlist string |
87 | IsWildcard bool | |
88 | WildcardForced bool | |
89 | WildcardIps StringSet | |
90 | SignalChan chan os.Signal | |
91 | Terminate bool | |
92 | StdIn bool | |
80 | 93 | } |
81 | 94 | |
82 | 95 | type RedirectHandler struct { |
86 | 99 | |
87 | 100 | type RedirectError struct { |
88 | 101 | StatusCode int |
102 | } | |
103 | ||
104 | // Add an element to a set | |
105 | func (set *StringSet) Add(s string) bool { | |
106 | _, found := set.set[s] | |
107 | set.set[s] = true | |
108 | return !found | |
109 | } | |
110 | ||
111 | // Add a list of elements to a set | |
112 | func (set *StringSet) AddRange(ss []string) { | |
113 | for _, s := range ss { | |
114 | set.set[s] = true | |
115 | } | |
116 | } | |
117 | ||
118 | // Test if an element is in a set | |
119 | func (set *StringSet) Contains(s string) bool { | |
120 | _, found := set.set[s] | |
121 | return found | |
122 | } | |
123 | ||
124 | // Check if any of the elements exist | |
125 | func (set *StringSet) ContainsAny(ss []string) bool { | |
126 | for _, s := range ss { | |
127 | if set.set[s] { | |
128 | return true | |
129 | } | |
130 | } | |
131 | return false | |
132 | } | |
133 | ||
134 | // Stringify the set | |
135 | func (set *StringSet) Stringify() string { | |
136 | values := []string{} | |
137 | for s, _ := range set.set { | |
138 | values = append(values, s) | |
139 | } | |
140 | return strings.Join(values, ",") | |
89 | 141 | } |
90 | 142 | |
91 | 143 | // Add an element to a set |
174 | 226 | var proxy string |
175 | 227 | valid := true |
176 | 228 | |
177 | s := State{StatusCodes: IntSet{set: map[int]bool{}}} | |
229 | s := State{ | |
230 | StatusCodes: IntSet{set: map[int]bool{}}, | |
231 | WildcardIps: StringSet{set: map[string]bool{}}, | |
232 | IsWildcard: false, | |
233 | StdIn: false, | |
234 | } | |
178 | 235 | |
179 | 236 | // Set up the variables we're interested in parsing. |
180 | 237 | flag.IntVar(&s.Threads, "t", 10, "Number of concurrent threads") |
196 | 253 | flag.BoolVar(&s.NoStatus, "n", false, "Don't print status codes") |
197 | 254 | flag.BoolVar(&s.IncludeLength, "l", false, "Include the length of the body in the output (dir mode only)") |
198 | 255 | flag.BoolVar(&s.UseSlash, "f", false, "Append a forward-slash to each directory request (dir mode only)") |
256 | flag.BoolVar(&s.WildcardForced, "fw", false, "Force continued operation when wildcard found (dns mode only)") | |
199 | 257 | |
200 | 258 | flag.Parse() |
201 | 259 | |
211 | 269 | s.Processor = ProcessDnsEntry |
212 | 270 | s.Setup = SetupDns |
213 | 271 | default: |
214 | fmt.Println("Mode (-m): Invalid value:", s.Mode) | |
272 | fmt.Println("[!] Mode (-m): Invalid value:", s.Mode) | |
215 | 273 | valid = false |
216 | 274 | } |
217 | 275 | |
218 | 276 | if s.Threads < 0 { |
219 | fmt.Println("Threads (-t): Invalid value:", s.Threads) | |
277 | fmt.Println("[!] Threads (-t): Invalid value:", s.Threads) | |
220 | 278 | valid = false |
221 | 279 | } |
222 | 280 | |
223 | if s.Wordlist == "" { | |
224 | fmt.Println("WordList (-w): Must be specified") | |
281 | stdin, err := os.Stdin.Stat() | |
282 | if err != nil { | |
283 | fmt.Println("[!] Unable to stat stdin, falling back to wordlist file.") | |
284 | } else if (stdin.Mode()&os.ModeCharDevice) == 0 && stdin.Size() > 0 { | |
285 | s.StdIn = true | |
286 | } | |
287 | ||
288 | if !s.StdIn { | |
289 | if s.Wordlist == "" { | |
290 | fmt.Println("[!] WordList (-w): Must be specified") | |
291 | valid = false | |
292 | } else if _, err := os.Stat(s.Wordlist); os.IsNotExist(err) { | |
293 | fmt.Println("[!] Wordlist (-w): File does not exist:", s.Wordlist) | |
294 | valid = false | |
295 | } | |
296 | } else if s.Wordlist != "" { | |
297 | fmt.Println("[!] Wordlist (-w) specified with pipe from stdin. Can't have both!") | |
225 | 298 | valid = false |
226 | } else if _, err := os.Stat(s.Wordlist); os.IsNotExist(err) { | |
227 | fmt.Println("Wordlist (-w): File does not exist:", s.Wordlist) | |
228 | valid = false | |
229 | 299 | } |
230 | 300 | |
231 | 301 | if s.Url == "" { |
232 | fmt.Println("Url/Domain (-u): Must be specified") | |
302 | fmt.Println("[!] Url/Domain (-u): Must be specified") | |
233 | 303 | valid = false |
234 | 304 | } |
235 | 305 | |
242 | 312 | s.Url = "http://" + s.Url |
243 | 313 | } |
244 | 314 | |
245 | // extensions are comma seaprated | |
315 | // extensions are comma separated | |
246 | 316 | if extensions != "" { |
247 | 317 | s.Extensions = strings.Split(extensions, ",") |
248 | 318 | for i := range s.Extensions { |
252 | 322 | } |
253 | 323 | } |
254 | 324 | |
255 | // status codes are comma seaprated | |
325 | // status codes are comma separated | |
256 | 326 | if codes != "" { |
257 | 327 | for _, c := range strings.Split(codes, ",") { |
258 | 328 | i, err := strconv.Atoi(c) |
259 | 329 | if err != nil { |
260 | panic("Invalid status code given") | |
330 | fmt.Println("[!] Invalid status code given: ", c) | |
331 | valid = false | |
332 | } else { | |
333 | s.StatusCodes.Add(i) | |
261 | 334 | } |
262 | s.StatusCodes.Add(i) | |
263 | 335 | } |
264 | 336 | } |
265 | 337 | |
275 | 347 | if err == nil { |
276 | 348 | s.Password = string(passBytes) |
277 | 349 | } else { |
278 | panic("Auth username given but reading of password failed") | |
350 | fmt.Println("[!] Auth username given but reading of password failed") | |
351 | valid = false | |
279 | 352 | } |
280 | 353 | } |
281 | 354 | |
286 | 359 | if proxy != "" { |
287 | 360 | proxyUrl, err := url.Parse(proxy) |
288 | 361 | if err != nil { |
289 | panic("Proxy URL is invalid") | |
362 | panic("[!] Proxy URL is invalid") | |
290 | 363 | } |
291 | 364 | s.ProxyUrl = proxyUrl |
292 | 365 | proxyUrlFunc = http.ProxyURL(s.ProxyUrl) |
308 | 381 | fmt.Println("[-] Unable to connect:", s.Url) |
309 | 382 | valid = false |
310 | 383 | } |
384 | } else { | |
385 | Ruler(&s) | |
311 | 386 | } |
312 | 387 | } |
313 | 388 | |
321 | 396 | // Process the busting of the website with the given |
322 | 397 | // set of settings from the command line. |
323 | 398 | func Process(s *State) { |
324 | wordlist, err := os.Open(s.Wordlist) | |
325 | if err != nil { | |
326 | panic("Failed to open wordlist") | |
327 | } | |
328 | 399 | |
329 | 400 | ShowConfig(s) |
330 | 401 | |
332 | 403 | Ruler(s) |
333 | 404 | return |
334 | 405 | } |
406 | ||
407 | PrepareSignalHandler(s) | |
335 | 408 | |
336 | 409 | // channels used for comms |
337 | 410 | wordChan := make(chan string, s.Threads) |
375 | 448 | printerGroup.Done() |
376 | 449 | }() |
377 | 450 | |
378 | defer wordlist.Close() | |
379 | ||
380 | // Lazy reading of the wordlist line by line | |
381 | scanner := bufio.NewScanner(wordlist) | |
451 | var scanner *bufio.Scanner | |
452 | ||
453 | if s.StdIn { | |
454 | // Read directly from stdin | |
455 | scanner = bufio.NewScanner(os.Stdin) | |
456 | } else { | |
457 | // Pull content from the wordlist | |
458 | wordlist, err := os.Open(s.Wordlist) | |
459 | if err != nil { | |
460 | panic("Failed to open wordlist") | |
461 | } | |
462 | defer wordlist.Close() | |
463 | ||
464 | // Lazy reading of the wordlist line by line | |
465 | scanner = bufio.NewScanner(wordlist) | |
466 | } | |
467 | ||
382 | 468 | for scanner.Scan() { |
469 | if s.Terminate { | |
470 | break | |
471 | } | |
383 | 472 | word := strings.TrimSpace(scanner.Text()) |
384 | 473 | |
385 | 474 | // Skip "comment" (starts with #), as well as empty lines |
397 | 486 | |
398 | 487 | func SetupDns(s *State) bool { |
399 | 488 | // Resolve a subdomain that probably shouldn't exist |
400 | _, err := net.LookupHost("bba1b18d-50f8-4f1d-8295-c861445ed7f5." + s.Url) | |
489 | guid := uuid.NewV4() | |
490 | wildcardIps, err := net.LookupHost(fmt.Sprintf("%s.%s", guid, s.Url)) | |
401 | 491 | if err == nil { |
402 | fmt.Println("[-] Wildcard DNS found.") | |
403 | return false | |
492 | s.IsWildcard = true | |
493 | s.WildcardIps.AddRange(wildcardIps) | |
494 | fmt.Println("[-] Wildcard DNS found. IP address(es): ", s.WildcardIps.Stringify()) | |
495 | if !s.WildcardForced { | |
496 | fmt.Println("[-] To force processing of Wildcard DNS, specify the '-fw' switch.") | |
497 | } | |
498 | return s.WildcardForced | |
404 | 499 | } |
405 | 500 | |
406 | 501 | if !s.Quiet { |
408 | 503 | _, err = net.LookupHost(s.Url) |
409 | 504 | if err != nil { |
410 | 505 | // Not an error, just a warning. Eg. `yp.to` doesn't resolve, but `cr.py.to` does! |
411 | fmt.Println("[!] Unable to validate base domain:", s.Url) | |
506 | fmt.Println("[-] Unable to validate base domain:", s.Url) | |
412 | 507 | } |
413 | 508 | } |
414 | 509 | |
424 | 519 | ips, err := net.LookupHost(subdomain) |
425 | 520 | |
426 | 521 | if err == nil { |
427 | result := Result{ | |
428 | Entity: subdomain, | |
429 | } | |
430 | if s.ShowIPs { | |
431 | result.Extra = strings.Join(ips, ", ") | |
432 | } | |
433 | resultChan <- result | |
522 | if !s.IsWildcard || !s.WildcardIps.ContainsAny(ips) { | |
523 | result := Result{ | |
524 | Entity: subdomain, | |
525 | } | |
526 | if s.ShowIPs { | |
527 | result.Extra = strings.Join(ips, ", ") | |
528 | } | |
529 | resultChan <- result | |
530 | } | |
434 | 531 | } else if s.Verbose { |
435 | 532 | result := Result{ |
436 | 533 | Entity: subdomain, |
513 | 610 | } |
514 | 611 | } |
515 | 612 | |
613 | func PrepareSignalHandler(s *State) { | |
614 | s.SignalChan = make(chan os.Signal, 1) | |
615 | signal.Notify(s.SignalChan, os.Interrupt) | |
616 | go func() { | |
617 | for _ = range s.SignalChan { | |
618 | // caught CTRL+C | |
619 | if !s.Quiet { | |
620 | fmt.Println("[!] Keyboard interrupt detected, terminating.") | |
621 | s.Terminate = true | |
622 | } | |
623 | } | |
624 | }() | |
625 | } | |
626 | ||
516 | 627 | func (e *RedirectError) Error() string { |
517 | 628 | return fmt.Sprintf("Redirect code: %d", e.StatusCode) |
518 | 629 | } |
548 | 659 | } |
549 | 660 | |
550 | 661 | fmt.Println("") |
551 | fmt.Println("Gobuster v1.1 OJ Reeves (@TheColonial)") | |
662 | fmt.Println("Gobuster v1.2 OJ Reeves (@TheColonial)") | |
552 | 663 | Ruler(state) |
553 | 664 | } |
554 | 665 | |
561 | 672 | fmt.Printf("[+] Mode : %s\n", state.Mode) |
562 | 673 | fmt.Printf("[+] Url/Domain : %s\n", state.Url) |
563 | 674 | fmt.Printf("[+] Threads : %d\n", state.Threads) |
564 | fmt.Printf("[+] Wordlist : %s\n", state.Wordlist) | |
675 | ||
676 | wordlist := "stdin (pipe)" | |
677 | if !state.StdIn { | |
678 | wordlist = state.Wordlist | |
679 | } | |
680 | fmt.Printf("[+] Wordlist : %s\n", wordlist) | |
565 | 681 | |
566 | 682 | if state.Mode == "dir" { |
567 | 683 | fmt.Printf("[+] Status codes : %s\n", state.StatusCodes.Stringify()) |