Imported Upstream version 1.0
Sophie Brun
8 years ago
0 | # Compiled Object files, Static and Dynamic libs (Shared Objects) | |
1 | *.o | |
2 | *.a | |
3 | *.so | |
4 | ||
5 | # Folders | |
6 | _obj | |
7 | _test | |
8 | ||
9 | # Architecture specific extensions/prefixes | |
10 | *.[568vq] | |
11 | [568vq].out | |
12 | ||
13 | *.cgo1.go | |
14 | *.cgo2.c | |
15 | _cgo_defun.c | |
16 | _cgo_gotypes.go | |
17 | _cgo_export.* | |
18 | ||
19 | _testmain.go | |
20 | ||
21 | *.exe | |
22 | *.test | |
23 | *.prof | |
24 | gobuster | |
25 | *.txt | |
26 | *.swp |
0 | Apache License | |
1 | Version 2.0, January 2004 | |
2 | http://www.apache.org/licenses/ | |
3 | ||
4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
5 | ||
6 | 1. Definitions. | |
7 | ||
8 | "License" shall mean the terms and conditions for use, reproduction, | |
9 | and distribution as defined by Sections 1 through 9 of this document. | |
10 | ||
11 | "Licensor" shall mean the copyright owner or entity authorized by | |
12 | the copyright owner that is granting the License. | |
13 | ||
14 | "Legal Entity" shall mean the union of the acting entity and all | |
15 | other entities that control, are controlled by, or are under common | |
16 | control with that entity. For the purposes of this definition, | |
17 | "control" means (i) the power, direct or indirect, to cause the | |
18 | direction or management of such entity, whether by contract or | |
19 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
20 | outstanding shares, or (iii) beneficial ownership of such entity. | |
21 | ||
22 | "You" (or "Your") shall mean an individual or Legal Entity | |
23 | exercising permissions granted by this License. | |
24 | ||
25 | "Source" form shall mean the preferred form for making modifications, | |
26 | including but not limited to software source code, documentation | |
27 | source, and configuration files. | |
28 | ||
29 | "Object" form shall mean any form resulting from mechanical | |
30 | transformation or translation of a Source form, including but | |
31 | not limited to compiled object code, generated documentation, | |
32 | and conversions to other media types. | |
33 | ||
34 | "Work" shall mean the work of authorship, whether in Source or | |
35 | Object form, made available under the License, as indicated by a | |
36 | copyright notice that is included in or attached to the work | |
37 | (an example is provided in the Appendix below). | |
38 | ||
39 | "Derivative Works" shall mean any work, whether in Source or Object | |
40 | form, that is based on (or derived from) the Work and for which the | |
41 | editorial revisions, annotations, elaborations, or other modifications | |
42 | represent, as a whole, an original work of authorship. For the purposes | |
43 | of this License, Derivative Works shall not include works that remain | |
44 | separable from, or merely link (or bind by name) to the interfaces of, | |
45 | the Work and Derivative Works thereof. | |
46 | ||
47 | "Contribution" shall mean any work of authorship, including | |
48 | the original version of the Work and any modifications or additions | |
49 | to that Work or Derivative Works thereof, that is intentionally | |
50 | submitted to Licensor for inclusion in the Work by the copyright owner | |
51 | or by an individual or Legal Entity authorized to submit on behalf of | |
52 | the copyright owner. For the purposes of this definition, "submitted" | |
53 | means any form of electronic, verbal, or written communication sent | |
54 | to the Licensor or its representatives, including but not limited to | |
55 | communication on electronic mailing lists, source code control systems, | |
56 | and issue tracking systems that are managed by, or on behalf of, the | |
57 | Licensor for the purpose of discussing and improving the Work, but | |
58 | excluding communication that is conspicuously marked or otherwise | |
59 | designated in writing by the copyright owner as "Not a Contribution." | |
60 | ||
61 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
62 | on behalf of whom a Contribution has been received by Licensor and | |
63 | subsequently incorporated within the Work. | |
64 | ||
65 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
66 | this License, each Contributor hereby grants to You a perpetual, | |
67 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
68 | copyright license to reproduce, prepare Derivative Works of, | |
69 | publicly display, publicly perform, sublicense, and distribute the | |
70 | Work and such Derivative Works in Source or Object form. | |
71 | ||
72 | 3. Grant of Patent License. Subject to the terms and conditions of | |
73 | this License, each Contributor hereby grants to You a perpetual, | |
74 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
75 | (except as stated in this section) patent license to make, have made, | |
76 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
77 | where such license applies only to those patent claims licensable | |
78 | by such Contributor that are necessarily infringed by their | |
79 | Contribution(s) alone or by combination of their Contribution(s) | |
80 | with the Work to which such Contribution(s) was submitted. If You | |
81 | institute patent litigation against any entity (including a | |
82 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
83 | or a Contribution incorporated within the Work constitutes direct | |
84 | or contributory patent infringement, then any patent licenses | |
85 | granted to You under this License for that Work shall terminate | |
86 | as of the date such litigation is filed. | |
87 | ||
88 | 4. Redistribution. You may reproduce and distribute copies of the | |
89 | Work or Derivative Works thereof in any medium, with or without | |
90 | modifications, and in Source or Object form, provided that You | |
91 | meet the following conditions: | |
92 | ||
93 | (a) You must give any other recipients of the Work or | |
94 | Derivative Works a copy of this License; and | |
95 | ||
96 | (b) You must cause any modified files to carry prominent notices | |
97 | stating that You changed the files; and | |
98 | ||
99 | (c) You must retain, in the Source form of any Derivative Works | |
100 | that You distribute, all copyright, patent, trademark, and | |
101 | attribution notices from the Source form of the Work, | |
102 | excluding those notices that do not pertain to any part of | |
103 | the Derivative Works; and | |
104 | ||
105 | (d) If the Work includes a "NOTICE" text file as part of its | |
106 | distribution, then any Derivative Works that You distribute must | |
107 | include a readable copy of the attribution notices contained | |
108 | within such NOTICE file, excluding those notices that do not | |
109 | pertain to any part of the Derivative Works, in at least one | |
110 | of the following places: within a NOTICE text file distributed | |
111 | as part of the Derivative Works; within the Source form or | |
112 | documentation, if provided along with the Derivative Works; or, | |
113 | within a display generated by the Derivative Works, if and | |
114 | wherever such third-party notices normally appear. The contents | |
115 | of the NOTICE file are for informational purposes only and | |
116 | do not modify the License. You may add Your own attribution | |
117 | notices within Derivative Works that You distribute, alongside | |
118 | or as an addendum to the NOTICE text from the Work, provided | |
119 | that such additional attribution notices cannot be construed | |
120 | as modifying the License. | |
121 | ||
122 | You may add Your own copyright statement to Your modifications and | |
123 | may provide additional or different license terms and conditions | |
124 | for use, reproduction, or distribution of Your modifications, or | |
125 | for any such Derivative Works as a whole, provided Your use, | |
126 | reproduction, and distribution of the Work otherwise complies with | |
127 | the conditions stated in this License. | |
128 | ||
129 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
130 | any Contribution intentionally submitted for inclusion in the Work | |
131 | by You to the Licensor shall be under the terms and conditions of | |
132 | this License, without any additional terms or conditions. | |
133 | Notwithstanding the above, nothing herein shall supersede or modify | |
134 | the terms of any separate license agreement you may have executed | |
135 | with Licensor regarding such Contributions. | |
136 | ||
137 | 6. Trademarks. This License does not grant permission to use the trade | |
138 | names, trademarks, service marks, or product names of the Licensor, | |
139 | except as required for reasonable and customary use in describing the | |
140 | origin of the Work and reproducing the content of the NOTICE file. | |
141 | ||
142 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
143 | agreed to in writing, Licensor provides the Work (and each | |
144 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
145 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
146 | implied, including, without limitation, any warranties or conditions | |
147 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
148 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
149 | appropriateness of using or redistributing the Work and assume any | |
150 | risks associated with Your exercise of permissions under this License. | |
151 | ||
152 | 8. Limitation of Liability. In no event and under no legal theory, | |
153 | whether in tort (including negligence), contract, or otherwise, | |
154 | unless required by applicable law (such as deliberate and grossly | |
155 | negligent acts) or agreed to in writing, shall any Contributor be | |
156 | liable to You for damages, including any direct, indirect, special, | |
157 | incidental, or consequential damages of any character arising as a | |
158 | result of this License or out of the use or inability to use the | |
159 | Work (including but not limited to damages for loss of goodwill, | |
160 | work stoppage, computer failure or malfunction, or any and all | |
161 | other commercial damages or losses), even if such Contributor | |
162 | has been advised of the possibility of such damages. | |
163 | ||
164 | 9. Accepting Warranty or Additional Liability. While redistributing | |
165 | the Work or Derivative Works thereof, You may choose to offer, | |
166 | and charge a fee for, acceptance of support, warranty, indemnity, | |
167 | or other liability obligations and/or rights consistent with this | |
168 | License. However, in accepting such obligations, You may act only | |
169 | on Your own behalf and on Your sole responsibility, not on behalf | |
170 | of any other Contributor, and only if You agree to indemnify, | |
171 | defend, and hold each Contributor harmless for any liability | |
172 | incurred by, or claims asserted against, such Contributor by reason | |
173 | of your accepting any such warranty or additional liability. | |
174 | ||
175 | END OF TERMS AND CONDITIONS | |
176 | ||
177 | APPENDIX: How to apply the Apache License to your work. | |
178 | ||
179 | To apply the Apache License to your work, attach the following | |
180 | boilerplate notice, with the fields enclosed by brackets "{}" | |
181 | replaced with your own identifying information. (Don't include | |
182 | the brackets!) The text should be enclosed in the appropriate | |
183 | comment syntax for the file format. We also recommend that a | |
184 | file or class name and description of purpose be included on the | |
185 | same "printed page" as the copyright notice for easier | |
186 | identification within third-party archives. | |
187 | ||
188 | Copyright {yyyy} {name of copyright owner} | |
189 | ||
190 | Licensed under the Apache License, Version 2.0 (the "License"); | |
191 | you may not use this file except in compliance with the License. | |
192 | You may obtain a copy of the License at | |
193 | ||
194 | http://www.apache.org/licenses/LICENSE-2.0 | |
195 | ||
196 | Unless required by applicable law or agreed to in writing, software | |
197 | distributed under the License is distributed on an "AS IS" BASIS, | |
198 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
199 | See the License for the specific language governing permissions and | |
200 | limitations under the License. | |
201 |
0 | Gobuster v1.0 (OJ Reeves @TheColonial) | |
1 | ====================================== | |
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). | |
4 | ||
5 | ### Oh dear God.. WHY!? | |
6 | ||
7 | Because I wanted: | |
8 | ||
9 | 1. ... something that didn't have a fat Java GUI (console FTW). | |
10 | 1. ... to build something that just worked on the command line. | |
11 | 1. ... something that did not do recursive brute force. | |
12 | 1. ... something that allowed me to brute force folders and multiple extensions at once. | |
13 | 1. ... something that compiled to native on multiple platforms. | |
14 | 1. ... something that was faster than an interpreted script (such as Python). | |
15 | 1. ... something that didn't require a runtime. | |
16 | 1. ... use something that was good with concurrency (hence Go). | |
17 | 1. ... to build something in Go that wasn't totally useless. | |
18 | ||
19 | ### But it's shit! And your implementation sucks! | |
20 | ||
21 | Yes, you're probably correct. Feel free to : | |
22 | ||
23 | * Not use it. | |
24 | * Show me how to do it better. | |
25 | ||
26 | ### Common Command line options | |
27 | ||
28 | * `-m <mode>` - which mode to use, either `dir` or `dns` (default: `dir`) | |
29 | * `-u <url/domain>` - full URL (including scheme), or base domain name. | |
30 | * `-t <threads>` - number of threads to run (default: `10`). | |
31 | * `-w <wordlist>` - path to the wordlist used for brute forcing. | |
32 | * `-v` - verbose output (show all results). | |
33 | ||
34 | ### Command line options for `dns` mode | |
35 | ||
36 | * `-i` - show all IP addresses for the result. | |
37 | ||
38 | ### Command line options for `dir` mode | |
39 | ||
40 | * `-c <http cookies>` - use this to specify any cookies that you might need (simulating auth). | |
41 | * `-f` - append `/` for directory brute forces. | |
42 | * `-r` - follow redirects. | |
43 | * `-l` - show the length of the response. | |
44 | * `-n` - "no status" mode, disables the output of the result's status code. | |
45 | * `-q` - disables banner/underline output. | |
46 | * `-e` - expand the results to include the full URL. | |
47 | * `-s <status codes>` - comma-separated set of the list of status codes to be deemed a "positive" (default: `200,204,301,302,307`). | |
48 | * `-x <extensions>` - list of extensions to check for, if any. | |
49 | * `-p <proxy url>` - specify a proxy to use for all requests (scheme much match the URL scheme) | |
50 | ||
51 | ### Building | |
52 | ||
53 | 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. | |
54 | ||
55 | #### Compiling | |
56 | ``` | |
57 | gobuster$ go build | |
58 | ``` | |
59 | This will create a `gobuster` binary for you. | |
60 | #### Running as a script | |
61 | ``` | |
62 | gobuster$ go run main.go <parameters> | |
63 | ``` | |
64 | ||
65 | ### Examples | |
66 | ||
67 | #### `dir` mode | |
68 | ||
69 | Command line might look like this: | |
70 | ``` | |
71 | $ ./gobuster -u https://mysite.com/path/to/folder -c 'session=123456' -t 50 -w common-files.txt -x .php,.html | |
72 | ``` | |
73 | Default options looks like this: | |
74 | ``` | |
75 | $ ./gobuster -u http://buffered.io/ -w words.txt | |
76 | ||
77 | ===================================================== | |
78 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
79 | (DNS support by Peleus @0x42424242) | |
80 | ===================================================== | |
81 | [+] Mode : dir | |
82 | [+] Url/Domain : http://buffered.io/ | |
83 | [+] Threads : 10 | |
84 | [+] Wordlist : words.txt | |
85 | [+] Status codes : 200,204,301,302,307 | |
86 | ===================================================== | |
87 | /index (Status: 200) | |
88 | /posts (Status: 301) | |
89 | /contact (Status: 301) | |
90 | ===================================================== | |
91 | ``` | |
92 | Default options with status codes disabled looks like this: | |
93 | ``` | |
94 | $ ./gobuster -u http://buffered.io/ -w words.txt -n | |
95 | ||
96 | ===================================================== | |
97 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
98 | (DNS support by Peleus @0x42424242) | |
99 | ===================================================== | |
100 | [+] Mode : dir | |
101 | [+] Url/Domain : http://buffered.io/ | |
102 | [+] Threads : 10 | |
103 | [+] Wordlist : words.txt | |
104 | [+] Status codes : 200,204,301,302,307 | |
105 | [+] No status : true | |
106 | ===================================================== | |
107 | /index | |
108 | /posts | |
109 | /contact | |
110 | ===================================================== | |
111 | ``` | |
112 | Verbose output looks like this: | |
113 | ``` | |
114 | $ ./gobuster -u http://buffered.io/ -w words.txt -v | |
115 | ||
116 | ===================================================== | |
117 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
118 | (DNS support by Peleus @0x42424242) | |
119 | ===================================================== | |
120 | [+] Mode : dir | |
121 | [+] Url/Domain : http://buffered.io/ | |
122 | [+] Threads : 10 | |
123 | [+] Wordlist : words.txt | |
124 | [+] Status codes : 200,204,301,302,307 | |
125 | [+] Verbose : true | |
126 | ===================================================== | |
127 | Found : /index (Status: 200) | |
128 | Missed: /derp (Status: 404) | |
129 | Found : /posts (Status: 301) | |
130 | Found : /contact (Status: 301) | |
131 | ===================================================== | |
132 | ``` | |
133 | Example showing content length: | |
134 | ``` | |
135 | ===================================================== | |
136 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
137 | (DNS support by Peleus @0x42424242) | |
138 | ===================================================== | |
139 | [+] Mode : dir | |
140 | [+] Url/Domain : http://buffered.io/ | |
141 | [+] Threads : 10 | |
142 | [+] Wordlist : /tmp/words | |
143 | [+] Status codes : 301,302,307,200,204 | |
144 | ===================================================== | |
145 | /contact (Status: 301) | |
146 | /posts (Status: 301) | |
147 | /index (Status: 200) [Size: 61481] | |
148 | ===================================================== | |
149 | ``` | |
150 | Quiet output, with status disabled and expanded mode looks like this ("grep mode"): | |
151 | ``` | |
152 | $ ./gobuster -u http://buffered.io/ -w words.txt -q -n -e | |
153 | http://buffered.io/posts | |
154 | http://buffered.io/contact | |
155 | http://buffered.io/index | |
156 | ``` | |
157 | ||
158 | #### `dns` mode | |
159 | ||
160 | Command line might look like this: | |
161 | ``` | |
162 | $ ./gobuster -m dns -u mysite.com -t 50 -w common-names.txt | |
163 | ``` | |
164 | Normal sample run goes like this: | |
165 | ``` | |
166 | $ ./gobuster -m dns -w subdomains.txt -u google.com | |
167 | ||
168 | ===================================================== | |
169 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
170 | (DNS support by Peleus @0x42424242) | |
171 | ===================================================== | |
172 | [+] Mode : dns | |
173 | [+] Url/Domain : google.com | |
174 | [+] Threads : 10 | |
175 | [+] Wordlist : subdomains.txt | |
176 | ===================================================== | |
177 | Found: m.google.com | |
178 | Found: admin.google.com | |
179 | Found: mobile.google.com | |
180 | Found: www.google.com | |
181 | Found: search.google.com | |
182 | Found: chrome.google.com | |
183 | Found: ns1.google.com | |
184 | Found: store.google.com | |
185 | Found: wap.google.com | |
186 | Found: support.google.com | |
187 | Found: directory.google.com | |
188 | Found: translate.google.com | |
189 | Found: news.google.com | |
190 | Found: music.google.com | |
191 | Found: mail.google.com | |
192 | Found: blog.google.com | |
193 | Found: cse.google.com | |
194 | Found: local.google.com | |
195 | ===================================================== | |
196 | ``` | |
197 | Show IP sample run goes like this: | |
198 | ``` | |
199 | $ ./gobuster -m dns -w subdomains.txt -u google.com -i | |
200 | ||
201 | ===================================================== | |
202 | Gobuster v1.0 (DIR support by OJ Reeves @TheColonial) | |
203 | (DNS support by Peleus @0x42424242) | |
204 | ===================================================== | |
205 | [+] Mode : dns | |
206 | [+] Url/Domain : google.com | |
207 | [+] Threads : 10 | |
208 | [+] Wordlist : subdomains.txt | |
209 | [+] Verbose : true | |
210 | ===================================================== | |
211 | Found: chrome.google.com [2404:6800:4006:801::200e, 216.58.220.110] | |
212 | Found: m.google.com [216.58.220.107, 2404:6800:4006:801::200b] | |
213 | Found: www.google.com [74.125.237.179, 74.125.237.177, 74.125.237.178, 74.125.237.180, 74.125.237.176, 2404:6800:4006:801::2004] | |
214 | Found: search.google.com [2404:6800:4006:801::200e, 216.58.220.110] | |
215 | Found: admin.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
216 | Found: store.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
217 | Found: mobile.google.com [216.58.220.107, 2404:6800:4006:801::200b] | |
218 | Found: ns1.google.com [216.239.32.10] | |
219 | Found: directory.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
220 | Found: translate.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
221 | Found: cse.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
222 | Found: local.google.com [2404:6800:4006:801::200e, 216.58.220.110] | |
223 | Found: music.google.com [2404:6800:4006:801::200e, 216.58.220.110] | |
224 | Found: wap.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
225 | Found: blog.google.com [216.58.220.105, 2404:6800:4006:801::2009] | |
226 | Found: support.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
227 | Found: news.google.com [216.58.220.110, 2404:6800:4006:801::200e] | |
228 | Found: mail.google.com [216.58.220.101, 2404:6800:4006:801::2005] | |
229 | ===================================================== | |
230 | ``` | |
231 | ||
232 | ### License | |
233 | ||
234 | See the LICENSE file. | |
235 | ||
236 | ### Thanks | |
237 | ||
238 | See the THANKS file. |
0 | package main | |
1 | ||
2 | //---------------------------------------------------- | |
3 | // Gobuster -- by OJ Reeves | |
4 | // | |
5 | // A crap attempt at building something that resembles | |
6 | // dirbuster or dirb using Go. The goal was to build | |
7 | // a tool that would help learn Go and to actually do | |
8 | // something useful. The idea of having this compile | |
9 | // to native code is also appealing. | |
10 | // | |
11 | // Run: gobuster -h | |
12 | //---------------------------------------------------- | |
13 | ||
14 | import ( | |
15 | "bufio" | |
16 | "crypto/tls" | |
17 | "flag" | |
18 | "fmt" | |
19 | "io/ioutil" | |
20 | "net" | |
21 | "net/http" | |
22 | "net/url" | |
23 | "os" | |
24 | "strconv" | |
25 | "strings" | |
26 | "sync" | |
27 | "unicode/utf8" | |
28 | ) | |
29 | ||
30 | // A single result which comes from an individual web | |
31 | // request. | |
32 | type Result struct { | |
33 | Entity string | |
34 | Status int | |
35 | Extra string | |
36 | Size *int64 | |
37 | } | |
38 | ||
39 | type PrintResultFunc func(s *State, r *Result) | |
40 | type ProcessorFunc func(s *State, entity string, resultChan chan<- Result) | |
41 | type SetupFunc func(s *State) bool | |
42 | ||
43 | // Shim type for "set" | |
44 | type IntSet struct { | |
45 | set map[int]bool | |
46 | } | |
47 | ||
48 | // Contains State that are read in from the command | |
49 | // line when the program is invoked. | |
50 | type State struct { | |
51 | Threads int | |
52 | Wordlist string | |
53 | Url string | |
54 | Cookies string | |
55 | Extensions []string | |
56 | StatusCodes IntSet | |
57 | Verbose bool | |
58 | UseSlash bool | |
59 | FollowRedirect bool | |
60 | IncludeLength bool | |
61 | ShowIPs bool | |
62 | Quiet bool | |
63 | NoStatus bool | |
64 | Expanded bool | |
65 | Mode string | |
66 | ProxyUrl *url.URL | |
67 | Setup SetupFunc | |
68 | Printer PrintResultFunc | |
69 | Processor ProcessorFunc | |
70 | Client *http.Client | |
71 | } | |
72 | ||
73 | type RedirectHandler struct { | |
74 | Transport http.RoundTripper | |
75 | State *State | |
76 | } | |
77 | ||
78 | type RedirectError struct { | |
79 | StatusCode int | |
80 | } | |
81 | ||
82 | // Add an element to a set | |
83 | func (set *IntSet) Add(i int) bool { | |
84 | _, found := set.set[i] | |
85 | set.set[i] = true | |
86 | return !found | |
87 | } | |
88 | ||
89 | // Test if an element is in a set | |
90 | func (set *IntSet) Contains(i int) bool { | |
91 | _, found := set.set[i] | |
92 | return found | |
93 | } | |
94 | ||
95 | // Stringify the set | |
96 | func (set *IntSet) Stringify() string { | |
97 | values := []string{} | |
98 | for s, _ := range set.set { | |
99 | values = append(values, strconv.Itoa(s)) | |
100 | } | |
101 | return strings.Join(values, ",") | |
102 | } | |
103 | ||
104 | // Make a request to the given URL. | |
105 | func MakeRequest(s *State, fullUrl, cookie string) (*int, *int64) { | |
106 | req, err := http.NewRequest("GET", fullUrl, nil) | |
107 | ||
108 | if err != nil { | |
109 | return nil, nil | |
110 | } | |
111 | ||
112 | if cookie != "" { | |
113 | req.Header.Set("Cookie", cookie) | |
114 | } | |
115 | ||
116 | resp, err := s.Client.Do(req) | |
117 | ||
118 | if err != nil { | |
119 | if ue, ok := err.(*url.Error); ok { | |
120 | if re, ok := ue.Err.(*RedirectError); ok { | |
121 | return &re.StatusCode, nil | |
122 | } | |
123 | } | |
124 | return nil, nil | |
125 | } | |
126 | ||
127 | defer resp.Body.Close() | |
128 | ||
129 | var length *int64 = nil | |
130 | ||
131 | if s.IncludeLength { | |
132 | length = new(int64) | |
133 | if resp.ContentLength <= 0 { | |
134 | body, err := ioutil.ReadAll(resp.Body) | |
135 | if err == nil { | |
136 | *length = int64(utf8.RuneCountInString(string(body))) | |
137 | } | |
138 | } else { | |
139 | *length = resp.ContentLength | |
140 | } | |
141 | } | |
142 | ||
143 | return &resp.StatusCode, length | |
144 | } | |
145 | ||
146 | // Small helper to combine URL with URI then make a | |
147 | // request to the generated location. | |
148 | func GoGet(s *State, url, uri, cookie string) (*int, *int64) { | |
149 | return MakeRequest(s, url+uri, cookie) | |
150 | } | |
151 | ||
152 | // Parse all the command line options into a settings | |
153 | // instance for future use. | |
154 | func ParseCmdLine() *State { | |
155 | var extensions string | |
156 | var codes string | |
157 | var proxy string | |
158 | valid := true | |
159 | ||
160 | s := State{StatusCodes: IntSet{set: map[int]bool{}}} | |
161 | ||
162 | // Set up the variables we're interested in parsing. | |
163 | flag.IntVar(&s.Threads, "t", 10, "Number of concurrent threads") | |
164 | flag.StringVar(&s.Mode, "m", "dir", "Directory/File mode (dir) or DNS mode (dns)") | |
165 | flag.StringVar(&s.Wordlist, "w", "", "Path to the wordlist") | |
166 | flag.StringVar(&codes, "s", "200,204,301,302,307", "Positive status codes (dir mode only)") | |
167 | flag.StringVar(&s.Url, "u", "", "The target URL or Domain") | |
168 | flag.StringVar(&s.Cookies, "c", "", "Cookies to use for the requests (dir mode only)") | |
169 | flag.StringVar(&extensions, "x", "", "File extension(s) to search for (dir mode only)") | |
170 | flag.StringVar(&proxy, "p", "", "Proxy to use for requests [http(s)://host:port] (dir mode only)") | |
171 | flag.BoolVar(&s.Verbose, "v", false, "Verbose output (errors)") | |
172 | flag.BoolVar(&s.ShowIPs, "i", false, "Show IP addresses (dns mode only)") | |
173 | flag.BoolVar(&s.FollowRedirect, "r", false, "Follow redirects") | |
174 | flag.BoolVar(&s.Quiet, "q", false, "Don't print the banner") | |
175 | flag.BoolVar(&s.Expanded, "e", false, "Expanded mode, print full URLs") | |
176 | flag.BoolVar(&s.NoStatus, "n", false, "Don't print status codes") | |
177 | flag.BoolVar(&s.IncludeLength, "l", false, "Include the length of the body in the output (dir mode only)") | |
178 | flag.BoolVar(&s.UseSlash, "f", false, "Append a forward-slash to each directory request (dir mode only)") | |
179 | ||
180 | flag.Parse() | |
181 | ||
182 | switch strings.ToLower(s.Mode) { | |
183 | case "dir": | |
184 | s.Printer = PrintDirResult | |
185 | s.Processor = ProcessDirEntry | |
186 | s.Setup = SetupDir | |
187 | case "dns": | |
188 | s.Printer = PrintDnsResult | |
189 | s.Processor = ProcessDnsEntry | |
190 | s.Setup = SetupDns | |
191 | default: | |
192 | fmt.Println("Mode (-m): Invalid value:", s.Mode) | |
193 | valid = false | |
194 | } | |
195 | ||
196 | if s.Threads < 0 { | |
197 | fmt.Println("Threads (-t): Invalid value:", s.Threads) | |
198 | valid = false | |
199 | } | |
200 | ||
201 | if s.Wordlist == "" { | |
202 | fmt.Println("WordList (-w): Must be specified") | |
203 | valid = false | |
204 | } else if _, err := os.Stat(s.Wordlist); os.IsNotExist(err) { | |
205 | fmt.Println("Wordlist (-w): File does not exist:", s.Wordlist) | |
206 | valid = false | |
207 | } | |
208 | ||
209 | if s.Url == "" { | |
210 | fmt.Println("Url/Domain (-u): Must be specified") | |
211 | valid = false | |
212 | } | |
213 | ||
214 | if s.Mode == "dir" { | |
215 | if strings.HasSuffix(s.Url, "/") == false { | |
216 | s.Url = s.Url + "/" | |
217 | } | |
218 | ||
219 | if strings.HasPrefix(s.Url, "http") == false { | |
220 | s.Url = "http://" + s.Url | |
221 | } | |
222 | ||
223 | // extensions are comma seaprated | |
224 | if extensions != "" { | |
225 | s.Extensions = strings.Split(extensions, ",") | |
226 | for i := range s.Extensions { | |
227 | if s.Extensions[i][0] != '.' { | |
228 | s.Extensions[i] = "." + s.Extensions[i] | |
229 | } | |
230 | } | |
231 | } | |
232 | ||
233 | // status codes are comma seaprated | |
234 | if codes != "" { | |
235 | for _, c := range strings.Split(codes, ",") { | |
236 | i, err := strconv.Atoi(c) | |
237 | if err != nil { | |
238 | panic("Invalid status code given") | |
239 | } | |
240 | s.StatusCodes.Add(i) | |
241 | } | |
242 | } | |
243 | ||
244 | if valid { | |
245 | var proxyUrlFunc func(*http.Request) (*url.URL, error) | |
246 | proxyUrlFunc = http.ProxyFromEnvironment | |
247 | ||
248 | if proxy != "" { | |
249 | proxyUrl, err := url.Parse(proxy) | |
250 | if err != nil { | |
251 | panic("Proxy URL is invalid") | |
252 | } | |
253 | s.ProxyUrl = proxyUrl | |
254 | proxyUrlFunc = http.ProxyURL(s.ProxyUrl) | |
255 | } | |
256 | ||
257 | s.Client = &http.Client{ | |
258 | Transport: &RedirectHandler{ | |
259 | State: &s, | |
260 | Transport: &http.Transport{ | |
261 | Proxy: proxyUrlFunc, | |
262 | TLSClientConfig: &tls.Config{ | |
263 | InsecureSkipVerify: true, | |
264 | }, | |
265 | }, | |
266 | }} | |
267 | ||
268 | code, _ := GoGet(&s, s.Url, "", s.Cookies) | |
269 | if code == nil { | |
270 | fmt.Println("[-] Unable to connect:", s.Url) | |
271 | valid = false | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | if valid { | |
277 | return &s | |
278 | } | |
279 | ||
280 | return nil | |
281 | } | |
282 | ||
283 | // Process the busting of the website with the given | |
284 | // set of settings from the command line. | |
285 | func Process(s *State) { | |
286 | wordlist, err := os.Open(s.Wordlist) | |
287 | if err != nil { | |
288 | panic("Failed to open wordlist") | |
289 | } | |
290 | ||
291 | Banner(s) | |
292 | ||
293 | if s.Setup(s) == false { | |
294 | Ruler(s) | |
295 | return | |
296 | } | |
297 | ||
298 | // channels used for comms | |
299 | wordChan := make(chan string, s.Threads) | |
300 | resultChan := make(chan Result) | |
301 | ||
302 | // Use a wait group for waiting for all threads | |
303 | // to finish | |
304 | processorGroup := new(sync.WaitGroup) | |
305 | processorGroup.Add(s.Threads) | |
306 | printerGroup := new(sync.WaitGroup) | |
307 | printerGroup.Add(1) | |
308 | ||
309 | // Create goroutines for each of the number of threads | |
310 | // specified. | |
311 | for i := 0; i < s.Threads; i++ { | |
312 | go func() { | |
313 | for { | |
314 | word := <-wordChan | |
315 | ||
316 | // Did we reach the end? If so break. | |
317 | if word == "" { | |
318 | break | |
319 | } | |
320 | ||
321 | // Mode-specific processing | |
322 | s.Processor(s, word, resultChan) | |
323 | } | |
324 | ||
325 | // Indicate to the wait group that the thread | |
326 | // has finished. | |
327 | processorGroup.Done() | |
328 | }() | |
329 | } | |
330 | ||
331 | // Single goroutine which handles the results as they | |
332 | // appear from the worker threads. | |
333 | go func() { | |
334 | for r := range resultChan { | |
335 | s.Printer(s, &r) | |
336 | } | |
337 | printerGroup.Done() | |
338 | }() | |
339 | ||
340 | defer wordlist.Close() | |
341 | ||
342 | // Lazy reading of the wordlist line by line | |
343 | scanner := bufio.NewScanner(wordlist) | |
344 | for scanner.Scan() { | |
345 | word := scanner.Text() | |
346 | ||
347 | // Skip "comment" lines | |
348 | if strings.HasPrefix(word, "#") == false { | |
349 | wordChan <- word | |
350 | } | |
351 | } | |
352 | ||
353 | close(wordChan) | |
354 | processorGroup.Wait() | |
355 | close(resultChan) | |
356 | printerGroup.Wait() | |
357 | Ruler(s) | |
358 | } | |
359 | ||
360 | func SetupDns(s *State) bool { | |
361 | // Resolve a subdomain that probably shouldn't exist | |
362 | _, err := net.LookupHost("bba1b18d-50f8-4f1d-8295-c861445ed7f5." + s.Url) | |
363 | if err == nil { | |
364 | fmt.Println("Wildcard DNS found.") | |
365 | return false | |
366 | } | |
367 | return true | |
368 | } | |
369 | ||
370 | func SetupDir(s *State) bool { | |
371 | return true | |
372 | } | |
373 | ||
374 | func ProcessDnsEntry(s *State, word string, resultChan chan<- Result) { | |
375 | subdomain := word + "." + s.Url | |
376 | ips, err := net.LookupHost(subdomain) | |
377 | ||
378 | if err == nil { | |
379 | result := Result{ | |
380 | Entity: subdomain, | |
381 | } | |
382 | if s.ShowIPs { | |
383 | result.Extra = strings.Join(ips, ", ") | |
384 | } | |
385 | resultChan <- result | |
386 | } else if s.Verbose { | |
387 | result := Result{ | |
388 | Entity: subdomain, | |
389 | Status: 404, | |
390 | } | |
391 | resultChan <- result | |
392 | } | |
393 | } | |
394 | ||
395 | func ProcessDirEntry(s *State, word string, resultChan chan<- Result) { | |
396 | suffix := "" | |
397 | if s.UseSlash { | |
398 | suffix = "/" | |
399 | } | |
400 | ||
401 | // Try the DIR first | |
402 | dirResp, dirSize := GoGet(s, s.Url, word+suffix, s.Cookies) | |
403 | if dirResp != nil { | |
404 | resultChan <- Result{ | |
405 | Entity: word + suffix, | |
406 | Status: *dirResp, | |
407 | Size: dirSize, | |
408 | } | |
409 | } | |
410 | ||
411 | // Follow up with files using each ext. | |
412 | for ext := range s.Extensions { | |
413 | file := word + s.Extensions[ext] | |
414 | fileResp, fileSize := GoGet(s, s.Url, file, s.Cookies) | |
415 | ||
416 | if fileResp != nil { | |
417 | resultChan <- Result{ | |
418 | Entity: file, | |
419 | Status: *fileResp, | |
420 | Size: fileSize, | |
421 | } | |
422 | } | |
423 | } | |
424 | } | |
425 | ||
426 | func PrintDnsResult(s *State, r *Result) { | |
427 | if r.Status == 404 { | |
428 | fmt.Printf("Missing: %s\n", r.Entity) | |
429 | } else if s.ShowIPs { | |
430 | fmt.Printf("Found: %s [%s]\n", r.Entity, r.Extra) | |
431 | } else { | |
432 | fmt.Printf("Found: %s\n", r.Entity) | |
433 | } | |
434 | } | |
435 | ||
436 | func PrintDirResult(s *State, r *Result) { | |
437 | output := "" | |
438 | ||
439 | // Prefix if we're in verbose mode | |
440 | if s.Verbose { | |
441 | if s.StatusCodes.Contains(r.Status) { | |
442 | output += "Found : " | |
443 | } else { | |
444 | output += "Missed: " | |
445 | } | |
446 | } | |
447 | ||
448 | if s.StatusCodes.Contains(r.Status) || s.Verbose { | |
449 | if s.Expanded { | |
450 | output += s.Url | |
451 | } else { | |
452 | output += "/" | |
453 | } | |
454 | output += r.Entity | |
455 | ||
456 | if !s.NoStatus { | |
457 | output += fmt.Sprintf(" (Status: %d)", r.Status) | |
458 | } | |
459 | ||
460 | if r.Size != nil { | |
461 | output += fmt.Sprintf(" [Size: %d]", *r.Size) | |
462 | } | |
463 | ||
464 | fmt.Println(output) | |
465 | } | |
466 | } | |
467 | ||
468 | func (e *RedirectError) Error() string { | |
469 | return fmt.Sprintf("Redirect code: %d", e.StatusCode) | |
470 | } | |
471 | ||
472 | func (rh *RedirectHandler) RoundTrip(req *http.Request) (resp *http.Response, err error) { | |
473 | if rh.State.FollowRedirect { | |
474 | return rh.Transport.RoundTrip(req) | |
475 | } | |
476 | ||
477 | resp, err = rh.Transport.RoundTrip(req) | |
478 | if err != nil { | |
479 | return resp, err | |
480 | } | |
481 | ||
482 | switch resp.StatusCode { | |
483 | case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, | |
484 | http.StatusNotModified, http.StatusUseProxy, http.StatusTemporaryRedirect: | |
485 | return nil, &RedirectError{StatusCode: resp.StatusCode} | |
486 | } | |
487 | ||
488 | return resp, err | |
489 | } | |
490 | ||
491 | func Ruler(s *State) { | |
492 | if !s.Quiet { | |
493 | fmt.Println("=====================================================") | |
494 | } | |
495 | } | |
496 | ||
497 | func Banner(state *State) { | |
498 | if state.Quiet { | |
499 | return | |
500 | } | |
501 | ||
502 | fmt.Println("") | |
503 | Ruler(state) | |
504 | fmt.Println("Gobuster v1.0 (DIR support by OJ Reeves @TheColonial)") | |
505 | fmt.Println(" (DNS support by Peleus @0x42424242)") | |
506 | Ruler(state) | |
507 | ||
508 | if state != nil { | |
509 | fmt.Printf("[+] Mode : %s\n", state.Mode) | |
510 | fmt.Printf("[+] Url/Domain : %s\n", state.Url) | |
511 | fmt.Printf("[+] Threads : %d\n", state.Threads) | |
512 | fmt.Printf("[+] Wordlist : %s\n", state.Wordlist) | |
513 | ||
514 | if state.Mode == "dir" { | |
515 | fmt.Printf("[+] Status codes : %s\n", state.StatusCodes.Stringify()) | |
516 | ||
517 | if state.ProxyUrl != nil { | |
518 | fmt.Printf("[+] Proxy : %s\n", state.ProxyUrl) | |
519 | } | |
520 | ||
521 | if state.Cookies != "" { | |
522 | fmt.Printf("[+] Cookies : %s\n", state.Cookies) | |
523 | } | |
524 | ||
525 | if len(state.Extensions) > 0 { | |
526 | fmt.Printf("[+] Extensions : %s\n", strings.Join(state.Extensions, ",")) | |
527 | } | |
528 | ||
529 | if state.UseSlash { | |
530 | fmt.Printf("[+] Add Slash : true\n") | |
531 | } | |
532 | ||
533 | if state.FollowRedirect { | |
534 | fmt.Printf("[+] Follow Redir : true\n") | |
535 | } | |
536 | ||
537 | if state.Expanded { | |
538 | fmt.Printf("[+] Expanded : true\n") | |
539 | } | |
540 | ||
541 | if state.NoStatus { | |
542 | fmt.Printf("[+] No status : true\n") | |
543 | } | |
544 | ||
545 | if state.Verbose { | |
546 | fmt.Printf("[+] Verbose : true\n") | |
547 | } | |
548 | } | |
549 | ||
550 | Ruler(state) | |
551 | } | |
552 | } | |
553 | ||
554 | func main() { | |
555 | state := ParseCmdLine() | |
556 | if state != nil { | |
557 | Process(state) | |
558 | } | |
559 | } |