Codebase list gobuster / 67143e2
Update upstream source from tag 'upstream/3.2.0' Update to upstream version '3.2.0' with Debian dir 23063647592eda59bda998e101421f1a60ce2ab9 Sophie Brun 1 year, 7 months ago
60 changed file(s) with 2074 addition(s) and 1504 deletion(s). Raw diff Collapse all Expand all
00 # These are supported funding model platforms
11
2 github: OJ
2 github: [OJ, firefart]
33 patreon: OJReeves
44 open_collective: gobuster
55 ko_fi: OJReeves
0 # To get started with Dependabot version updates, you'll need to specify which
1 # package ecosystems to update and where the package manifests are located.
2 # Please see the documentation for all configuration options:
3 # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
4
5 version: 2
6 updates:
7 - package-ecosystem: "gomod"
8 directory: "/"
9 schedule:
10 interval: "weekly"
11
12 - package-ecosystem: "github-actions"
13 directory: "/"
14 schedule:
15 # Check for updates to GitHub Actions every weekday
16 interval: "daily"
55 runs-on: ubuntu-latest
66 strategy:
77 matrix:
8 go: ["1.13.10", "1.14.2"]
8 go: ["1.18", "1.19"]
99 steps:
1010 - name: Set up Go ${{ matrix.go }}
11 uses: actions/setup-go@v1
11 uses: actions/setup-go@v3
1212 with:
1313 go-version: ${{ matrix.go }}
14 id: go
1514
16 - name: Check out code into the Go module directory
17 uses: actions/checkout@v2
15 - name: Check out code
16 uses: actions/[email protected]
17
18 - name: build cache
19 uses: actions/cache@v3
20 with:
21 path: ~/go/pkg/mod
22 key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
23 restore-keys: |
24 ${{ runner.os }}-go-
1825
1926 - name: Get dependencies
2027 run: |
2128 go get -v -t -d ./...
22 if [ -f Gopkg.toml ]; then
23 curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
24 dep ensure
25 fi
2629
27 - name: Build
28 run: go build -v .
30 - name: Build linux
31 run: make linux
32
33 - name: Build windows
34 run: make windows
2935
3036 - name: Test
3137 run: make test
32
33 - name: Lint
34 run: make lint
0 name: golangci-lint
1 on: [push, pull_request]
2 jobs:
3 golangci:
4 name: lint
5 runs-on: ubuntu-latest
6 steps:
7 - uses: actions/[email protected]
8
9 - uses: actions/setup-go@v3
10 with:
11 go-version: "^1.19"
12
13 - name: golangci-lint
14 uses: golangci/golangci-lint-action@v3
15 with:
16 version: latest
0 name: goreleaser
1
2 on:
3 push:
4 tags:
5 - "*"
6
7 permissions:
8 contents: write
9
10 jobs:
11 goreleaser:
12 runs-on: ubuntu-latest
13 steps:
14 - name: Checkout
15 uses: actions/[email protected]
16 with:
17 fetch-depth: 0
18 - name: Fetch all tags
19 run: git fetch --force --tags
20 - name: Set up Go
21 uses: actions/setup-go@v3
22 with:
23 go-version: 1.19
24 - name: Run GoReleaser
25 uses: goreleaser/goreleaser-action@v3
26 with:
27 distribution: goreleaser
28 version: latest
29 args: release --rm-dist
30 env:
31 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
0 # Compiled Object files, Static and Dynamic libs (Shared Objects)
1 *.o
2 *.a
0 # Binaries for programs and plugins
1 *.exe
2 *.exe~
3 *.dll
34 *.so
5 *.dylib
46
5 # Folders
6 _obj
7 _test
7 # Test binary, built with `go test -c`
8 *.test
89
9 # Architecture specific extensions/prefixes
10 *.[568vq]
11 [568vq].out
10 # Output of the go coverage tool, specifically when used with LiteIDE
11 *.out
1212
13 *.cgo1.go
14 *.cgo2.c
15 _cgo_defun.c
16 _cgo_gotypes.go
17 _cgo_export.*
13 # Dependency directories (remove the comment below to include it)
14 # vendor/
1815
19 _testmain.go
20
21 *.exe
22 *.test
2316 *.prof
2417 *.txt
2518 *.swp
2720 .vscode/
2821 gobuster
2922 build
30 v3
3123
32 .idea/
24 dist/
00 linters:
1 enable-all: true
2 disable:
3 - wsl
4 - gocyclo
5 - gocognit
6 - funlen
7 - lll
8 - dogsled
9 - gomnd
10 - nestif
11 - testpackage
12 - godot
13 - goerr113
14 - gofumpt
15
16 issues:
17 exclude-rules:
18 - text: "TLS InsecureSkipVerify may be true"
19 linters:
20 - gosec
21
22 - text: ifElseChain
23 linters:
24 - gocritic
25
26 - path: cli\\cmd\\.+\.go
27 linters:
28 - gochecknoinits
29 - gochecknoglobals
30
31 - path: cli/cmd/.+\.go
32 linters:
33 - gochecknoinits
34 - gochecknoglobals
35
36 - path: helper/useragents.go
37 linters:
38 - gochecknoglobals
39
40 - path: _test\.go
41 linters:
42 - scopelint
1 enable:
2 - nonamedreturns
0 # This is an example .goreleaser.yml file with some sensible defaults.
1 # Make sure to check the documentation at https://goreleaser.com
2 before:
3 hooks:
4 # You may remove this if you don't use go modules.
5 - go mod tidy
6 # you may remove this if you don't need go generate
7 - go generate ./...
8 builds:
9 - env:
10 - CGO_ENABLED=0
11 goos:
12 - linux
13 - windows
14 - darwin
15 archives:
16 - format: tar.gz
17 format_overrides:
18 - goos: windows
19 format: zip
20 replacements:
21 darwin: Darwin
22 linux: Linux
23 windows: Windows
24 386: i386
25 amd64: x86_64
26 checksum:
27 name_template: "checksums.txt"
28 snapshot:
29 name_template: "{{ incpatch .Version }}-dev"
30 changelog:
31 sort: asc
32 filters:
33 exclude:
34 - "^docs:"
35 - "^test:"
00 FROM golang:latest AS build-env
11 WORKDIR /src
2 ENV GO111MODULE=on
2 ENV CGO_ENABLED=0
33 COPY go.mod /src/
44 RUN go mod download
55 COPY . .
6 RUN CGO_ENABLED=0 GOOS=linux go build -a -o gobuster -ldflags="-s -w" -gcflags="all=-trimpath=/src" -asmflags="all=-trimpath=/src"
6 RUN go build -a -o gobuster -ldflags="-s -w" -gcflags="all=-trimpath=/src" -asmflags="all=-trimpath=/src"
77
88 FROM alpine:latest
99
0 TARGET=./build
1 ARCHS=amd64 386
2 LDFLAGS="-s -w"
0 .DEFAULT_GOAL := linux
31
4 current:
5 @go build -o ./gobuster; \
6 echo "Done."
2 .PHONY: linux
3 linux:
4 go build -o ./gobuster
75
6 .PHONY: windows
7 windows:
8 GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o ./gobuster.exe
9
10 .PHONY: fmt
811 fmt:
9 @go fmt ./...; \
10 echo "Done."
12 go fmt ./...
1113
14 .PHONY: update
1215 update:
13 @go get -u; \
14 go mod tidy -v; \
15 echo "Done."
16 go get -u
17 go mod tidy -v
1618
17 windows:
18 @for GOARCH in ${ARCHS}; do \
19 echo "Building for windows $${GOARCH} ..." ; \
20 mkdir -p ${TARGET}/gobuster-windows-$${GOARCH} ; \
21 GOOS=windows GOARCH=$${GOARCH} GO111MODULE=on CGO_ENABLED=0 go build -ldflags=${LDFLAGS} -trimpath -o ${TARGET}/gobuster-windows-$${GOARCH}/gobuster.exe ; \
22 done; \
23 echo "Done."
19 .PHONY: all
20 all: fmt update linux windows test lint
2421
25 linux:
26 @for GOARCH in ${ARCHS}; do \
27 echo "Building for linux $${GOARCH} ..." ; \
28 mkdir -p ${TARGET}/gobuster-linux-$${GOARCH} ; \
29 GOOS=linux GOARCH=$${GOARCH} GO111MODULE=on CGO_ENABLED=0 go build -ldflags=${LDFLAGS} -trimpath -o ${TARGET}/gobuster-linux-$${GOARCH}/gobuster ; \
30 done; \
31 echo "Done."
22 .PHONY: test
23 test:
24 go test -v -race ./...
3225
33 darwin:
34 @for GOARCH in ${ARCHS}; do \
35 echo "Building for darwin $${GOARCH} ..." ; \
36 mkdir -p ${TARGET}/gobuster-darwin-$${GOARCH} ; \
37 GOOS=darwin GOARCH=$${GOARCH} GO111MODULE=on CGO_ENABLED=0 go build -ldflags=${LDFLAGS} -trimpath -o ${TARGET}/gobuster-darwin-$${GOARCH}/gobuster ; \
38 done; \
39 echo "Done."
40
41 all: clean fmt update test lint darwin linux windows
42
43 test:
44 @go test -v -race ./... ; \
45 echo "Done."
46
26 .PHONY: lint
4727 lint:
48 @if [ ! -f "$$(go env GOPATH)/bin/golangci-lint" ]; then \
49 curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin v1.29.0; \
50 fi
5128 "$$(go env GOPATH)/bin/golangci-lint" run ./...
5229 go mod tidy
5330
54 clean:
55 @rm -rf ${TARGET}/* ; \
56 go clean ./... ; \
57 echo "Done."
31 .PHONY: lint-update
32 lint-update:
33 curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$(go env GOPATH)/bin
34 $$(go env GOPATH)/bin/golangci-lint --version
35
36 .PHONY: tag
37 tag:
38 @[ "${TAG}" ] && echo "Tagging a new version ${TAG}" || ( echo "TAG is not set"; exit 1 )
39 git tag -a "${TAG}" -m "${TAG}"
40 git push origin "${TAG}"
0 # Gobuster v3.1.0
0 # Gobuster v3.2.0
11
22 Gobuster is a tool used to brute-force:
33
4 * URIs (directories and files) in web sites.
5 * DNS subdomains (with wildcard support).
6 * Virtual Host names on target web servers.
7 * Open Amazon S3 buckets
4 - URIs (directories and files) in web sites.
5 - DNS subdomains (with wildcard support).
6 - Virtual Host names on target web servers.
7 - Open Amazon S3 buckets
88
99 ## Tags, Statuses, etc
1010
11 [![Build Status](https://travis-ci.com/OJ/gobuster.svg?branch=master)](https://travis-ci.com/OJ/gobuster) [![Backers on Open Collective](https://opencollective.com/gobuster/backers/badge.svg)] [![Sponsors on Open Collective](https://opencollective.com/gobuster/sponsors/badge.svg)]
12
13 ## Oh dear God.. WHY!?
14
15 Because I wanted:
16
17 1. ... something that didn't have a fat Java GUI (console FTW).
18 1. ... to build something that just worked on the command line.
19 1. ... something that did not do recursive brute force.
20 1. ... something that allowed me to brute force folders and multiple extensions at once.
21 1. ... something that compiled to native on multiple platforms.
22 1. ... something that was faster than an interpreted script (such as Python).
23 1. ... something that didn't require a runtime.
24 1. ... use something that was good with concurrency (hence Go).
25 1. ... to build something in Go that wasn't totally useless.
26
27 ## But it's shit! And your implementation sucks!
28
29 Yes, you're probably correct. Feel free to:
30
31 * Not use it.
32 * Show me how to do it better.
11 [![Build Status](https://travis-ci.com/OJ/gobuster.svg?branch=master)](https://travis-ci.com/OJ/gobuster) [![Backers on Open Collective](https://opencollective.com/gobuster/backers/badge.svg)](https://opencollective.com/gobuster) [![Sponsors on Open Collective](https://opencollective.com/gobuster/sponsors/badge.svg)](https://opencollective.com/gobuster)
12
3313
3414 ## Love this tool? Back it!
3515
3919
4020 All funds that are donated to this project will be donated to charity. A full log of charity donations will be available in this repository as they are processed.
4121
42 ## Changes in 3.1
43
44 * enumerate public AWS S3 buckets
45 * fuzzing mode
46 * specify HTTP method
47 * added support for patterns. You can now specify a file containing patterns that are applied to every word, one by line. Every occurrence of the term `{GOBUSTER}` in it will be replaced with the current wordlist item. Please use with caution as this can cause increase the number of requests issued a lot.
48 * The shorthand `p` flag which was assigned to proxy is now used by the pattern flag
49
50 ## Changes in 3.0
51
52 * New CLI options so modes are strictly separated (`-m` is now gone!)
53 * Performance Optimizations and better connection handling
54 * Ability to enumerate vhost names
55 * Option to supply custom HTTP headers
22 # Changes
23
24 ## 3.2
25
26 - Use go 1.19
27 - use contexts in the correct way
28 - get rid of the wildcard flag (except in DNS mode)
29 - color output
30 - retry on timeout
31 - google cloud bucket enumeration
32 - fix nil reference errors
33
34 ## 3.1
35
36 - enumerate public AWS S3 buckets
37 - fuzzing mode
38 - specify HTTP method
39 - added support for patterns. You can now specify a file containing patterns that are applied to every word, one by line. Every occurrence of the term `{GOBUSTER}` in it will be replaced with the current wordlist item. Please use with caution as this can cause increase the number of requests issued a lot.
40 - The shorthand `p` flag which was assigned to proxy is now used by the pattern flag
41
42 ## 3.0
43
44 - New CLI options so modes are strictly separated (`-m` is now gone!)
45 - Performance Optimizations and better connection handling
46 - Ability to enumerate vhost names
47 - Option to supply custom HTTP headers
48
49 # License
50
51 See the LICENSE file.
52
53 # Manual
5654
5755 ## Available Modes
5856
59 * dir - the classic directory brute-forcing mode
60 * dns - DNS subdomain brute-forcing mode
61 * s3 - Enumerate open S3 buckets and look for existence and bucket listings
62 * vhost - virtual host brute-forcing mode (not the same as DNS!)
63
64 ## Built-in Help
57 - dir - the classic directory brute-forcing mode
58 - dns - DNS subdomain brute-forcing mode
59 - s3 - Enumerate open S3 buckets and look for existence and bucket listings
60 - gcs - Enumerate open google cloud buckets
61 - vhost - virtual host brute-forcing mode (not the same as DNS!)
62 - fuzz - some basic fuzzing, replaces the `FUZZ` keyword
63
64 ## Easy Installation
65
66 ### Binary Releases
67
68 We are now shipping binaries for each of the releases so that you don't even have to build them yourself! How wonderful is that!
69
70 If you're stupid enough to trust binaries that I've put together, you can download them from the [releases](https://github.com/OJ/gobuster/releases) page.
71
72 ### Using `go install`
73
74 If you have a [Go](https://golang.org/) environment ready to go (at least go 1.19), it's as easy as:
75
76 ```bash
77 go install github.com/OJ/gobuster/v3@latest
78 ```
79
80 PS: You need at least go 1.19 to compile gobuster.
81
82 ### Building From Source
83
84 Since this tool is written in [Go](https://golang.org/) you need to 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. You need at least go 1.19 to compile gobuster.
85
86 ### Compiling
87
88 `gobuster` has external dependencies, and so they need to be pulled in first:
89
90 ```bash
91 go get && go build
92 ```
93
94 This will create a `gobuster` binary for you. If you want to install it in the `$GOPATH/bin` folder you can run:
95
96 ```bash
97 go install
98 ```
99
100 ## Modes
65101
66102 Help is built-in!
67103
68 * `gobuster help` - outputs the top-level help.
69 * `gobuster help <mode>` - outputs the help specific to that mode.
70
71 ## `dns` Mode Help
72
73 ```text
104 - `gobuster help` - outputs the top-level help.
105 - `gobuster help <mode>` - outputs the help specific to that mode.
106
107 ## `dns` Mode
108
109 ### Options
110
111 ```text
112 Uses DNS subdomain enumeration mode
113
74114 Usage:
75115 gobuster dns [flags]
76116
84124 --wildcard Force continued operation when wildcard found
85125
86126 Global Flags:
127 --delay duration Time each thread waits between requests (e.g. 1500ms)
128 --no-color Disable color output
129 --no-error Don't display errors
87130 -z, --no-progress Don't display progress
88131 -o, --output string Output file to write results to (defaults to stdout)
132 -p, --pattern string File containing replacement patterns
89133 -q, --quiet Don't print the banner and other noise
90134 -t, --threads int Number of concurrent threads (default 10)
91 --delay duration Time each thread waits between requests (e.g. 1500ms)
92135 -v, --verbose Verbose output (errors)
93136 -w, --wordlist string Path to the wordlist
94137 ```
95138
96 ## `dir` Mode Options
97
98 ```text
99 Usage:
100 gobuster dir [flags]
101
102 Flags:
103 -f, --add-slash Append / to each request
104 -c, --cookies string Cookies to use for the requests
105 -e, --expanded Expanded mode, print full URLs
106 -x, --extensions string File extension(s) to search for
107 -r, --follow-redirect Follow redirects
108 -H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
109 -h, --help help for dir
110 -l, --include-length Include the length of the body in the output
111 -k, --no-tls-validation Skip TLS certificate verification
112 -n, --no-status Don't print status codes
113 -P, --password string Password for Basic Auth
114 -p, --proxy string Proxy to use for requests [http(s)://host:port]
115 -s, --status-codes string Positive status codes (will be overwritten with status-codes-blacklist if set) (default "200,204,301,302,307,401,403")
116 -b, --status-codes-blacklist string Negative status codes (will override status-codes if set)
117 --timeout duration HTTP Timeout (default 10s)
118 -u, --url string The target URL
119 -a, --useragent string Set the User-Agent string (default "gobuster/3.1.0")
120 -U, --username string Username for Basic Auth
121 -d, --discover-backup Upon finding a file search for backup files
122 --wildcard Force continued operation when wildcard found
123
124 Global Flags:
125 -z, --no-progress Don't display progress
126 -o, --output string Output file to write results to (defaults to stdout)
127 -q, --quiet Don't print the banner and other noise
128 -t, --threads int Number of concurrent threads (default 10)
129 --delay duration Time each thread waits between requests (e.g. 1500ms)
130 -v, --verbose Verbose output (errors)
131 -w, --wordlist string Path to the wordlist
132 ```
133
134 ## `vhost` Mode Options
135
136 ```text
137 Usage:
138 gobuster vhost [flags]
139
140 Flags:
141 -c, --cookies string Cookies to use for the requests
142 -r, --follow-redirect Follow redirects
143 -H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
144 -h, --help help for vhost
145 -k, --no-tls-validation Skip TLS certificate verification
146 -P, --password string Password for Basic Auth
147 -p, --proxy string Proxy to use for requests [http(s)://host:port]
148 --timeout duration HTTP Timeout (default 10s)
149 -u, --url string The target URL
150 -a, --useragent string Set the User-Agent string (default "gobuster/3.1.0")
151 -U, --username string Username for Basic Auth
152
153 Global Flags:
154 -z, --no-progress Don't display progress
155 -o, --output string Output file to write results to (defaults to stdout)
156 -q, --quiet Don't print the banner and other noise
157 -t, --threads int Number of concurrent threads (default 10)
158 --delay duration Time each thread waits between requests (e.g. 1500ms)
159 -v, --verbose Verbose output (errors)
160 -w, --wordlist string Path to the wordlist
161 ```
162
163 ## Easy Installation
164
165 ### Binary Releases
166
167 We are now shipping binaries for each of the releases so that you don't even have to build them yourself! How wonderful is that!
168
169 If you're stupid enough to trust binaries that I've put together, you can download them from the [releases](https://github.com/OJ/gobuster/releases) page.
170
171 ### Using `go get`
172
173 If you have a [Go](https://golang.org/) environment ready to go, it's as easy as:
174
175 ```bash
176 go get github.com/OJ/gobuster
177 ```
178
179 ## Building From Source
180
181 Since this tool is written in [Go](https://golang.org/) you need to 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.
182
183 ### Compiling
184
185 `gobuster` now has external dependencies, and so they need to be pulled in first:
186
187 ```bash
188 go get && go build
189 ```
190
191 This will create a `gobuster` binary for you. If you want to install it in the `$GOPATH/bin` folder you can run:
192
193 ```bash
194 go install
195 ```
196
197 If you have all the dependencies already, you can make use of the build scripts:
198
199 * `make` - builds for the current Go configuration (ie. runs `go build`).
200 * `make windows` - builds 32 and 64 bit binaries for windows, and writes them to the `build` folder.
201 * `make linux` - builds 32 and 64 bit binaries for linux, and writes them to the `build` folder.
202 * `make darwin` - builds 32 and 64 bit binaries for darwin, and writes them to the `build` folder.
203 * `make all` - builds for all platforms and architectures, and writes the resulting binaries to the `build` folder.
204 * `make clean` - clears out the `build` folder.
205 * `make test` - runs the tests.
206
207 ## Wordlists via STDIN
208
209 Wordlists can be piped into `gobuster` via stdin by providing a `-` to the `-w` option:
210
211 ```bash
212 hashcat -a 3 --stdout ?l | gobuster dir -u https://mysite.com -w -
213 ```
214
215 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.
216
217 ## Patterns
218
219 You can supply pattern files that will be applied to every word from the wordlist.
220 Just place the string `{GOBUSTER}` in it and this will be replaced with the word.
221 This feature is also handy in s3 mode to pre- or postfix certain patterns.
222
223 **Caution:** Using a big pattern file can cause a lot of request as every pattern is applied to every word in the wordlist.
224
225 ### Example file
226
227 ```text
228 {GOBUSTER}Partial
229 {GOBUSTER}Service
230 PRE{GOBUSTER}POST
231 {GOBUSTER}-prod
232 {GOBUSTER}-dev
233 ```
234
235 ## Examples
236
237 ### `dir` Mode
238
239 Command line might look like this:
240
241 ```bash
242 gobuster dir -u https://mysite.com/path/to/folder -c 'session=123456' -t 50 -w common-files.txt -x .php,.html
243 ```
244
245 Default options looks like this:
246
247 ```bash
248 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt
249
250 ===============================================================
251 Gobuster v3.1.0
252 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
253 ===============================================================
254 [+] Mode : dir
255 [+] Url/Domain : https://buffered.io/
256 [+] Threads : 10
257 [+] Wordlist : /home/oj/wordlists/shortlist.txt
258 [+] Status codes : 200,204,301,302,307,401,403
259 [+] User Agent : gobuster/3.1.0
260 [+] Timeout : 10s
261 ===============================================================
262 2019/06/21 11:49:43 Starting gobuster
263 ===============================================================
264 /categories (Status: 301)
265 /contact (Status: 301)
266 /posts (Status: 301)
267 /index (Status: 200)
268 ===============================================================
269 2019/06/21 11:49:44 Finished
270 ===============================================================
271 ```
272
273 Default options with status codes disabled looks like this:
274
275 ```bash
276 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -n
277
278 ===============================================================
279 Gobuster v3.1.0
280 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
281 ===============================================================
282 [+] Mode : dir
283 [+] Url/Domain : https://buffered.io/
284 [+] Threads : 10
285 [+] Wordlist : /home/oj/wordlists/shortlist.txt
286 [+] Status codes : 200,204,301,302,307,401,403
287 [+] User Agent : gobuster/3.1.0
288 [+] No status : true
289 [+] Timeout : 10s
290 ===============================================================
291 2019/06/21 11:50:18 Starting gobuster
292 ===============================================================
293 /categories
294 /contact
295 /index
296 /posts
297 ===============================================================
298 2019/06/21 11:50:18 Finished
299 ===============================================================
300 ```
301
302 Verbose output looks like this:
303
304 ```bash
305 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -v
306
307 ===============================================================
308 Gobuster v3.1.0
309 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
310 ===============================================================
311 [+] Mode : dir
312 [+] Url/Domain : https://buffered.io/
313 [+] Threads : 10
314 [+] Wordlist : /home/oj/wordlists/shortlist.txt
315 [+] Status codes : 200,204,301,302,307,401,403
316 [+] User Agent : gobuster/3.1.0
317 [+] Verbose : true
318 [+] Timeout : 10s
319 ===============================================================
320 2019/06/21 11:50:51 Starting gobuster
321 ===============================================================
322 Missed: /alsodoesnotexist (Status: 404)
323 Found: /index (Status: 200)
324 Missed: /doesnotexist (Status: 404)
325 Found: /categories (Status: 301)
326 Found: /posts (Status: 301)
327 Found: /contact (Status: 301)
328 ===============================================================
329 2019/06/21 11:50:51 Finished
330 ===============================================================
331 ```
332
333 Example showing content length:
334
335 ```bash
336 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -l
337
338 ===============================================================
339 Gobuster v3.1.0
340 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
341 ===============================================================
342 [+] Mode : dir
343 [+] Url/Domain : https://buffered.io/
344 [+] Threads : 10
345 [+] Wordlist : /home/oj/wordlists/shortlist.txt
346 [+] Status codes : 200,204,301,302,307,401,403
347 [+] User Agent : gobuster/3.1.0
348 [+] Show length : true
349 [+] Timeout : 10s
350 ===============================================================
351 2019/06/21 11:51:16 Starting gobuster
352 ===============================================================
353 /categories (Status: 301) [Size: 178]
354 /posts (Status: 301) [Size: 178]
355 /contact (Status: 301) [Size: 178]
356 /index (Status: 200) [Size: 51759]
357 ===============================================================
358 2019/06/21 11:51:17 Finished
359 ===============================================================
360 ```
361
362 Quiet output, with status disabled and expanded mode looks like this ("grep mode"):
363
364 ```bash
365 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -q -n -e
366 https://buffered.io/index
367 https://buffered.io/contact
368 https://buffered.io/posts
369 https://buffered.io/categories
370 ```
371
372 ### `dns` Mode
373
374 Command line might look like this:
375
376 ```bash
139 ### Examples
140
141
142 ```text
377143 gobuster dns -d mysite.com -t 50 -w common-names.txt
378144 ```
379145
380146 Normal sample run goes like this:
381147
382 ```bash
148 ```text
383149 gobuster dns -d google.com -w ~/wordlists/subdomains.txt
384150
385151 ===============================================================
386 Gobuster v3.1.0
152 Gobuster v3.2.0
387153 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
388154 ===============================================================
389155 [+] Mode : dns
418184
419185 Show IP sample run goes like this:
420186
421 ```bash
187 ```text
422188 gobuster dns -d google.com -w ~/wordlists/subdomains.txt -i
423189
424190 ===============================================================
425 Gobuster v3.1.0
191 Gobuster v3.2.0
426192 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
427193 ===============================================================
428194 [+] Mode : dns
457223
458224 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.
459225
460 ```bash
226 ```text
461227 gobuster dns -d yp.to -w ~/wordlists/subdomains.txt -i
462228
463229 ===============================================================
464 Gobuster v3.1.0
230 Gobuster v3.2.0
465231 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
466232 ===============================================================
467233 [+] Mode : dns
480246
481247 Wildcard DNS is also detected properly:
482248
483 ```bash
249 ```text
484250 gobuster dns -d 0.0.1.xip.io -w ~/wordlists/subdomains.txt
485251
486252 ===============================================================
487 Gobuster v3.1.0
253 Gobuster v3.2.0
488254 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
489255 ===============================================================
490256 [+] Mode : dns
503269
504270 If the user wants to force processing of a domain that has wildcard entries, use `--wildcard`:
505271
506 ```bash
272 ```text
507273 gobuster dns -d 0.0.1.xip.io -w ~/wordlists/subdomains.txt --wildcard
508274
509275 ===============================================================
510 Gobuster v3.1.0
276 Gobuster v3.2.0
511277 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
512278 ===============================================================
513279 [+] Mode : dns
525291 ===============================================================
526292 ```
527293
528 ### `vhost` Mode
529
530 Command line might look like this:
531
532 ```bash
294 ## `dir` Mode
295
296 ### Options
297
298 ```text
299 Uses directory/file enumeration mode
300
301 Usage:
302 gobuster dir [flags]
303
304 Flags:
305 -f, --add-slash Append / to each request
306 -c, --cookies string Cookies to use for the requests
307 -d, --discover-backup Also search for backup files by appending multiple backup extensions
308 --exclude-length ints exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.
309 -e, --expanded Expanded mode, print full URLs
310 -x, --extensions string File extension(s) to search for
311 -r, --follow-redirect Follow redirects
312 -H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
313 -h, --help help for dir
314 --hide-length Hide the length of the body in the output
315 -m, --method string Use the following HTTP method (default "GET")
316 -n, --no-status Don't print status codes
317 -k, --no-tls-validation Skip TLS certificate verification
318 -P, --password string Password for Basic Auth
319 --proxy string Proxy to use for requests [http(s)://host:port]
320 --random-agent Use a random User-Agent string
321 --retry Should retry on request timeout
322 --retry-attempts int Times to retry on request timeout (default 3)
323 -s, --status-codes string Positive status codes (will be overwritten with status-codes-blacklist if set)
324 -b, --status-codes-blacklist string Negative status codes (will override status-codes if set) (default "404")
325 --timeout duration HTTP Timeout (default 10s)
326 -u, --url string The target URL
327 -a, --useragent string Set the User-Agent string (default "gobuster/3.2.0")
328 -U, --username string Username for Basic Auth
329
330 Global Flags:
331 --delay duration Time each thread waits between requests (e.g. 1500ms)
332 --no-color Disable color output
333 --no-error Don't display errors
334 -z, --no-progress Don't display progress
335 -o, --output string Output file to write results to (defaults to stdout)
336 -p, --pattern string File containing replacement patterns
337 -q, --quiet Don't print the banner and other noise
338 -t, --threads int Number of concurrent threads (default 10)
339 -v, --verbose Verbose output (errors)
340 -w, --wordlist string Path to the wordlist
341 ```
342
343 ### Examples
344
345 ```text
346 gobuster dir -u https://mysite.com/path/to/folder -c 'session=123456' -t 50 -w common-files.txt -x .php,.html
347 ```
348
349 Default options looks like this:
350
351 ```text
352 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt
353
354 ===============================================================
355 Gobuster v3.2.0
356 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
357 ===============================================================
358 [+] Mode : dir
359 [+] Url/Domain : https://buffered.io/
360 [+] Threads : 10
361 [+] Wordlist : /home/oj/wordlists/shortlist.txt
362 [+] Status codes : 200,204,301,302,307,401,403
363 [+] User Agent : gobuster/3.2.0
364 [+] Timeout : 10s
365 ===============================================================
366 2019/06/21 11:49:43 Starting gobuster
367 ===============================================================
368 /categories (Status: 301)
369 /contact (Status: 301)
370 /posts (Status: 301)
371 /index (Status: 200)
372 ===============================================================
373 2019/06/21 11:49:44 Finished
374 ===============================================================
375 ```
376
377 Default options with status codes disabled looks like this:
378
379 ```text
380 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -n
381
382 ===============================================================
383 Gobuster v3.2.0
384 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
385 ===============================================================
386 [+] Mode : dir
387 [+] Url/Domain : https://buffered.io/
388 [+] Threads : 10
389 [+] Wordlist : /home/oj/wordlists/shortlist.txt
390 [+] Status codes : 200,204,301,302,307,401,403
391 [+] User Agent : gobuster/3.2.0
392 [+] No status : true
393 [+] Timeout : 10s
394 ===============================================================
395 2019/06/21 11:50:18 Starting gobuster
396 ===============================================================
397 /categories
398 /contact
399 /index
400 /posts
401 ===============================================================
402 2019/06/21 11:50:18 Finished
403 ===============================================================
404 ```
405
406 Verbose output looks like this:
407
408 ```text
409 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -v
410
411 ===============================================================
412 Gobuster v3.2.0
413 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
414 ===============================================================
415 [+] Mode : dir
416 [+] Url/Domain : https://buffered.io/
417 [+] Threads : 10
418 [+] Wordlist : /home/oj/wordlists/shortlist.txt
419 [+] Status codes : 200,204,301,302,307,401,403
420 [+] User Agent : gobuster/3.2.0
421 [+] Verbose : true
422 [+] Timeout : 10s
423 ===============================================================
424 2019/06/21 11:50:51 Starting gobuster
425 ===============================================================
426 Missed: /alsodoesnotexist (Status: 404)
427 Found: /index (Status: 200)
428 Missed: /doesnotexist (Status: 404)
429 Found: /categories (Status: 301)
430 Found: /posts (Status: 301)
431 Found: /contact (Status: 301)
432 ===============================================================
433 2019/06/21 11:50:51 Finished
434 ===============================================================
435 ```
436
437 Example showing content length:
438
439 ```text
440 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -l
441
442 ===============================================================
443 Gobuster v3.2.0
444 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
445 ===============================================================
446 [+] Mode : dir
447 [+] Url/Domain : https://buffered.io/
448 [+] Threads : 10
449 [+] Wordlist : /home/oj/wordlists/shortlist.txt
450 [+] Status codes : 200,204,301,302,307,401,403
451 [+] User Agent : gobuster/3.2.0
452 [+] Show length : true
453 [+] Timeout : 10s
454 ===============================================================
455 2019/06/21 11:51:16 Starting gobuster
456 ===============================================================
457 /categories (Status: 301) [Size: 178]
458 /posts (Status: 301) [Size: 178]
459 /contact (Status: 301) [Size: 178]
460 /index (Status: 200) [Size: 51759]
461 ===============================================================
462 2019/06/21 11:51:17 Finished
463 ===============================================================
464 ```
465
466 Quiet output, with status disabled and expanded mode looks like this ("grep mode"):
467
468 ```text
469 gobuster dir -u https://buffered.io -w ~/wordlists/shortlist.txt -q -n -e
470 https://buffered.io/index
471 https://buffered.io/contact
472 https://buffered.io/posts
473 https://buffered.io/categories
474 ```
475
476 ## `vhost` Mode
477
478 ### Options
479
480 ```text
481 Uses VHOST enumeration mode (you most probably want to use the IP adress as the URL parameter
482
483 Usage:
484 gobuster vhost [flags]
485
486 Flags:
487 --append-domain Append main domain from URL to words from wordlist. Otherwise the fully qualified domains need to be specified in the wordlist.
488 -c, --cookies string Cookies to use for the requests
489 --domain string 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
490 --exclude-length ints exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.
491 -r, --follow-redirect Follow redirects
492 -H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
493 -h, --help help for vhost
494 -m, --method string Use the following HTTP method (default "GET")
495 -k, --no-tls-validation Skip TLS certificate verification
496 -P, --password string Password for Basic Auth
497 --proxy string Proxy to use for requests [http(s)://host:port]
498 --random-agent Use a random User-Agent string
499 --retry Should retry on request timeout
500 --retry-attempts int Times to retry on request timeout (default 3)
501 --timeout duration HTTP Timeout (default 10s)
502 -u, --url string The target URL
503 -a, --useragent string Set the User-Agent string (default "gobuster/3.2.0")
504 -U, --username string Username for Basic Auth
505
506 Global Flags:
507 --delay duration Time each thread waits between requests (e.g. 1500ms)
508 --no-color Disable color output
509 --no-error Don't display errors
510 -z, --no-progress Don't display progress
511 -o, --output string Output file to write results to (defaults to stdout)
512 -p, --pattern string File containing replacement patterns
513 -q, --quiet Don't print the banner and other noise
514 -t, --threads int Number of concurrent threads (default 10)
515 -v, --verbose Verbose output (errors)
516 -w, --wordlist string Path to the wordlist
517 ```
518
519 ### Examples
520
521
522 ```text
533523 gobuster vhost -u https://mysite.com -w common-vhosts.txt
534524 ```
535525
536526 Normal sample run goes like this:
537527
538 ```bash
528 ```text
539529 gobuster vhost -u https://mysite.com -w common-vhosts.txt
540530
541531 ===============================================================
542 Gobuster v3.1.0
532 Gobuster v3.2.0
543533 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
544534 ===============================================================
545535 [+] Url: https://mysite.com
546536 [+] Threads: 10
547537 [+] Wordlist: common-vhosts.txt
548 [+] User Agent: gobuster/3.1.0
538 [+] User Agent: gobuster/3.2.0
549539 [+] Timeout: 10s
550540 ===============================================================
551541 2019/06/21 08:36:00 Starting gobuster
558548 ===============================================================
559549 ```
560550
561 ### `s3` Mode
562
563 Command line might look like this:
551 ## `fuzz` Mode
552
553 ### Options
554
555 ```text
556 Uses fuzzing mode
557
558 Usage:
559 gobuster fuzz [flags]
560
561 Flags:
562 -c, --cookies string Cookies to use for the requests
563 --exclude-length ints exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.
564 -b, --excludestatuscodes string Negative status codes (will override statuscodes if set)
565 -r, --follow-redirect Follow redirects
566 -H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
567 -h, --help help for fuzz
568 -m, --method string Use the following HTTP method (default "GET")
569 -k, --no-tls-validation Skip TLS certificate verification
570 -P, --password string Password for Basic Auth
571 --proxy string Proxy to use for requests [http(s)://host:port]
572 --random-agent Use a random User-Agent string
573 --retry Should retry on request timeout
574 --retry-attempts int Times to retry on request timeout (default 3)
575 --timeout duration HTTP Timeout (default 10s)
576 -u, --url string The target URL
577 -a, --useragent string Set the User-Agent string (default "gobuster/3.2.0")
578 -U, --username string Username for Basic Auth
579
580 Global Flags:
581 --delay duration Time each thread waits between requests (e.g. 1500ms)
582 --no-color Disable color output
583 --no-error Don't display errors
584 -z, --no-progress Don't display progress
585 -o, --output string Output file to write results to (defaults to stdout)
586 -p, --pattern string File containing replacement patterns
587 -q, --quiet Don't print the banner and other noise
588 -t, --threads int Number of concurrent threads (default 10)
589 -v, --verbose Verbose output (errors)
590 -w, --wordlist string Path to the wordlist
591 ```
592
593 ### Examples
594
595 ```text
596 gobuster fuzz -u https://example.com?FUZZ=test -w parameter-names.txt
597 ```
598
599 ## `s3` Mode
600
601 ### Options
602
603 ```text
604 Uses aws bucket enumeration mode
605
606 Usage:
607 gobuster s3 [flags]
608
609 Flags:
610 -h, --help help for s3
611 -m, --maxfiles int max files to list when listing buckets (only shown in verbose mode) (default 5)
612 -k, --no-tls-validation Skip TLS certificate verification
613 --proxy string Proxy to use for requests [http(s)://host:port]
614 --random-agent Use a random User-Agent string
615 --retry Should retry on request timeout
616 --retry-attempts int Times to retry on request timeout (default 3)
617 --timeout duration HTTP Timeout (default 10s)
618 -a, --useragent string Set the User-Agent string (default "gobuster/3.2.0")
619
620 Global Flags:
621 --delay duration Time each thread waits between requests (e.g. 1500ms)
622 --no-color Disable color output
623 --no-error Don't display errors
624 -z, --no-progress Don't display progress
625 -o, --output string Output file to write results to (defaults to stdout)
626 -p, --pattern string File containing replacement patterns
627 -q, --quiet Don't print the banner and other noise
628 -t, --threads int Number of concurrent threads (default 10)
629 -v, --verbose Verbose output (errors)
630 -w, --wordlist string Path to the wordlist
631 ```
632
633 ### Examples
634
635 ```text
636 gobuster s3 -w bucket-names.txt
637 ```
638
639 ## `gcs` Mode
640
641 ### Options
642
643 ```text
644 Uses gcs bucket enumeration mode
645
646 Usage:
647 gobuster gcs [flags]
648
649 Flags:
650 -h, --help help for gcs
651 -m, --maxfiles int max files to list when listing buckets (only shown in verbose mode) (default 5)
652 -k, --no-tls-validation Skip TLS certificate verification
653 --proxy string Proxy to use for requests [http(s)://host:port]
654 --random-agent Use a random User-Agent string
655 --retry Should retry on request timeout
656 --retry-attempts int Times to retry on request timeout (default 3)
657 --timeout duration HTTP Timeout (default 10s)
658 -a, --useragent string Set the User-Agent string (default "gobuster/3.2.0")
659
660 Global Flags:
661 --delay duration Time each thread waits between requests (e.g. 1500ms)
662 --no-color Disable color output
663 --no-error Don't display errors
664 -z, --no-progress Don't display progress
665 -o, --output string Output file to write results to (defaults to stdout)
666 -p, --pattern string File containing replacement patterns
667 -q, --quiet Don't print the banner and other noise
668 -t, --threads int Number of concurrent threads (default 10)
669 -v, --verbose Verbose output (errors)
670 -w, --wordlist string Path to the wordlist
671 ```
672
673 ### Examples
674
675 ```text
676 gobuster gcs -w bucket-names.txt
677 ```
678
679 ## Wordlists via STDIN
680
681 Wordlists can be piped into `gobuster` via stdin by providing a `-` to the `-w` option:
564682
565683 ```bash
566 gobuster s3 -w bucket-names.txt
567 ```
568
569 ### `fuzzing` Mode
570
571 Command line might look like this:
572
573 ```bash
574 gobuster fuzz -u https://example.com?FUZZ=test -w parameter-names.txt
684 hashcat -a 3 --stdout ?l | gobuster dir -u https://mysite.com -w -
685 ```
686
687 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.
688
689 ## Patterns
690
691 You can supply pattern files that will be applied to every word from the wordlist.
692 Just place the string `{GOBUSTER}` in it and this will be replaced with the word.
693 This feature is also handy in s3 mode to pre- or postfix certain patterns.
694
695 **Caution:** Using a big pattern file can cause a lot of request as every pattern is applied to every word in the wordlist.
696
697 ### Example file
698
699 ```text
700 {GOBUSTER}Partial
701 {GOBUSTER}Service
702 PRE{GOBUSTER}POST
703 {GOBUSTER}-prod
704 {GOBUSTER}-dev
575705 ```
576706
577707 #### Use case in combination with patterns
578708
579 * Create a custom wordlist for the target containing company names and so on
580 * Create a pattern file to use for common bucket names.
709 - Create a custom wordlist for the target containing company names and so on
710 - Create a pattern file to use for common bucket names.
581711
582712 ```bash
583713 curl -s --output - https://raw.githubusercontent.com/eth0izzle/bucket-stream/master/permutations/extended.txt | sed -s 's/%s/{GOBUSTER}/' > patterns.txt
584714 ```
585715
586 * Run gobuster with the custom input. Be sure to turn verbose mode on to see the bucket details
587
588 ```bash
716 - Run gobuster with the custom input. Be sure to turn verbose mode on to see the bucket details
717
718 ```text
589719 gobuster s3 --wordlist my.custom.wordlist -p patterns.txt -v
590720 ```
591721
594724 ```text
595725 PS C:\Users\firefart\Documents\code\gobuster> .\gobuster.exe s3 --wordlist .\wordlist.txt
596726 ===============================================================
597 Gobuster v3.1.0
727 Gobuster v3.2.0
598728 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
599729 ===============================================================
600730 [+] Threads: 10
601731 [+] Wordlist: .\wordlist.txt
602 [+] User Agent: gobuster/3.1.0
732 [+] User Agent: gobuster/3.2.0
603733 [+] Timeout: 10s
604734 [+] Maximum files to list: 5
605735 ===============================================================
623753 ```text
624754 PS C:\Users\firefart\Documents\code\gobuster> .\gobuster.exe s3 --wordlist .\wordlist.txt -v
625755 ===============================================================
626 Gobuster v3.1.0
756 Gobuster v3.2.0
627757 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
628758 ===============================================================
629759 [+] Threads: 10
630760 [+] Wordlist: .\wordlist.txt
631 [+] User Agent: gobuster/3.1.0
761 [+] User Agent: gobuster/3.2.0
632762 [+] Verbose: true
633763 [+] Timeout: 10s
634764 [+] Maximum files to list: 5
653783 ```text
654784 PS C:\Users\firefart\Documents\code\gobuster> .\gobuster.exe s3 --wordlist .\wordlist.txt -e
655785 ===============================================================
656 Gobuster v3.1.0
786 Gobuster v3.2.0
657787 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
658788 ===============================================================
659789 [+] Threads: 10
660790 [+] Wordlist: .\wordlist.txt
661 [+] User Agent: gobuster/3.1.0
791 [+] User Agent: gobuster/3.2.0
662792 [+] Timeout: 10s
663793 [+] Expanded: true
664794 [+] Maximum files to list: 5
677807 2019/08/12 21:48:38 Finished
678808 ===============================================================
679809 ```
680
681 ## License
682
683 See the LICENSE file.
684
685 ## Thanks
686
687 See the THANKS file for people who helped out.
+0
-1
TODO.md less more
0 # TODO
00 package cmd
11
22 import (
3 "errors"
34 "fmt"
45 "log"
56
1011 "github.com/spf13/cobra"
1112 )
1213
14 // nolint:gochecknoglobals
1315 var cmdDir *cobra.Command
1416
1517 func runDir(cmd *cobra.Command, args []string) error {
1820 return fmt.Errorf("error on parsing arguments: %w", err)
1921 }
2022
21 plugin, err := gobusterdir.NewGobusterDir(mainContext, globalopts, pluginopts)
23 plugin, err := gobusterdir.NewGobusterDir(globalopts, pluginopts)
2224 if err != nil {
2325 return fmt.Errorf("error on creating gobusterdir: %w", err)
2426 }
2527
2628 if err := cli.Gobuster(mainContext, globalopts, plugin); err != nil {
27 if goberr, ok := err.(*gobusterdir.ErrWildcard); ok {
28 return fmt.Errorf("%s. To continue please exclude the status code, the length or use the --wildcard switch", goberr.Error())
29 var wErr *gobusterdir.ErrWildcard
30 if errors.As(err, &wErr) {
31 return fmt.Errorf("%w. To continue please exclude the status code or the length", wErr)
2932 }
3033 return fmt.Errorf("error on running gobuster: %w", err)
3134 }
5558 plugin.NoTLSValidation = httpOpts.NoTLSValidation
5659 plugin.Headers = httpOpts.Headers
5760 plugin.Method = httpOpts.Method
61 plugin.RetryOnTimeout = httpOpts.RetryOnTimeout
62 plugin.RetryAttempts = httpOpts.RetryAttempts
5863
5964 plugin.Extensions, err = cmdDir.Flags().GetString("extensions")
6065 if err != nil {
6166 return nil, nil, fmt.Errorf("invalid value for extensions: %w", err)
6267 }
68 ret, err := helper.ParseExtensions(plugin.Extensions)
69 if err != nil {
70 return nil, nil, fmt.Errorf("invalid value for extensions: %w", err)
71 }
72 plugin.ExtensionsParsed = ret
6373
64 if plugin.Extensions != "" {
65 ret, err := helper.ParseExtensions(plugin.Extensions)
66 if err != nil {
67 return nil, nil, fmt.Errorf("invalid value for extensions: %w", err)
68 }
69 plugin.ExtensionsParsed = ret
74 // parse normal status codes
75 plugin.StatusCodes, err = cmdDir.Flags().GetString("status-codes")
76 if err != nil {
77 return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err)
7078 }
79 ret2, err := helper.ParseCommaSeparatedInt(plugin.StatusCodes)
80 if err != nil {
81 return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err)
82 }
83 plugin.StatusCodesParsed = ret2
7184
85 // blacklist will override the normal status codes
7286 plugin.StatusCodesBlacklist, err = cmdDir.Flags().GetString("status-codes-blacklist")
7387 if err != nil {
7488 return nil, nil, fmt.Errorf("invalid value for status-codes-blacklist: %w", err)
7589 }
90 ret3, err := helper.ParseCommaSeparatedInt(plugin.StatusCodesBlacklist)
91 if err != nil {
92 return nil, nil, fmt.Errorf("invalid value for status-codes-blacklist: %w", err)
93 }
94 plugin.StatusCodesBlacklistParsed = ret3
7695
77 // blacklist will override the normal status codes
78 if plugin.StatusCodesBlacklist != "" {
79 ret, err := helper.ParseCommaSeparatedInt(plugin.StatusCodesBlacklist)
80 if err != nil {
81 return nil, nil, fmt.Errorf("invalid value for status-codes-blacklist: %w", err)
82 }
83 plugin.StatusCodesBlacklistParsed = ret
84 } else {
85 // parse normal status codes
86 plugin.StatusCodes, err = cmdDir.Flags().GetString("status-codes")
87 if err != nil {
88 return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err)
89 }
90 ret, err := helper.ParseCommaSeparatedInt(plugin.StatusCodes)
91 if err != nil {
92 return nil, nil, fmt.Errorf("invalid value for status-codes: %w", err)
93 }
94 plugin.StatusCodesParsed = ret
96 if plugin.StatusCodes != "" && plugin.StatusCodesBlacklist != "" {
97 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.",
98 plugin.StatusCodes, plugin.StatusCodesBlacklist)
99 }
100
101 if plugin.StatusCodes == "" && plugin.StatusCodesBlacklist == "" {
102 return nil, nil, fmt.Errorf("status-codes and status-codes-blacklist are both not set, please set one")
95103 }
96104
97105 plugin.UseSlash, err = cmdDir.Flags().GetBool("add-slash")
114122 return nil, nil, fmt.Errorf("invalid value for hide-length: %w", err)
115123 }
116124
117 plugin.WildcardForced, err = cmdDir.Flags().GetBool("wildcard")
118 if err != nil {
119 return nil, nil, fmt.Errorf("invalid value for wildcard: %w", err)
120 }
121
122125 plugin.DiscoverBackup, err = cmdDir.Flags().GetBool("discover-backup")
123126 if err != nil {
124127 return nil, nil, fmt.Errorf("invalid value for discover-backup: %w", err)
132135 return globalopts, plugin, nil
133136 }
134137
138 // nolint:gochecknoinits
135139 func init() {
136140 cmdDir = &cobra.Command{
137141 Use: "dir",
149153 cmdDir.Flags().BoolP("no-status", "n", false, "Don't print status codes")
150154 cmdDir.Flags().Bool("hide-length", false, "Hide the length of the body in the output")
151155 cmdDir.Flags().BoolP("add-slash", "f", false, "Append / to each request")
152 cmdDir.Flags().Bool("wildcard", false, "Force continued operation when wildcard found")
153 cmdDir.Flags().BoolP("discover-backup", "d", false, "Upon finding a file search for backup files")
156 cmdDir.Flags().BoolP("discover-backup", "d", false, "Also search for backup files by appending multiple backup extensions")
154157 cmdDir.Flags().IntSlice("exclude-length", []int{}, "exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.")
155158
156159 cmdDir.PersistentPreRun = func(cmd *cobra.Command, args []string) {
22 import (
33 "context"
44 "fmt"
5 "io/ioutil"
5 "io"
66 "log"
77 "net/http"
88 "net/http/httptest"
3030 pluginopts := gobusterdir.NewOptionsDir()
3131 pluginopts.URL = h.URL
3232 pluginopts.Timeout = 10 * time.Second
33 pluginopts.WildcardForced = true
3433
3534 pluginopts.Extensions = ".php,.csv"
3635 tmpExt, err := helper.ParseExtensions(pluginopts.Extensions)
4645 }
4746 pluginopts.StatusCodesParsed = tmpStat
4847
49 wordlist, err := ioutil.TempFile("", "")
48 wordlist, err := os.CreateTemp("", "")
5049 if err != nil {
5150 b.Fatalf("could not create tempfile: %v", err)
5251 }
7271 }
7372 defer devnull.Close()
7473 log.SetFlags(0)
75 log.SetOutput(ioutil.Discard)
74 log.SetOutput(io.Discard)
7675
7776 // Run the real benchmark
7877 for x := 0; x < b.N; x++ {
7978 os.Stdout = devnull
8079 os.Stderr = devnull
81 plugin, err := gobusterdir.NewGobusterDir(ctx, &globalopts, pluginopts)
80 plugin, err := gobusterdir.NewGobusterDir(&globalopts, pluginopts)
8281 if err != nil {
8382 b.Fatalf("error on creating gobusterdir: %v", err)
8483 }
00 package cmd
11
22 import (
3 "errors"
34 "fmt"
45 "log"
56 "runtime"
1112 "github.com/spf13/cobra"
1213 )
1314
15 // nolint:gochecknoglobals
1416 var cmdDNS *cobra.Command
1517
1618 func runDNS(cmd *cobra.Command, args []string) error {
2527 }
2628
2729 if err := cli.Gobuster(mainContext, globalopts, plugin); err != nil {
28 if goberr, ok := err.(*gobusterdns.ErrWildcard); ok {
29 return fmt.Errorf("%s. To force processing of Wildcard DNS, specify the '--wildcard' switch", goberr.Error())
30 var wErr *gobusterdns.ErrWildcard
31 if errors.As(err, &wErr) {
32 return fmt.Errorf("%w. To force processing of Wildcard DNS, specify the '--wildcard' switch", wErr)
3033 }
3134 return fmt.Errorf("error on running gobuster: %w", err)
3235 }
7780 return globalopts, plugin, nil
7881 }
7982
83 // nolint:gochecknoinits
8084 func init() {
8185 cmdDNS = &cobra.Command{
8286 Use: "dns",
00 package cmd
11
22 import (
3 "errors"
34 "fmt"
45 "log"
56
1011 "github.com/spf13/cobra"
1112 )
1213
14 // nolint:gochecknoglobals
1315 var cmdFuzz *cobra.Command
1416
1517 func runFuzz(cmd *cobra.Command, args []string) error {
1820 return fmt.Errorf("error on parsing arguments: %w", err)
1921 }
2022
21 plugin, err := gobusterfuzz.NewGobusterFuzz(mainContext, globalopts, pluginopts)
23 plugin, err := gobusterfuzz.NewGobusterFuzz(globalopts, pluginopts)
2224 if err != nil {
2325 return fmt.Errorf("error on creating gobusterfuzz: %w", err)
2426 }
2527
2628 if err := cli.Gobuster(mainContext, globalopts, plugin); err != nil {
27 if goberr, ok := err.(*gobusterfuzz.ErrWildcard); ok {
28 return fmt.Errorf("%s. To force processing of Wildcard responses, specify the '--wildcard' switch", goberr.Error())
29 var wErr *gobusterfuzz.ErrWildcard
30 if errors.As(err, &wErr) {
31 return fmt.Errorf("%w. To continue please exclude the status code or the length", wErr)
2932 }
3033 return fmt.Errorf("error on running gobuster: %w", err)
3134 }
5558 plugin.NoTLSValidation = httpOpts.NoTLSValidation
5659 plugin.Headers = httpOpts.Headers
5760 plugin.Method = httpOpts.Method
61 plugin.RetryOnTimeout = httpOpts.RetryOnTimeout
62 plugin.RetryAttempts = httpOpts.RetryAttempts
5863
64 // blacklist will override the normal status codes
5965 plugin.ExcludedStatusCodes, err = cmdFuzz.Flags().GetString("excludestatuscodes")
6066 if err != nil {
6167 return nil, nil, fmt.Errorf("invalid value for excludestatuscodes: %w", err)
6268 }
63
64 // blacklist will override the normal status codes
65 if plugin.ExcludedStatusCodes != "" {
66 ret, err := helper.ParseCommaSeparatedInt(plugin.ExcludedStatusCodes)
67 if err != nil {
68 return nil, nil, fmt.Errorf("invalid value for excludestatuscodes: %w", err)
69 }
70 plugin.ExcludedStatusCodesParsed = ret
69 ret, err := helper.ParseCommaSeparatedInt(plugin.ExcludedStatusCodes)
70 if err != nil {
71 return nil, nil, fmt.Errorf("invalid value for excludestatuscodes: %w", err)
7172 }
72
73 plugin.WildcardForced, err = cmdFuzz.Flags().GetBool("wildcard")
74 if err != nil {
75 return nil, nil, fmt.Errorf("invalid value for wildcard: %w", err)
76 }
73 plugin.ExcludedStatusCodesParsed = ret
7774
7875 plugin.ExcludeLength, err = cmdFuzz.Flags().GetIntSlice("exclude-length")
7976 if err != nil {
8380 return globalopts, plugin, nil
8481 }
8582
83 // nolint:gochecknoinits
8684 func init() {
8785 cmdFuzz = &cobra.Command{
8886 Use: "fuzz",
9593 }
9694 cmdFuzz.Flags().StringP("excludestatuscodes", "b", "", "Negative status codes (will override statuscodes if set)")
9795 cmdFuzz.Flags().IntSlice("exclude-length", []int{}, "exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.")
98 cmdFuzz.Flags().BoolP("wildcard", "", false, "Force continued operation when wildcard found")
9996
10097 cmdFuzz.PersistentPreRun = func(cmd *cobra.Command, args []string) {
10198 configureGlobalOptions()
0 package cmd
1
2 import (
3 "fmt"
4
5 "github.com/OJ/gobuster/v3/cli"
6 "github.com/OJ/gobuster/v3/gobustergcs"
7 "github.com/OJ/gobuster/v3/libgobuster"
8 "github.com/spf13/cobra"
9 )
10
11 // nolint:gochecknoglobals
12 var cmdGCS *cobra.Command
13
14 func runGCS(cmd *cobra.Command, args []string) error {
15 globalopts, pluginopts, err := parseGCSOptions()
16 if err != nil {
17 return fmt.Errorf("error on parsing arguments: %w", err)
18 }
19
20 plugin, err := gobustergcs.NewGobusterGCS(globalopts, pluginopts)
21 if err != nil {
22 return fmt.Errorf("error on creating gobustergcs: %w", err)
23 }
24
25 if err := cli.Gobuster(mainContext, globalopts, plugin); err != nil {
26 return fmt.Errorf("error on running gobuster: %w", err)
27 }
28 return nil
29 }
30
31 func parseGCSOptions() (*libgobuster.Options, *gobustergcs.OptionsGCS, error) {
32 globalopts, err := parseGlobalOptions()
33 if err != nil {
34 return nil, nil, err
35 }
36
37 pluginopts := gobustergcs.NewOptionsGCS()
38
39 httpOpts, err := parseBasicHTTPOptions(cmdGCS)
40 if err != nil {
41 return nil, nil, err
42 }
43
44 pluginopts.UserAgent = httpOpts.UserAgent
45 pluginopts.Proxy = httpOpts.Proxy
46 pluginopts.Timeout = httpOpts.Timeout
47 pluginopts.NoTLSValidation = httpOpts.NoTLSValidation
48 pluginopts.RetryOnTimeout = httpOpts.RetryOnTimeout
49 pluginopts.RetryAttempts = httpOpts.RetryAttempts
50
51 pluginopts.MaxFilesToList, err = cmdGCS.Flags().GetInt("maxfiles")
52 if err != nil {
53 return nil, nil, fmt.Errorf("invalid value for maxfiles: %w", err)
54 }
55
56 return globalopts, pluginopts, nil
57 }
58
59 // nolint:gochecknoinits
60 func init() {
61 cmdGCS = &cobra.Command{
62 Use: "gcs",
63 Short: "Uses gcs bucket enumeration mode",
64 RunE: runGCS,
65 }
66
67 addBasicHTTPOptions(cmdGCS)
68 cmdGCS.Flags().IntP("maxfiles", "m", 5, "max files to list when listing buckets (only shown in verbose mode)")
69
70 cmdGCS.PersistentPreRun = func(cmd *cobra.Command, args []string) {
71 configureGlobalOptions()
72 }
73
74 rootCmd.AddCommand(cmdGCS)
75 }
1010 "github.com/OJ/gobuster/v3/helper"
1111 "github.com/OJ/gobuster/v3/libgobuster"
1212 "github.com/spf13/cobra"
13 "golang.org/x/crypto/ssh/terminal"
13 "golang.org/x/term"
1414 )
1515
1616 func addBasicHTTPOptions(cmd *cobra.Command) {
1818 cmd.Flags().BoolP("random-agent", "", false, "Use a random User-Agent string")
1919 cmd.Flags().StringP("proxy", "", "", "Proxy to use for requests [http(s)://host:port]")
2020 cmd.Flags().DurationP("timeout", "", 10*time.Second, "HTTP Timeout")
21 cmd.Flags().BoolP("no-tls-validation", "k", false, "Skip TLS certificate verification")
22 cmd.Flags().BoolP("retry", "", false, "Should retry on request timeout")
23 cmd.Flags().IntP("retry-attempts", "", 3, "Times to retry on request timeout")
2124 }
2225
2326 func addCommonHTTPOptions(cmd *cobra.Command) error {
2730 cmd.Flags().StringP("username", "U", "", "Username for Basic Auth")
2831 cmd.Flags().StringP("password", "P", "", "Password for Basic Auth")
2932 cmd.Flags().BoolP("follow-redirect", "r", false, "Follow redirects")
30 cmd.Flags().BoolP("no-tls-validation", "k", false, "Skip TLS certificate verification")
3133 cmd.Flags().StringArrayP("headers", "H", []string{""}, "Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'")
3234 cmd.Flags().StringP("method", "m", "GET", "Use the following HTTP method")
3335
5153 return options, fmt.Errorf("invalid value for random-agent: %w", err)
5254 }
5355 if randomUA {
54 options.UserAgent = helper.GetRandomUserAgent()
56 ua, err := helper.GetRandomUserAgent()
57 if err != nil {
58 return options, err
59 }
60 options.UserAgent = ua
5561 }
5662
5763 options.Proxy, err = cmd.Flags().GetString("proxy")
6268 options.Timeout, err = cmd.Flags().GetDuration("timeout")
6369 if err != nil {
6470 return options, fmt.Errorf("invalid value for timeout: %w", err)
71 }
72
73 options.RetryOnTimeout, err = cmd.Flags().GetBool("retry")
74 if err != nil {
75 return options, fmt.Errorf("invalid value for retry: %w", err)
76 }
77
78 options.RetryAttempts, err = cmd.Flags().GetInt("retry-attempts")
79 if err != nil {
80 return options, fmt.Errorf("invalid value for retry-attempts: %w", err)
81 }
82
83 options.NoTLSValidation, err = cmd.Flags().GetBool("no-tls-validation")
84 if err != nil {
85 return options, fmt.Errorf("invalid value for no-tls-validation: %w", err)
6586 }
6687 return options, nil
6788 }
7798 options.Proxy = basic.Proxy
7899 options.Timeout = basic.Timeout
79100 options.UserAgent = basic.UserAgent
101 options.NoTLSValidation = basic.NoTLSValidation
102 options.RetryOnTimeout = basic.RetryOnTimeout
103 options.RetryAttempts = basic.RetryAttempts
80104
81105 options.URL, err = cmd.Flags().GetString("url")
82106 if err != nil {
123147 return options, fmt.Errorf("invalid value for follow-redirect: %w", err)
124148 }
125149
126 options.NoTLSValidation, err = cmd.Flags().GetBool("no-tls-validation")
127 if err != nil {
128 return options, fmt.Errorf("invalid value for no-tls-validation: %w", err)
129 }
130
131150 options.Method, err = cmd.Flags().GetString("method")
132151 if err != nil {
133152 return options, fmt.Errorf("invalid value for method: %w", err)
156175 if options.Username != "" && options.Password == "" {
157176 fmt.Printf("[?] Auth Password: ")
158177 // please don't remove the int cast here as it is sadly needed on windows :/
159 passBytes, err := terminal.ReadPassword(int(syscall.Stdin)) //nolint:unconvert
178 passBytes, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert
160179 // print a newline to simulate the newline that was entered
161180 // this means that formatting/printing after doesn't look bad.
162181 fmt.Println("")
88 "os/signal"
99
1010 "github.com/OJ/gobuster/v3/libgobuster"
11
11 "github.com/fatih/color"
1212 "github.com/spf13/cobra"
1313 )
1414
15 // nolint:gochecknoglobals
1516 var rootCmd = &cobra.Command{
1617 Use: "gobuster",
1718 SilenceUsage: true,
1819 }
1920
21 // nolint:gochecknoglobals
2022 var mainContext context.Context
2123
2224 // Execute is the main cobra method
4648 // Once before and once after the help output. Not sure if
4749 // this is going to be needed to output other errors that
4850 // aren't automatically outputted.
49 //fmt.Println(err)
51 // fmt.Println(err)
5052 os.Exit(1)
5153 }
5254 }
134136 return nil, fmt.Errorf("invalid value for no-error: %w", err)
135137 }
136138
139 noColor, err := rootCmd.Flags().GetBool("no-color")
140 if err != nil {
141 return nil, fmt.Errorf("invalid value for no-color: %w", err)
142 }
143 if noColor {
144 color.NoColor = true
145 }
146
137147 return globalopts, nil
138148 }
139149
147157 }
148158 }
149159
160 // nolint:gochecknoinits
150161 func init() {
151162 rootCmd.PersistentFlags().DurationP("delay", "", 0, "Time each thread waits between requests (e.g. 1500ms)")
152163 rootCmd.PersistentFlags().IntP("threads", "t", 10, "Number of concurrent threads")
157168 rootCmd.PersistentFlags().BoolP("no-progress", "z", false, "Don't display progress")
158169 rootCmd.PersistentFlags().Bool("no-error", false, "Don't display errors")
159170 rootCmd.PersistentFlags().StringP("pattern", "p", "", "File containing replacement patterns")
171 rootCmd.PersistentFlags().Bool("no-color", false, "Disable color output")
160172 }
88 "github.com/spf13/cobra"
99 )
1010
11 // nolint:gochecknoglobals
1112 var cmdS3 *cobra.Command
1213
1314 func runS3(cmd *cobra.Command, args []string) error {
1617 return fmt.Errorf("error on parsing arguments: %w", err)
1718 }
1819
19 plugin, err := gobusters3.NewGobusterS3(mainContext, globalopts, pluginopts)
20 plugin, err := gobusters3.NewGobusterS3(globalopts, pluginopts)
2021 if err != nil {
2122 return fmt.Errorf("error on creating gobusters3: %w", err)
2223 }
4344 plugin.UserAgent = httpOpts.UserAgent
4445 plugin.Proxy = httpOpts.Proxy
4546 plugin.Timeout = httpOpts.Timeout
47 plugin.NoTLSValidation = httpOpts.NoTLSValidation
48 plugin.RetryOnTimeout = httpOpts.RetryOnTimeout
49 plugin.RetryAttempts = httpOpts.RetryAttempts
4650
4751 plugin.MaxFilesToList, err = cmdS3.Flags().GetInt("maxfiles")
4852 if err != nil {
5256 return globalopts, plugin, nil
5357 }
5458
59 // nolint:gochecknoinits
5560 func init() {
5661 cmdS3 = &cobra.Command{
5762 Use: "s3",
66 "github.com/spf13/cobra"
77 )
88
9 // nolint:gochecknoglobals
910 var cmdVersion *cobra.Command
1011
1112 func runVersion(cmd *cobra.Command, args []string) error {
1314 return nil
1415 }
1516
17 // nolint:gochecknoinits
1618 func init() {
1719 cmdVersion = &cobra.Command{
1820 Use: "version",
99 "github.com/spf13/cobra"
1010 )
1111
12 // nolint:gochecknoglobals
1213 var cmdVhost *cobra.Command
1314
1415 func runVhost(cmd *cobra.Command, args []string) error {
1718 return fmt.Errorf("error on parsing arguments: %w", err)
1819 }
1920
20 plugin, err := gobustervhost.NewGobusterVhost(mainContext, globalopts, pluginopts)
21 plugin, err := gobustervhost.NewGobusterVhost(globalopts, pluginopts)
2122 if err != nil {
2223 return fmt.Errorf("error on creating gobustervhost: %w", err)
2324 }
5051 plugin.NoTLSValidation = httpOpts.NoTLSValidation
5152 plugin.Headers = httpOpts.Headers
5253 plugin.Method = httpOpts.Method
54 plugin.RetryOnTimeout = httpOpts.RetryOnTimeout
55 plugin.RetryAttempts = httpOpts.RetryAttempts
56
57 plugin.AppendDomain, err = cmdVhost.Flags().GetBool("append-domain")
58 if err != nil {
59 return nil, nil, fmt.Errorf("invalid value for append-domain: %w", err)
60 }
61
62 plugin.ExcludeLength, err = cmdVhost.Flags().GetIntSlice("exclude-length")
63 if err != nil {
64 return nil, nil, fmt.Errorf("invalid value for excludelength: %w", err)
65 }
66
67 plugin.Domain, err = cmdVhost.Flags().GetString("domain")
68 if err != nil {
69 return nil, nil, fmt.Errorf("invalid value for domain: %w", err)
70 }
5371
5472 return globalopts, &plugin, nil
5573 }
5674
75 // nolint:gochecknoinits
5776 func init() {
5877 cmdVhost = &cobra.Command{
5978 Use: "vhost",
60 Short: "Uses VHOST enumeration mode",
79 Short: "Uses VHOST enumeration mode (you most probably want to use the IP adress as the URL parameter",
6180 RunE: runVhost,
6281 }
6382 if err := addCommonHTTPOptions(cmdVhost); err != nil {
6483 log.Fatalf("%v", err)
6584 }
85 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.")
86 cmdVhost.Flags().IntSlice("exclude-length", []int{}, "exclude the following content length (completely ignores the status). Supply multiple times to exclude multiple sizes.")
87 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")
6688
6789 cmdVhost.PersistentPreRun = func(cmd *cobra.Command, args []string) {
6890 configureGlobalOptions()
22 import (
33 "context"
44 "fmt"
5 "io/ioutil"
5 "io"
66 "log"
77 "os"
88 "testing"
2121 pluginopts.URL = h.URL
2222 pluginopts.Timeout = 10 * time.Second
2323
24 wordlist, err := ioutil.TempFile("", "")
24 wordlist, err := os.CreateTemp("", "")
2525 if err != nil {
2626 b.Fatalf("could not create tempfile: %v", err)
2727 }
4747 }
4848 defer devnull.Close()
4949 log.SetFlags(0)
50 log.SetOutput(ioutil.Discard)
50 log.SetOutput(io.Discard)
5151
5252 // Run the real benchmark
5353 for x := 0; x < b.N; x++ {
5454 os.Stdout = devnull
5555 os.Stderr = devnull
56 plugin, err := gobustervhost.NewGobusterVhost(ctx, &globalopts, &pluginopts)
56 plugin, err := gobustervhost.NewGobusterVhost(&globalopts, &pluginopts)
5757 if err != nil {
5858 b.Fatalf("error on creating gobusterdir: %v", err)
5959 }
0 //go:build !windows
1
2 package cli
3
4 const (
5 TERMINAL_CLEAR_LINE = "\r\x1b[2K"
6 )
0 //go:build windows
1
2 package cli
3
4 const (
5 TERMINAL_CLEAR_LINE = "\r\r"
6 )
1818 fmt.Println("by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)")
1919 }
2020
21 type outputType struct {
22 Mu *sync.RWMutex
23 MaxCharsWritten int
24 }
25
26 // right pad a string
27 // nolint:unparam
28 func rightPad(s string, padStr string, overallLen int) string {
29 strLen := len(s)
30 if overallLen <= strLen {
31 return s
32 }
33
34 toPad := overallLen - strLen - 1
35 pad := strings.Repeat(padStr, toPad)
36 return fmt.Sprintf("%s%s", s, pad)
37 }
38
3921 // resultWorker outputs the results as they come in. This needs to be a range and should not handle
4022 // the context so the channel always has a receiver and libgobuster will not block.
41 func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup, output *outputType) {
23 func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup) {
4224 defer wg.Done()
4325
4426 var f *os.File
5133 defer f.Close()
5234 }
5335
54 for r := range g.Results() {
36 for r := range g.Progress.ResultChan {
5537 s, err := r.ResultToString()
5638 if err != nil {
5739 g.LogError.Fatal(err)
5840 }
5941 if s != "" {
6042 s = strings.TrimSpace(s)
61 output.Mu.Lock()
62 w, _ := fmt.Printf("\r%s\n", rightPad(s, " ", output.MaxCharsWritten))
63 // -1 to remove the newline, otherwise it's always bigger
64 if (w - 1) > output.MaxCharsWritten {
65 output.MaxCharsWritten = w - 1
66 }
67 output.Mu.Unlock()
43 _, _ = fmt.Printf("%s%s\n", TERMINAL_CLEAR_LINE, s)
6844 if f != nil {
6945 err = writeToFile(f, s)
7046 if err != nil {
7753
7854 // errorWorker outputs the errors as they come in. This needs to be a range and should not handle
7955 // the context so the channel always has a receiver and libgobuster will not block.
80 func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup, output *outputType) {
56 func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup) {
8157 defer wg.Done()
8258
83 for e := range g.Errors() {
59 for e := range g.Progress.ErrorChan {
8460 if !g.Opts.Quiet && !g.Opts.NoError {
85 output.Mu.Lock()
86 g.LogError.Printf("[!] %v", e)
87 output.Mu.Unlock()
61 g.LogError.Printf("[!] %s\n", e.Error())
8862 }
8963 }
9064 }
9165
9266 // progressWorker outputs the progress every tick. It will stop once cancel() is called
9367 // on the context
94 func progressWorker(c context.Context, g *libgobuster.Gobuster, wg *sync.WaitGroup, output *outputType) {
68 func progressWorker(ctx context.Context, g *libgobuster.Gobuster, wg *sync.WaitGroup) {
9569 defer wg.Done()
9670
9771 tick := time.NewTicker(cliProgressUpdate)
10074 select {
10175 case <-tick.C:
10276 if !g.Opts.Quiet && !g.Opts.NoProgress {
103 g.RequestsCountMutex.RLock()
104 output.Mu.Lock()
105 var charsWritten int
77 requestsIssued := g.Progress.RequestsIssued()
78 requestsExpected := g.Progress.RequestsExpected()
10679 if g.Opts.Wordlist == "-" {
107 s := fmt.Sprintf("\rProgress: %d", g.RequestsIssued)
108 s = rightPad(s, " ", output.MaxCharsWritten)
109 charsWritten, _ = fmt.Fprint(os.Stderr, s)
80 s := fmt.Sprintf("%sProgress: %d", TERMINAL_CLEAR_LINE, requestsIssued)
81 _, _ = fmt.Fprint(os.Stderr, s)
11082 // only print status if we already read in the wordlist
111 } else if g.RequestsExpected > 0 {
112 s := fmt.Sprintf("\rProgress: %d / %d (%3.2f%%)", g.RequestsIssued, g.RequestsExpected, float32(g.RequestsIssued)*100.0/float32(g.RequestsExpected))
113 s = rightPad(s, " ", output.MaxCharsWritten)
114 charsWritten, _ = fmt.Fprint(os.Stderr, s)
83 } else if requestsExpected > 0 {
84 s := fmt.Sprintf("%sProgress: %d / %d (%3.2f%%)", TERMINAL_CLEAR_LINE, requestsIssued, requestsExpected, float32(requestsIssued)*100.0/float32(requestsExpected))
85 _, _ = fmt.Fprint(os.Stderr, s)
11586 }
116 if charsWritten > output.MaxCharsWritten {
117 output.MaxCharsWritten = charsWritten
118 }
119
120 output.Mu.Unlock()
121 g.RequestsCountMutex.RUnlock()
12287 }
123 case <-c.Done():
88 case <-ctx.Done():
12489 return
12590 }
12691 }
135100 }
136101
137102 // Gobuster is the main entry point for the CLI
138 func Gobuster(prevCtx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin) error {
103 func Gobuster(ctx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin) error {
139104 // Sanity checks
140105 if opts == nil {
141106 return fmt.Errorf("please provide valid options")
145110 return fmt.Errorf("please provide a valid plugin")
146111 }
147112
148 ctx, cancel := context.WithCancel(prevCtx)
113 ctxCancel, cancel := context.WithCancel(ctx)
149114 defer cancel()
150115
151 gobuster, err := libgobuster.NewGobuster(ctx, opts, plugin)
116 gobuster, err := libgobuster.NewGobuster(opts, plugin)
152117 if err != nil {
153118 return err
154119 }
172137 // when we call wg.Wait()
173138 var wg sync.WaitGroup
174139
175 outputMutex := new(sync.RWMutex)
176 o := &outputType{
177 Mu: outputMutex,
178 MaxCharsWritten: 0,
179 }
140 wg.Add(1)
141 go resultWorker(gobuster, opts.OutputFilename, &wg)
180142
181143 wg.Add(1)
182 go resultWorker(gobuster, opts.OutputFilename, &wg, o)
183
184 wg.Add(1)
185 go errorWorker(gobuster, &wg, o)
144 go errorWorker(gobuster, &wg)
186145
187146 if !opts.Quiet && !opts.NoProgress {
188147 // if not quiet add a new workgroup entry and start the goroutine
189148 wg.Add(1)
190 go progressWorker(ctx, gobuster, &wg, o)
149 go progressWorker(ctxCancel, gobuster, &wg)
191150 }
192151
193 err = gobuster.Start()
152 err = gobuster.Run(ctxCancel)
194153
195154 // call cancel func so progressWorker will exit (the only goroutine in this
196155 // file using the context) and to free resources
204163 }
205164
206165 if !opts.Quiet {
207 // clear stderr progress
208 fmt.Fprintf(os.Stderr, "\r%s\n", rightPad("", " ", o.MaxCharsWritten))
209166 fmt.Println(ruler)
210167 gobuster.LogInfo.Println("Finished")
211168 fmt.Println(ruler)
00 // cSpell Settings
11 {
2 // Version of the setting file. Always 0.1
3 "version": "0.1",
2 // Version of the setting file. Always 0.2
3 "version": "0.2",
44 // language - current active spelling language
55 "language": "en",
66 // words - list of words to be always considered correct
1212 "gobusterdns",
1313 "gobusterfuzz",
1414 "gobustervhost",
15 "gobustergcs",
1516 "vhost",
1617 "vhosts",
1718 "cname",
2728 "unconvert",
2829 "unparam",
2930 "prealloc",
30 "gochecknoglobals"
31 "gochecknoglobals",
32 "gochecknoinits",
33 "fatih",
34 "netip"
3135 ],
3236 // flagWords - list of words to be always considered incorrect
3337 // This is useful for offensive words and common spelling errors.
00 module github.com/OJ/gobuster/v3
11
2 go 1.19
3
24 require (
3 github.com/google/uuid v1.1.1
4 github.com/spf13/cobra v1.0.0
5 github.com/spf13/pflag v1.0.5 // indirect
6 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
7 golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed // indirect
5 github.com/fatih/color v1.13.0
6 github.com/google/uuid v1.3.0
7 github.com/spf13/cobra v1.5.0
8 golang.org/x/term v0.0.0-20220919170432-7a66f970e087
89 )
910
10 go 1.15
11 require (
12 github.com/inconshreveable/mousetrap v1.0.1 // indirect
13 github.com/mattn/go-colorable v0.1.13 // indirect
14 github.com/mattn/go-isatty v0.0.16 // indirect
15 github.com/spf13/pflag v1.0.5 // indirect
16 golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
17 )
+26
-138
go.sum less more
0 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
1 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
3 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
4 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
5 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
6 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
7 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
8 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
9 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
10 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
11 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
12 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
13 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
14 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
15 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
16 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
17 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
19 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
20 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
21 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
22 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
23 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
24 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
25 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
26 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
27 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
28 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
29 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
30 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
31 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
32 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
33 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
34 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
35 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
36 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
37 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
38 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
39 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
40 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
41 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
42 github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
0 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1 github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
2 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
3 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
4 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
435 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
44 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
45 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
46 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
47 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
48 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
49 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
50 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
51 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
52 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
53 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
54 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
55 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
56 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
57 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
58 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
59 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
60 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
61 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
62 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
63 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
64 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
65 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
66 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
67 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
68 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
69 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
70 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
71 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
72 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
73 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
74 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
75 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
76 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
77 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
78 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
79 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
80 github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
81 github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
82 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
83 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
84 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
6 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
7 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
8 github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
9 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
10 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
11 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
12 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
13 github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
14 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
15 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
16 github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
17 github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
8518 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
8619 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
87 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
88 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
89 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
90 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
91 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
92 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
93 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
94 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
95 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
96 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
97 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
98 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
99 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
100 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
101 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
102 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
103 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
104 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
105 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
106 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
107 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
108 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
109 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
110 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
111 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
112 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
113 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
114 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
115 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
116 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
117 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
118 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
119 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
120 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
121 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
122 golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed h1:WBkVNH1zd9jg/dK4HCM4lNANnmd12EHC9z+LmcCG4ns=
123 golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
124 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
125 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
126 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
127 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
128 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
129 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
130 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
131 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
132 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
133 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
134 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
20 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
21 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
22 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24 golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM=
25 golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
26 golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
27 golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
13528 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
136 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
137 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
138 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
139 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
140 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
141 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
29 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
44 "bytes"
55 "context"
66 "fmt"
7 "net"
8 "net/http"
79 "strings"
810 "text/tabwriter"
911
3234
3335 // GobusterDir is the main type to implement the interface
3436 type GobusterDir struct {
35 options *OptionsDir
36 globalopts *libgobuster.Options
37 http *libgobuster.HTTPClient
38 requestsPerRun *int // helper variable so we do not recalculate this over and over
37 options *OptionsDir
38 globalopts *libgobuster.Options
39 http *libgobuster.HTTPClient
3940 }
4041
4142 // NewGobusterDir creates a new initialized GobusterDir
42 func NewGobusterDir(cont context.Context, globalopts *libgobuster.Options, opts *OptionsDir) (*GobusterDir, error) {
43 func NewGobusterDir(globalopts *libgobuster.Options, opts *OptionsDir) (*GobusterDir, error) {
4344 if globalopts == nil {
4445 return nil, fmt.Errorf("please provide valid global options")
4546 }
5455 }
5556
5657 basicOptions := libgobuster.BasicHTTPOptions{
57 Proxy: opts.Proxy,
58 Timeout: opts.Timeout,
59 UserAgent: opts.UserAgent,
58 Proxy: opts.Proxy,
59 Timeout: opts.Timeout,
60 UserAgent: opts.UserAgent,
61 NoTLSValidation: opts.NoTLSValidation,
62 RetryOnTimeout: opts.RetryOnTimeout,
63 RetryAttempts: opts.RetryAttempts,
6064 }
6165
6266 httpOpts := libgobuster.HTTPOptions{
6367 BasicHTTPOptions: basicOptions,
6468 FollowRedirect: opts.FollowRedirect,
65 NoTLSValidation: opts.NoTLSValidation,
6669 Username: opts.Username,
6770 Password: opts.Password,
6871 Headers: opts.Headers,
7073 Method: opts.Method,
7174 }
7275
73 h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
76 h, err := libgobuster.NewHTTPClient(&httpOpts)
7477 if err != nil {
7578 return nil, err
7679 }
8487 return "directory enumeration"
8588 }
8689
87 // RequestsPerRun returns the number of requests this plugin makes per single wordlist item
88 func (d *GobusterDir) RequestsPerRun() int {
89 if d.requestsPerRun != nil {
90 return *d.requestsPerRun
91 }
92
93 num := 1 + len(d.options.ExtensionsParsed.Set)
94 if d.options.DiscoverBackup {
95 // default word
96 num += len(backupExtensions)
97 num += len(backupDotExtensions)
98 // backups of filenames
99 num += len(d.options.ExtensionsParsed.Set) * len(backupExtensions)
100 num += len(d.options.ExtensionsParsed.Set) * len(backupDotExtensions)
101 }
102 d.requestsPerRun = &num
103
104 return *d.requestsPerRun
105 }
106
10790 // PreRun is the pre run implementation of gobusterdir
108 func (d *GobusterDir) PreRun() error {
91 func (d *GobusterDir) PreRun(ctx context.Context) error {
10992 // add trailing slash
11093 if !strings.HasSuffix(d.options.URL, "/") {
11194 d.options.URL = fmt.Sprintf("%s/", d.options.URL)
11295 }
11396
114 _, _, _, _, err := d.http.Request(d.options.URL, libgobuster.RequestOptions{})
97 _, _, _, _, err := d.http.Request(ctx, d.options.URL, libgobuster.RequestOptions{})
11598 if err != nil {
11699 return fmt.Errorf("unable to connect to %s: %w", d.options.URL, err)
117100 }
122105 url = fmt.Sprintf("%s/", url)
123106 }
124107
125 wildcardResp, wildcardLength, _, _, err := d.http.Request(url, libgobuster.RequestOptions{})
108 wildcardResp, wildcardLength, _, _, err := d.http.Request(ctx, url, libgobuster.RequestOptions{})
126109 if err != nil {
127110 return err
128111 }
133116 }
134117
135118 if d.options.StatusCodesBlacklistParsed.Length() > 0 {
136 if !d.options.StatusCodesBlacklistParsed.Contains(*wildcardResp) && !d.options.WildcardForced {
137 return &ErrWildcard{url: url, statusCode: *wildcardResp, length: wildcardLength}
119 if !d.options.StatusCodesBlacklistParsed.Contains(wildcardResp) {
120 return &ErrWildcard{url: url, statusCode: wildcardResp, length: wildcardLength}
138121 }
139122 } else if d.options.StatusCodesParsed.Length() > 0 {
140 if d.options.StatusCodesParsed.Contains(*wildcardResp) && !d.options.WildcardForced {
141 return &ErrWildcard{url: url, statusCode: *wildcardResp, length: wildcardLength}
123 if d.options.StatusCodesParsed.Contains(wildcardResp) {
124 return &ErrWildcard{url: url, statusCode: wildcardResp, length: wildcardLength}
142125 }
143126 } else {
144127 return fmt.Errorf("StatusCodes and StatusCodesBlacklist are both not set which should not happen")
162145 return ret
163146 }
164147
165 // Run is the process implementation of gobusterdir
166 func (d *GobusterDir) Run(word string, resChannel chan<- libgobuster.Result) error {
167 suffix := ""
168 if d.options.UseSlash {
169 suffix = "/"
170 }
171
148 func (d *GobusterDir) AdditionalWords(word string) []string {
149 var words []string
172150 // build list of urls to check
173151 // 1: No extension
174152 // 2: With extension
175153 // 3: backupextension
176 urlsToCheck := make(map[string]string)
177 entity := fmt.Sprintf("%s%s", word, suffix)
178 dirURL := fmt.Sprintf("%s%s", d.options.URL, entity)
179 urlsToCheck[entity] = dirURL
180154 if d.options.DiscoverBackup {
181 for _, u := range getBackupFilenames(word) {
182 url := fmt.Sprintf("%s%s", d.options.URL, u)
183 urlsToCheck[u] = url
184 }
155 words = append(words, getBackupFilenames(word)...)
185156 }
186157 for ext := range d.options.ExtensionsParsed.Set {
187158 filename := fmt.Sprintf("%s.%s", word, ext)
188 url := fmt.Sprintf("%s%s", d.options.URL, filename)
189 urlsToCheck[filename] = url
159 words = append(words, filename)
190160 if d.options.DiscoverBackup {
191 for _, u := range getBackupFilenames(filename) {
192 url2 := fmt.Sprintf("%s%s", d.options.URL, u)
193 urlsToCheck[u] = url2
161 words = append(words, getBackupFilenames(filename)...)
162 }
163 }
164 return words
165 }
166
167 // ProcessWord is the process implementation of gobusterdir
168 func (d *GobusterDir) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
169 suffix := ""
170 if d.options.UseSlash {
171 suffix = "/"
172 }
173 entity := fmt.Sprintf("%s%s", word, suffix)
174 url := fmt.Sprintf("%s%s", d.options.URL, entity)
175
176 tries := 1
177 if d.options.RetryOnTimeout && d.options.RetryAttempts > 0 {
178 // add it so it will be the overall max requests
179 tries += d.options.RetryAttempts
180 }
181
182 var statusCode int
183 var size int64
184 var header http.Header
185 for i := 1; i <= tries; i++ {
186 var err error
187 statusCode, size, header, _, err = d.http.Request(ctx, url, libgobuster.RequestOptions{})
188 if err != nil {
189 // check if it's a timeout and if we should try again and try again
190 // otherwise the timeout error is raised
191 if netErr, ok := err.(net.Error); ok && netErr.Timeout() && i != tries {
192 continue
193 } else if strings.Contains(err.Error(), "invalid control character in URL") {
194 // put error in error chan so it's printed out and ignore it
195 // so gobuster will not quit
196 progress.ErrorChan <- err
197 continue
198 } else {
199 return err
194200 }
195201 }
196 }
197
198 for entity, url := range urlsToCheck {
199 statusCode, size, header, _, err := d.http.Request(url, libgobuster.RequestOptions{})
200 if err != nil {
201 return err
202 }
203 if statusCode != nil {
204 resultStatus := false
205
206 if d.options.StatusCodesBlacklistParsed.Length() > 0 {
207 if !d.options.StatusCodesBlacklistParsed.Contains(*statusCode) {
208 resultStatus = true
209 }
210 } else if d.options.StatusCodesParsed.Length() > 0 {
211 if d.options.StatusCodesParsed.Contains(*statusCode) {
212 resultStatus = true
213 }
214 } else {
215 return fmt.Errorf("StatusCodes and StatusCodesBlacklist are both not set which should not happen")
202 break
203 }
204
205 if statusCode != 0 {
206 resultStatus := false
207
208 if d.options.StatusCodesBlacklistParsed.Length() > 0 {
209 if !d.options.StatusCodesBlacklistParsed.Contains(statusCode) {
210 resultStatus = true
216211 }
217
218 if (resultStatus && !helper.SliceContains(d.options.ExcludeLength, int(size))) || d.globalopts.Verbose {
219 resChannel <- Result{
220 URL: d.options.URL,
221 Path: entity,
222 Verbose: d.globalopts.Verbose,
223 Expanded: d.options.Expanded,
224 NoStatus: d.options.NoStatus,
225 HideLength: d.options.HideLength,
226 Found: resultStatus,
227 Header: header,
228 StatusCode: *statusCode,
229 Size: size,
230 }
212 } else if d.options.StatusCodesParsed.Length() > 0 {
213 if d.options.StatusCodesParsed.Contains(statusCode) {
214 resultStatus = true
215 }
216 } else {
217 return fmt.Errorf("StatusCodes and StatusCodesBlacklist are both not set which should not happen")
218 }
219
220 if (resultStatus && !helper.SliceContains(d.options.ExcludeLength, int(size))) || d.globalopts.Verbose {
221 progress.ResultChan <- Result{
222 URL: d.options.URL,
223 Path: entity,
224 Verbose: d.globalopts.Verbose,
225 Expanded: d.options.Expanded,
226 NoStatus: d.options.NoStatus,
227 HideLength: d.options.HideLength,
228 Found: resultStatus,
229 Header: header,
230 StatusCode: statusCode,
231 Size: size,
231232 }
232233 }
233234 }
77 type OptionsDir struct {
88 libgobuster.HTTPOptions
99 Extensions string
10 ExtensionsParsed libgobuster.StringSet
10 ExtensionsParsed libgobuster.Set[string]
1111 StatusCodes string
12 StatusCodesParsed libgobuster.IntSet
12 StatusCodesParsed libgobuster.Set[int]
1313 StatusCodesBlacklist string
14 StatusCodesBlacklistParsed libgobuster.IntSet
14 StatusCodesBlacklistParsed libgobuster.Set[int]
1515 UseSlash bool
16 WildcardForced bool
1716 HideLength bool
1817 Expanded bool
1918 NoStatus bool
2423 // NewOptionsDir returns a new initialized OptionsDir
2524 func NewOptionsDir() *OptionsDir {
2625 return &OptionsDir{
27 StatusCodesParsed: libgobuster.NewIntSet(),
28 StatusCodesBlacklistParsed: libgobuster.NewIntSet(),
29 ExtensionsParsed: libgobuster.NewStringSet(),
26 StatusCodesParsed: libgobuster.NewSet[int](),
27 StatusCodesBlacklistParsed: libgobuster.NewSet[int](),
28 ExtensionsParsed: libgobuster.NewSet[string](),
3029 }
3130 }
33 "bytes"
44 "fmt"
55 "net/http"
6
7 "github.com/fatih/color"
8 )
9
10 var (
11 white = color.New(color.FgWhite).FprintfFunc()
12 yellow = color.New(color.FgYellow).FprintfFunc()
13 green = color.New(color.FgGreen).FprintfFunc()
14 blue = color.New(color.FgBlue).FprintfFunc()
15 red = color.New(color.FgRed).FprintfFunc()
16 cyan = color.New(color.FgCyan).FprintfFunc()
617 )
718
819 // Result represents a single result
1930 Size int64
2031 }
2132
22 // ToString converts the Result to it's textual representation
33 // ResultToString converts the Result to it's textual representation
2334 func (r Result) ResultToString() (string, error) {
2435 buf := &bytes.Buffer{}
2536
5061 }
5162
5263 if !r.NoStatus {
53 if _, err := fmt.Fprintf(buf, " (Status: %d)", r.StatusCode); err != nil {
54 return "", err
64 color := white
65 if r.StatusCode == 200 {
66 color = green
67 } else if r.StatusCode >= 300 && r.StatusCode < 400 {
68 color = cyan
69 } else if r.StatusCode >= 400 && r.StatusCode < 500 {
70 color = yellow
71 } else if r.StatusCode >= 500 && r.StatusCode < 600 {
72 color = red
5573 }
74
75 color(buf, " (Status: %d)", r.StatusCode)
5676 }
5777
5878 if !r.HideLength {
6383
6484 location := r.Header.Get("Location")
6585 if location != "" {
66 if _, err := fmt.Fprintf(buf, " [--> %s]", location); err != nil {
67 return "", err
68 }
86 blue(buf, " [--> %s]", location)
6987 }
7088
7189 if _, err := fmt.Fprintf(buf, "\n"); err != nil {
66 "fmt"
77 "log"
88 "net"
9 "net/netip"
910 "strings"
1011 "text/tabwriter"
1112 "time"
1617
1718 // ErrWildcard is returned if a wildcard response is found
1819 type ErrWildcard struct {
19 wildcardIps libgobuster.StringSet
20 wildcardIps libgobuster.Set[netip.Addr]
2021 }
2122
2223 // Error is the implementation of the error interface
3031 globalopts *libgobuster.Options
3132 options *OptionsDNS
3233 isWildcard bool
33 wildcardIps libgobuster.StringSet
34 wildcardIps libgobuster.Set[netip.Addr]
3435 }
3536
3637 func newCustomDialer(server string) func(ctx context.Context, network, address string) (net.Conn, error) {
6465 g := GobusterDNS{
6566 options: opts,
6667 globalopts: globalopts,
67 wildcardIps: libgobuster.NewStringSet(),
68 wildcardIps: libgobuster.NewSet[netip.Addr](),
6869 resolver: resolver,
6970 }
7071 return &g, nil
7576 return "DNS enumeration"
7677 }
7778
78 // RequestsPerRun returns the number of requests this plugin makes per single wordlist item
79 func (d *GobusterDNS) RequestsPerRun() int {
80 return 1
81 }
82
8379 // PreRun is the pre run implementation of gobusterdns
84 func (d *GobusterDNS) PreRun() error {
80 func (d *GobusterDNS) PreRun(ctx context.Context) error {
8581 // Resolve a subdomain that probably shouldn't exist
8682 guid := uuid.New()
87 wildcardIps, err := d.dnsLookup(fmt.Sprintf("%s.%s", guid, d.options.Domain))
83 wildcardIps, err := d.dnsLookup(ctx, fmt.Sprintf("%s.%s", guid, d.options.Domain))
8884 if err == nil {
8985 d.isWildcard = true
9086 d.wildcardIps.AddRange(wildcardIps)
9591
9692 if !d.globalopts.Quiet {
9793 // Provide a warning if the base domain doesn't resolve (in case of typo)
98 _, err = d.dnsLookup(d.options.Domain)
94 _, err = d.dnsLookup(ctx, d.options.Domain)
9995 if err != nil {
10096 // Not an error, just a warning. Eg. `yp.to` doesn't resolve, but `cr.yp.to` does!
10197 log.Printf("[-] Unable to validate base domain: %s (%v)", d.options.Domain, err)
105101 return nil
106102 }
107103
108 // Run is the process implementation of gobusterdns
109 func (d *GobusterDNS) Run(word string, resChannel chan<- libgobuster.Result) error {
104 // ProcessWord is the process implementation of gobusterdns
105 func (d *GobusterDNS) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
110106 subdomain := fmt.Sprintf("%s.%s", word, d.options.Domain)
111 ips, err := d.dnsLookup(subdomain)
107 ips, err := d.dnsLookup(ctx, subdomain)
112108 if err == nil {
113109 if !d.isWildcard || !d.wildcardIps.ContainsAny(ips) {
114110 result := Result{
120116 if d.options.ShowIPs {
121117 result.IPs = ips
122118 } else if d.options.ShowCNAME {
123 cname, err := d.dnsLookupCname(subdomain)
119 cname, err := d.dnsLookupCname(ctx, subdomain)
124120 if err == nil {
125121 result.CNAME = cname
126122 }
127123 }
128 resChannel <- result
124 progress.ResultChan <- result
129125 }
130126 } else if d.globalopts.Verbose {
131 resChannel <- Result{
127 progress.ResultChan <- Result{
132128 Subdomain: subdomain,
133129 Found: false,
134130 ShowIPs: d.options.ShowIPs,
136132 }
137133 }
138134 return nil
135 }
136
137 func (d *GobusterDNS) AdditionalWords(word string) []string {
138 return []string{}
139139 }
140140
141141 // GetConfigString returns the string representation of the current config
218218 return strings.TrimSpace(buffer.String()), nil
219219 }
220220
221 func (d *GobusterDNS) dnsLookup(domain string) ([]string, error) {
222 ctx, cancel := context.WithTimeout(context.Background(), d.options.Timeout)
221 func (d *GobusterDNS) dnsLookup(ctx context.Context, domain string) ([]netip.Addr, error) {
222 ctx2, cancel := context.WithTimeout(ctx, d.options.Timeout)
223223 defer cancel()
224 return d.resolver.LookupHost(ctx, domain)
225 }
226
227 func (d *GobusterDNS) dnsLookupCname(domain string) (string, error) {
228 ctx, cancel := context.WithTimeout(context.Background(), d.options.Timeout)
224 return d.resolver.LookupNetIP(ctx2, "ip", domain)
225 }
226
227 func (d *GobusterDNS) dnsLookupCname(ctx context.Context, domain string) (string, error) {
228 ctx2, cancel := context.WithTimeout(ctx, d.options.Timeout)
229229 defer cancel()
230230 time.Sleep(time.Second)
231 return d.resolver.LookupCNAME(ctx, domain)
232 }
231 return d.resolver.LookupCNAME(ctx2, domain)
232 }
11
22 import (
33 "bytes"
4 "fmt"
4 "net/netip"
55 "strings"
6
7 "github.com/fatih/color"
8 )
9
10 var (
11 yellow = color.New(color.FgYellow).FprintfFunc()
12 green = color.New(color.FgGreen).FprintfFunc()
613 )
714
815 // Result represents a single result
1118 ShowCNAME bool
1219 Found bool
1320 Subdomain string
14 IPs []string
21 IPs []netip.Addr
1522 CNAME string
1623 }
1724
18 // ToString converts the Result to it's textual representation
25 // ResultToString converts the Result to it's textual representation
1926 func (r Result) ResultToString() (string, error) {
2027 buf := &bytes.Buffer{}
2128
29 c := green
30
2231 if r.Found {
23 if _, err := fmt.Fprintf(buf, "Found: "); err != nil {
24 return "", err
25 }
32 c(buf, "Found: ")
2633 } else {
27 if _, err := fmt.Fprintf(buf, "Missed: "); err != nil {
28 return "", err
29 }
34 c = yellow
35 c(buf, "Missed: ")
3036 }
3137
3238 if r.ShowIPs && r.Found {
33 if _, err := fmt.Fprintf(buf, "%s [%s]\n", r.Subdomain, strings.Join(r.IPs, ",")); err != nil {
34 return "", err
39 ips := make([]string, len(r.IPs))
40 for i := range r.IPs {
41 ips[i] = r.IPs[i].String()
3542 }
43 c(buf, "%s [%s]\n", r.Subdomain, strings.Join(ips, ","))
3644 } else if r.ShowCNAME && r.Found && r.CNAME != "" {
37 if _, err := fmt.Fprintf(buf, "%s [%s]\n", r.Subdomain, r.CNAME); err != nil {
38 return "", err
39 }
45 c(buf, "%s [%s]\n", r.Subdomain, r.CNAME)
4046 } else {
41 if _, err := fmt.Fprintf(buf, "%s\n", r.Subdomain); err != nil {
42 return "", err
43 }
47 c(buf, "%s\n", r.Subdomain)
4448 }
4549
4650 s := buf.String()
44 "bytes"
55 "context"
66 "fmt"
7 "net"
78 "strings"
89 "text/tabwriter"
910
3031 }
3132
3233 // NewGobusterFuzz creates a new initialized GobusterFuzz
33 func NewGobusterFuzz(cont context.Context, globalopts *libgobuster.Options, opts *OptionsFuzz) (*GobusterFuzz, error) {
34 func NewGobusterFuzz(globalopts *libgobuster.Options, opts *OptionsFuzz) (*GobusterFuzz, error) {
3435 if globalopts == nil {
3536 return nil, fmt.Errorf("please provide valid global options")
3637 }
4546 }
4647
4748 basicOptions := libgobuster.BasicHTTPOptions{
48 Proxy: opts.Proxy,
49 Timeout: opts.Timeout,
50 UserAgent: opts.UserAgent,
49 Proxy: opts.Proxy,
50 Timeout: opts.Timeout,
51 UserAgent: opts.UserAgent,
52 NoTLSValidation: opts.NoTLSValidation,
53 RetryOnTimeout: opts.RetryOnTimeout,
54 RetryAttempts: opts.RetryAttempts,
5155 }
5256
5357 httpOpts := libgobuster.HTTPOptions{
5458 BasicHTTPOptions: basicOptions,
5559 FollowRedirect: opts.FollowRedirect,
56 NoTLSValidation: opts.NoTLSValidation,
5760 Username: opts.Username,
5861 Password: opts.Password,
5962 Headers: opts.Headers,
6164 Method: opts.Method,
6265 }
6366
64 h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
67 h, err := libgobuster.NewHTTPClient(&httpOpts)
6568 if err != nil {
6669 return nil, err
6770 }
7477 return "fuzzing"
7578 }
7679
77 // RequestsPerRun returns the number of requests this plugin makes per single wordlist item
78 func (d *GobusterFuzz) RequestsPerRun() int {
79 return 1
80 }
81
8280 // PreRun is the pre run implementation of gobusterfuzz
83 func (d *GobusterFuzz) PreRun() error {
81 func (d *GobusterFuzz) PreRun(ctx context.Context) error {
8482 return nil
8583 }
8684
87 // Run is the process implementation of gobusterfuzz
88 func (d *GobusterFuzz) Run(word string, resChannel chan<- libgobuster.Result) error {
89 workingURL := strings.ReplaceAll(d.options.URL, "FUZZ", word)
90 statusCode, size, _, _, err := d.http.Request(workingURL, libgobuster.RequestOptions{})
91 if err != nil {
92 return err
93 }
94 if statusCode != nil {
85 // ProcessWord is the process implementation of gobusterfuzz
86 func (d *GobusterFuzz) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
87 url := strings.ReplaceAll(d.options.URL, "FUZZ", word)
88
89 tries := 1
90 if d.options.RetryOnTimeout && d.options.RetryAttempts > 0 {
91 // add it so it will be the overall max requests
92 tries += d.options.RetryAttempts
93 }
94
95 var statusCode int
96 var size int64
97 for i := 1; i <= tries; i++ {
98 var err error
99 statusCode, size, _, _, err = d.http.Request(ctx, url, libgobuster.RequestOptions{})
100 if err != nil {
101 // check if it's a timeout and if we should try again and try again
102 // otherwise the timeout error is raised
103 if netErr, ok := err.(net.Error); ok && netErr.Timeout() && i != tries {
104 continue
105 } else if strings.Contains(err.Error(), "invalid control character in URL") {
106 // put error in error chan so it's printed out and ignore it
107 // so gobuster will not quit
108 progress.ErrorChan <- err
109 continue
110 } else {
111 return err
112 }
113 }
114 break
115 }
116
117 if statusCode != 0 {
95118 resultStatus := true
96119
97120 if helper.SliceContains(d.options.ExcludeLength, int(size)) {
99122 }
100123
101124 if d.options.ExcludedStatusCodesParsed.Length() > 0 {
102 if d.options.ExcludedStatusCodesParsed.Contains(*statusCode) {
125 if d.options.ExcludedStatusCodesParsed.Contains(statusCode) {
103126 resultStatus = false
104127 }
105128 }
106129
107130 if resultStatus || d.globalopts.Verbose {
108 resChannel <- Result{
131 progress.ResultChan <- Result{
109132 Verbose: d.globalopts.Verbose,
110133 Found: resultStatus,
111 Path: workingURL,
112 StatusCode: *statusCode,
134 Path: url,
135 StatusCode: statusCode,
113136 Size: size,
114137 }
115138 }
116139 }
117140 return nil
141 }
142
143 func (d *GobusterFuzz) AdditionalWords(word string) []string {
144 return []string{}
118145 }
119146
120147 // GetConfigString returns the string representation of the current config
77 type OptionsFuzz struct {
88 libgobuster.HTTPOptions
99 ExcludedStatusCodes string
10 ExcludedStatusCodesParsed libgobuster.IntSet
11 WildcardForced bool
10 ExcludedStatusCodesParsed libgobuster.Set[int]
1211 ExcludeLength []int
1312 }
1413
1514 // NewOptionsFuzz returns a new initialized OptionsFuzz
1615 func NewOptionsFuzz() *OptionsFuzz {
1716 return &OptionsFuzz{
18 ExcludedStatusCodesParsed: libgobuster.NewIntSet(),
17 ExcludedStatusCodesParsed: libgobuster.NewSet[int](),
1918 }
2019 }
11
22 import (
33 "bytes"
4 "fmt"
4
5 "github.com/fatih/color"
6 )
7
8 var (
9 yellow = color.New(color.FgYellow).FprintfFunc()
10 green = color.New(color.FgGreen).FprintfFunc()
511 )
612
713 // Result represents a single result
1319 Size int64
1420 }
1521
16 // ToString converts the Result to it's textual representation
22 // ResultToString converts the Result to it's textual representation
1723 func (r Result) ResultToString() (string, error) {
1824 buf := &bytes.Buffer{}
25
26 c := green
1927
2028 // Prefix if we're in verbose mode
2129 if r.Verbose {
2230 if r.Found {
23 if _, err := fmt.Fprintf(buf, "Found: "); err != nil {
24 return "", err
25 }
31 c(buf, "Found: ")
2632 } else {
27 if _, err := fmt.Fprintf(buf, "Missed: "); err != nil {
28 return "", err
29 }
33 c = yellow
34 c(buf, "Missed: ")
3035 }
3136 } else if r.Found {
32 if _, err := fmt.Fprintf(buf, "Found: "); err != nil {
33 return "", err
34 }
37 c(buf, "Found: ")
3538 }
3639
37 if _, err := fmt.Fprintf(buf, "[Status=%d] [Length=%d] %s", r.StatusCode, r.Size, r.Path); err != nil {
38 return "", err
39 }
40
41 if _, err := fmt.Fprintf(buf, "\n"); err != nil {
42 return "", err
43 }
40 c(buf, "[Status=%d] [Length=%d] %s", r.StatusCode, r.Size, r.Path)
41 c(buf, "\n")
4442
4543 s := buf.String()
4644 return s, nil
0 package gobustergcs
1
2 import (
3 "bufio"
4 "bytes"
5 "context"
6 "encoding/json"
7 "fmt"
8 "net"
9 "net/http"
10 "regexp"
11 "strings"
12 "text/tabwriter"
13
14 "github.com/OJ/gobuster/v3/libgobuster"
15 )
16
17 // GobusterGCS is the main type to implement the interface
18 type GobusterGCS struct {
19 options *OptionsGCS
20 globalopts *libgobuster.Options
21 http *libgobuster.HTTPClient
22 bucketRegex *regexp.Regexp
23 }
24
25 // NewGobusterGCS creates a new initialized GobusterGCS
26 func NewGobusterGCS(globalopts *libgobuster.Options, opts *OptionsGCS) (*GobusterGCS, error) {
27 if globalopts == nil {
28 return nil, fmt.Errorf("please provide valid global options")
29 }
30
31 if opts == nil {
32 return nil, fmt.Errorf("please provide valid plugin options")
33 }
34
35 g := GobusterGCS{
36 options: opts,
37 globalopts: globalopts,
38 }
39
40 basicOptions := libgobuster.BasicHTTPOptions{
41 Proxy: opts.Proxy,
42 Timeout: opts.Timeout,
43 UserAgent: opts.UserAgent,
44 NoTLSValidation: opts.NoTLSValidation,
45 RetryOnTimeout: opts.RetryOnTimeout,
46 RetryAttempts: opts.RetryAttempts,
47 }
48
49 httpOpts := libgobuster.HTTPOptions{
50 BasicHTTPOptions: basicOptions,
51 // needed so we can list bucket contents
52 FollowRedirect: true,
53 }
54
55 h, err := libgobuster.NewHTTPClient(&httpOpts)
56 if err != nil {
57 return nil, err
58 }
59 g.http = h
60 // https://cloud.google.com/storage/docs/naming-buckets
61 g.bucketRegex = regexp.MustCompile(`^[a-z0-9][a-z0-9\-_.]{1,61}[a-z0-9](\.[a-z0-9][a-z0-9\-_.]{1,61}[a-z0-9])*$`)
62
63 return &g, nil
64 }
65
66 // Name should return the name of the plugin
67 func (s *GobusterGCS) Name() string {
68 return "GCS bucket enumeration"
69 }
70
71 // PreRun is the pre run implementation of GobusterS3
72 func (s *GobusterGCS) PreRun(ctx context.Context) error {
73 return nil
74 }
75
76 // ProcessWord is the process implementation of GobusterS3
77 func (s *GobusterGCS) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
78 // only check for valid bucket names
79 if !s.isValidBucketName(word) {
80 return nil
81 }
82
83 bucketURL := fmt.Sprintf("https://storage.googleapis.com/storage/v1/b/%s/o?maxResults=%d", word, s.options.MaxFilesToList)
84
85 tries := 1
86 if s.options.RetryOnTimeout && s.options.RetryAttempts > 0 {
87 // add it so it will be the overall max requests
88 tries += s.options.RetryAttempts
89 }
90
91 var statusCode int
92 var body []byte
93 for i := 1; i <= tries; i++ {
94 var err error
95 statusCode, _, _, body, err = s.http.Request(ctx, bucketURL, libgobuster.RequestOptions{ReturnBody: true})
96 if err != nil {
97 // check if it's a timeout and if we should try again and try again
98 // otherwise the timeout error is raised
99 if netErr, ok := err.(net.Error); ok && netErr.Timeout() && i != tries {
100 continue
101 } else if strings.Contains(err.Error(), "invalid control character in URL") {
102 // put error in error chan so it's printed out and ignore it
103 // so gobuster will not quit
104 progress.ErrorChan <- err
105 continue
106 } else {
107 return err
108 }
109 }
110 break
111 }
112
113 if statusCode == 0 || body == nil {
114 return nil
115 }
116
117 // looks like 401, 403, and 404 are the only negative status codes
118 found := false
119 switch statusCode {
120 case http.StatusUnauthorized,
121 http.StatusForbidden,
122 http.StatusNotFound:
123 found = false
124 case http.StatusOK:
125 // listing enabled
126 found = true
127 default:
128 // default to found as we use negative status codes
129 found = true
130 }
131
132 // nothing found, bail out
133 // may add the result later if we want to enable verbose output
134 if !found {
135 return nil
136 }
137
138 extraStr := ""
139 if s.globalopts.Verbose {
140 // get status
141 var result map[string]interface{}
142 err := json.Unmarshal(body, &result)
143
144 if err != nil {
145 return fmt.Errorf("could not parse response json: %w", err)
146 }
147
148 if _, exist := result["error"]; exist {
149 // https://cloud.google.com/storage/docs/json_api/v1/status-codes
150 gcsError := GCSError{}
151 err := json.Unmarshal(body, &gcsError)
152 if err != nil {
153 return fmt.Errorf("could not parse error json: %w", err)
154 }
155 extraStr = fmt.Sprintf("Error: %s (%d)", gcsError.Error.Message, gcsError.Error.Code)
156 } else if v, exist := result["kind"]; exist && v == "storage#objects" {
157 // https://cloud.google.com/storage/docs/json_api/v1/status-codes
158 // bucket listing enabled
159 gcsListing := GCSListing{}
160 err := json.Unmarshal(body, &gcsListing)
161 if err != nil {
162 return fmt.Errorf("could not parse result json: %w", err)
163 }
164 extraStr = "Bucket Listing enabled: "
165 for _, x := range gcsListing.Items {
166 extraStr += fmt.Sprintf("%s (%sb), ", x.Name, x.Size)
167 }
168 extraStr = strings.TrimRight(extraStr, ", ")
169 }
170 }
171
172 progress.ResultChan <- Result{
173 Found: found,
174 BucketName: word,
175 Status: extraStr,
176 }
177
178 return nil
179 }
180
181 func (s *GobusterGCS) AdditionalWords(word string) []string {
182 return []string{}
183 }
184
185 // GetConfigString returns the string representation of the current config
186 func (s *GobusterGCS) GetConfigString() (string, error) {
187 var buffer bytes.Buffer
188 bw := bufio.NewWriter(&buffer)
189 tw := tabwriter.NewWriter(bw, 0, 5, 3, ' ', 0)
190 o := s.options
191
192 if _, err := fmt.Fprintf(tw, "[+] Threads:\t%d\n", s.globalopts.Threads); err != nil {
193 return "", err
194 }
195
196 if s.globalopts.Delay > 0 {
197 if _, err := fmt.Fprintf(tw, "[+] Delay:\t%s\n", s.globalopts.Delay); err != nil {
198 return "", err
199 }
200 }
201
202 wordlist := "stdin (pipe)"
203 if s.globalopts.Wordlist != "-" {
204 wordlist = s.globalopts.Wordlist
205 }
206 if _, err := fmt.Fprintf(tw, "[+] Wordlist:\t%s\n", wordlist); err != nil {
207 return "", err
208 }
209
210 if s.globalopts.PatternFile != "" {
211 if _, err := fmt.Fprintf(tw, "[+] Patterns:\t%s (%d entries)\n", s.globalopts.PatternFile, len(s.globalopts.Patterns)); err != nil {
212 return "", err
213 }
214 }
215
216 if o.Proxy != "" {
217 if _, err := fmt.Fprintf(tw, "[+] Proxy:\t%s\n", o.Proxy); err != nil {
218 return "", err
219 }
220 }
221
222 if o.UserAgent != "" {
223 if _, err := fmt.Fprintf(tw, "[+] User Agent:\t%s\n", o.UserAgent); err != nil {
224 return "", err
225 }
226 }
227
228 if _, err := fmt.Fprintf(tw, "[+] Timeout:\t%s\n", o.Timeout.String()); err != nil {
229 return "", err
230 }
231
232 if s.globalopts.Verbose {
233 if _, err := fmt.Fprintf(tw, "[+] Verbose:\ttrue\n"); err != nil {
234 return "", err
235 }
236 }
237
238 if _, err := fmt.Fprintf(tw, "[+] Maximum files to list:\t%d\n", o.MaxFilesToList); err != nil {
239 return "", err
240 }
241
242 if err := tw.Flush(); err != nil {
243 return "", fmt.Errorf("error on tostring: %w", err)
244 }
245
246 if err := bw.Flush(); err != nil {
247 return "", fmt.Errorf("error on tostring: %w", err)
248 }
249
250 return strings.TrimSpace(buffer.String()), nil
251 }
252
253 // https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
254 func (s *GobusterGCS) isValidBucketName(bucketName string) bool {
255 if len(bucketName) > 222 || !s.bucketRegex.MatchString(bucketName) {
256 return false
257 }
258 if strings.HasPrefix(bucketName, "-") || strings.HasSuffix(bucketName, "-") ||
259 strings.HasPrefix(bucketName, "_") || strings.HasSuffix(bucketName, "_") ||
260 strings.HasPrefix(bucketName, ".") || strings.HasSuffix(bucketName, ".") {
261 return false
262 }
263 return true
264 }
0 package gobustergcs
1
2 import (
3 "github.com/OJ/gobuster/v3/libgobuster"
4 )
5
6 // OptionsGCS is the struct to hold all options for this plugin
7 type OptionsGCS struct {
8 libgobuster.BasicHTTPOptions
9 MaxFilesToList int
10 }
11
12 // NewOptionsGCS returns a new initialized OptionsS3
13 func NewOptionsGCS() *OptionsGCS {
14 return &OptionsGCS{}
15 }
0 package gobustergcs
1
2 import (
3 "bytes"
4
5 "github.com/fatih/color"
6 )
7
8 var (
9 green = color.New(color.FgGreen).FprintfFunc()
10 )
11
12 // Result represents a single result
13 type Result struct {
14 Found bool
15 BucketName string
16 Status string
17 }
18
19 // ResultToString converts the Result to it's textual representation
20 func (r Result) ResultToString() (string, error) {
21 buf := &bytes.Buffer{}
22
23 c := green
24
25 c(buf, "https://storage.googleapis.com/storage/v1/b/%s/o", r.BucketName)
26
27 if r.Status != "" {
28 c(buf, " [%s]", r.Status)
29 }
30 c(buf, "\n")
31
32 str := buf.String()
33 return str, nil
34 }
0 package gobustergcs
1
2 // GCSError represents a returned error from GCS
3 type GCSError struct {
4 Error struct {
5 Code int `json:"code"`
6 Message string `json:"message"`
7 Errors []struct {
8 Message string `json:"message"`
9 Reason string `json:"reason"`
10 LocationType string `json:"locationType"`
11 Location string `json:"location"`
12 } `json:"errors"`
13 } `json:"error"`
14 }
15
16 // GCSListing contains only a subset of returned properties
17 type GCSListing struct {
18 IsTruncated string `json:"nextPageToken"`
19 Items []struct {
20 Name string `json:"name"`
21 LastModified string `json:"updated"`
22 Size string `json:"size"`
23 } `json:"items"`
24 }
55 "context"
66 "encoding/xml"
77 "fmt"
8 "net"
89 "net/http"
910 "regexp"
1011 "strings"
2223 }
2324
2425 // NewGobusterS3 creates a new initialized GobusterS3
25 func NewGobusterS3(cont context.Context, globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) {
26 func NewGobusterS3(globalopts *libgobuster.Options, opts *OptionsS3) (*GobusterS3, error) {
2627 if globalopts == nil {
2728 return nil, fmt.Errorf("please provide valid global options")
2829 }
3738 }
3839
3940 basicOptions := libgobuster.BasicHTTPOptions{
40 Proxy: opts.Proxy,
41 Timeout: opts.Timeout,
42 UserAgent: opts.UserAgent,
41 Proxy: opts.Proxy,
42 Timeout: opts.Timeout,
43 UserAgent: opts.UserAgent,
44 NoTLSValidation: opts.NoTLSValidation,
45 RetryOnTimeout: opts.RetryOnTimeout,
46 RetryAttempts: opts.RetryAttempts,
4347 }
4448
4549 httpOpts := libgobuster.HTTPOptions{
4852 FollowRedirect: true,
4953 }
5054
51 h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
55 h, err := libgobuster.NewHTTPClient(&httpOpts)
5256 if err != nil {
5357 return nil, err
5458 }
6367 return "S3 bucket enumeration"
6468 }
6569
66 // RequestsPerRun returns the number of requests this plugin makes per single wordlist item
67 func (s *GobusterS3) RequestsPerRun() int {
68 return 1
69 }
70
7170 // PreRun is the pre run implementation of GobusterS3
72 func (s *GobusterS3) PreRun() error {
71 func (s *GobusterS3) PreRun(ctx context.Context) error {
7372 return nil
7473 }
7574
76 // Run is the process implementation of GobusterS3
77 func (s *GobusterS3) Run(word string, resChannel chan<- libgobuster.Result) error {
75 // ProcessWord is the process implementation of GobusterS3
76 func (s *GobusterS3) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
7877 // only check for valid bucket names
7978 if !s.isValidBucketName(word) {
8079 return nil
8180 }
8281
83 bucketURL := fmt.Sprintf("http://%s.s3.amazonaws.com/?max-keys=%d", word, s.options.MaxFilesToList)
84 status, _, _, body, err := s.http.Request(bucketURL, libgobuster.RequestOptions{ReturnBody: true})
85 if err != nil {
86 return err
87 }
88
89 if status == nil || body == nil {
82 bucketURL := fmt.Sprintf("https://%s.s3.amazonaws.com/?max-keys=%d", word, s.options.MaxFilesToList)
83
84 tries := 1
85 if s.options.RetryOnTimeout && s.options.RetryAttempts > 0 {
86 // add it so it will be the overall max requests
87 tries += s.options.RetryAttempts
88 }
89
90 var statusCode int
91 var body []byte
92 for i := 1; i <= tries; i++ {
93 var err error
94 statusCode, _, _, body, err = s.http.Request(ctx, bucketURL, libgobuster.RequestOptions{ReturnBody: true})
95 if err != nil {
96 // check if it's a timeout and if we should try again and try again
97 // otherwise the timeout error is raised
98 if netErr, ok := err.(net.Error); ok && netErr.Timeout() && i != tries {
99 continue
100 } else if strings.Contains(err.Error(), "invalid control character in URL") {
101 // put error in error chan so it's printed out and ignore it
102 // so gobuster will not quit
103 progress.ErrorChan <- err
104 continue
105 } else {
106 return err
107 }
108 }
109 break
110 }
111
112 if statusCode == 0 || body == nil {
90113 return nil
91114 }
92115
93116 // looks like 404 and 400 are the only negative status codes
94117 found := false
95 switch *status {
118 switch statusCode {
96119 case http.StatusBadRequest:
97120 case http.StatusNotFound:
98121 found = false
137160 }
138161 }
139162
140 resChannel <- Result{
163 progress.ResultChan <- Result{
141164 Found: found,
142165 BucketName: word,
143166 Status: extraStr,
144167 }
145168
146169 return nil
170 }
171
172 func (d *GobusterS3) AdditionalWords(word string) []string {
173 return []string{}
147174 }
148175
149176 // GetConfigString returns the string representation of the current config
11
22 import (
33 "bytes"
4 "fmt"
4
5 "github.com/fatih/color"
6 )
7
8 var (
9 green = color.New(color.FgGreen).FprintfFunc()
510 )
611
712 // Result represents a single result
1116 Status string
1217 }
1318
14 // ToString converts the Result to it's textual representation
19 // ResultToString converts the Result to it's textual representation
1520 func (r Result) ResultToString() (string, error) {
1621 buf := &bytes.Buffer{}
1722
18 if _, err := fmt.Fprintf(buf, "http://%s.s3.amazonaws.com/", r.BucketName); err != nil {
19 return "", err
20 }
23 c := green
24
25 c(buf, "http://%s.s3.amazonaws.com/", r.BucketName)
2126
2227 if r.Status != "" {
23 if _, err := fmt.Fprintf(buf, " [%s]", r.Status); err != nil {
24 return "", err
25 }
28 c(buf, " [%s]", r.Status)
2629 }
27
28 if _, err := fmt.Fprintf(buf, "\n"); err != nil {
29 return "", err
30 }
30 c(buf, "\n")
3131
3232 str := buf.String()
3333 return str, nil
44 "bytes"
55 "context"
66 "fmt"
7 "net"
8 "net/http"
79 "net/url"
810 "strings"
911 "text/tabwriter"
1012
13 "github.com/OJ/gobuster/v3/helper"
1114 "github.com/OJ/gobuster/v3/libgobuster"
1215 "github.com/google/uuid"
1316 )
1417
1518 // GobusterVhost is the main type to implement the interface
1619 type GobusterVhost struct {
17 options *OptionsVhost
18 globalopts *libgobuster.Options
19 http *libgobuster.HTTPClient
20 domain string
21 baseline1 []byte
22 baseline2 []byte
20 options *OptionsVhost
21 globalopts *libgobuster.Options
22 http *libgobuster.HTTPClient
23 domain string
24 normalBody []byte
25 abnormalBody []byte
2326 }
2427
2528 // NewGobusterVhost creates a new initialized GobusterDir
26 func NewGobusterVhost(cont context.Context, globalopts *libgobuster.Options, opts *OptionsVhost) (*GobusterVhost, error) {
29 func NewGobusterVhost(globalopts *libgobuster.Options, opts *OptionsVhost) (*GobusterVhost, error) {
2730 if globalopts == nil {
2831 return nil, fmt.Errorf("please provide valid global options")
2932 }
3841 }
3942
4043 basicOptions := libgobuster.BasicHTTPOptions{
41 Proxy: opts.Proxy,
42 Timeout: opts.Timeout,
43 UserAgent: opts.UserAgent,
44 Proxy: opts.Proxy,
45 Timeout: opts.Timeout,
46 UserAgent: opts.UserAgent,
47 NoTLSValidation: opts.NoTLSValidation,
48 RetryOnTimeout: opts.RetryOnTimeout,
49 RetryAttempts: opts.RetryAttempts,
4450 }
4551
4652 httpOpts := libgobuster.HTTPOptions{
4753 BasicHTTPOptions: basicOptions,
4854 FollowRedirect: opts.FollowRedirect,
49 NoTLSValidation: opts.NoTLSValidation,
5055 Username: opts.Username,
5156 Password: opts.Password,
5257 Headers: opts.Headers,
5459 Method: opts.Method,
5560 }
5661
57 h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
62 h, err := libgobuster.NewHTTPClient(&httpOpts)
5863 if err != nil {
5964 return nil, err
6065 }
6772 return "VHOST enumeration"
6873 }
6974
70 // RequestsPerRun returns the number of requests this plugin makes per single wordlist item
71 func (v *GobusterVhost) RequestsPerRun() int {
72 return 1
73 }
74
7575 // PreRun is the pre run implementation of gobusterdir
76 func (v *GobusterVhost) PreRun() error {
76 func (v *GobusterVhost) PreRun(ctx context.Context) error {
7777 // add trailing slash
7878 if !strings.HasSuffix(v.options.URL, "/") {
7979 v.options.URL = fmt.Sprintf("%s/", v.options.URL)
8383 if err != nil {
8484 return fmt.Errorf("invalid url %s: %w", v.options.URL, err)
8585 }
86 v.domain = urlParsed.Host
87
88 // request default vhost for baseline1
89 _, _, _, tmp, err := v.http.Request(v.options.URL, libgobuster.RequestOptions{ReturnBody: true})
86 if v.options.Domain != "" {
87 v.domain = v.options.Domain
88 } else {
89 v.domain = urlParsed.Host
90 }
91
92 // request default vhost for normalBody
93 _, _, _, body, err := v.http.Request(ctx, v.options.URL, libgobuster.RequestOptions{ReturnBody: true})
9094 if err != nil {
9195 return fmt.Errorf("unable to connect to %s: %w", v.options.URL, err)
9296 }
93 v.baseline1 = tmp
94
95 // request non existent vhost for baseline2
97 v.normalBody = body
98
99 // request non existent vhost for abnormalBody
96100 subdomain := fmt.Sprintf("%s.%s", uuid.New(), v.domain)
97 _, _, _, tmp, err = v.http.Request(v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
101 _, _, _, body, err = v.http.Request(ctx, v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
98102 if err != nil {
99103 return fmt.Errorf("unable to connect to %s: %w", v.options.URL, err)
100104 }
101 v.baseline2 = tmp
105 v.abnormalBody = body
102106 return nil
103107 }
104108
105 // Run is the process implementation of gobusterdir
106 func (v *GobusterVhost) Run(word string, resChannel chan<- libgobuster.Result) error {
107 subdomain := fmt.Sprintf("%s.%s", word, v.domain)
108 status, size, header, body, err := v.http.Request(v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
109 if err != nil {
110 return err
109 // ProcessWord is the process implementation of gobusterdir
110 func (v *GobusterVhost) ProcessWord(ctx context.Context, word string, progress *libgobuster.Progress) error {
111 var subdomain string
112 if v.options.AppendDomain {
113 subdomain = fmt.Sprintf("%s.%s", word, v.domain)
114 } else {
115 // wordlist needs to include full domains
116 subdomain = word
117 }
118
119 tries := 1
120 if v.options.RetryOnTimeout && v.options.RetryAttempts > 0 {
121 // add it so it will be the overall max requests
122 tries += v.options.RetryAttempts
123 }
124
125 var statusCode int
126 var size int64
127 var header http.Header
128 var body []byte
129 for i := 1; i <= tries; i++ {
130 var err error
131 statusCode, size, header, body, err = v.http.Request(ctx, v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
132 if err != nil {
133 // check if it's a timeout and if we should try again and try again
134 // otherwise the timeout error is raised
135 if netErr, ok := err.(net.Error); ok && netErr.Timeout() && i != tries {
136 continue
137 } else if strings.Contains(err.Error(), "invalid control character in URL") {
138 // put error in error chan so it's printed out and ignore it
139 // so gobuster will not quit
140 progress.ErrorChan <- err
141 continue
142 } else {
143 return err
144 }
145 }
146 break
111147 }
112148
113149 // subdomain must not match default vhost and non existent vhost
114150 // or verbose mode is enabled
115 found := !bytes.Equal(body, v.baseline1) && !bytes.Equal(body, v.baseline2)
116 if found || v.globalopts.Verbose {
151 found := body != nil && !bytes.Equal(body, v.normalBody) && !bytes.Equal(body, v.abnormalBody)
152 if (found && !helper.SliceContains(v.options.ExcludeLength, int(size))) || v.globalopts.Verbose {
117153 resultStatus := false
118154 if found {
119155 resultStatus = true
120156 }
121 resChannel <- Result{
157 progress.ResultChan <- Result{
122158 Found: resultStatus,
123159 Vhost: subdomain,
124 StatusCode: *status,
160 StatusCode: statusCode,
125161 Size: size,
126162 Header: header,
127163 }
128164 }
129165 return nil
166 }
167
168 func (v *GobusterVhost) AdditionalWords(word string) []string {
169 return []string{}
130170 }
131171
132172 // GetConfigString returns the string representation of the current config
201241 return "", err
202242 }
203243
244 if _, err := fmt.Fprintf(tw, "[+] Append Domain:\t%t\n", v.options.AppendDomain); err != nil {
245 return "", err
246 }
247
248 if len(o.ExcludeLength) > 0 {
249 if _, err := fmt.Fprintf(tw, "[+] Exclude Length:\t%s\n", helper.JoinIntSlice(v.options.ExcludeLength)); err != nil {
250 return "", err
251 }
252 }
253
204254 if err := tw.Flush(); err != nil {
205255 return "", fmt.Errorf("error on tostring: %w", err)
206256 }
66 // OptionsVhost is the struct to hold all options for this plugin
77 type OptionsVhost struct {
88 libgobuster.HTTPOptions
9 AppendDomain bool
10 ExcludeLength []int
11 Domain string
912 }
00 package gobustervhost
11
22 import (
3 "bytes"
43 "fmt"
54 "net/http"
5
6 "github.com/fatih/color"
7 )
8
9 var (
10 white = color.New(color.FgWhite).SprintFunc()
11 yellow = color.New(color.FgYellow).SprintFunc()
12 green = color.New(color.FgGreen).SprintFunc()
13 blue = color.New(color.FgBlue).SprintFunc()
14 red = color.New(color.FgRed).SprintFunc()
15 cyan = color.New(color.FgCyan).SprintFunc()
616 )
717
818 // Result represents a single result
1424 Header http.Header
1525 }
1626
17 // ToString converts the Result to it's textual representation
27 // ResultToString converts the Result to it's textual representation
1828 func (r Result) ResultToString() (string, error) {
19 buf := &bytes.Buffer{}
20
21 statusText := "Missed"
29 statusText := yellow("Missed")
2230 if r.Found {
23 statusText = "Found"
31 statusText = green("Found")
2432 }
2533
26 if _, err := fmt.Fprintf(buf, "%s: %s (Status: %d) [Size: %d]\n", statusText, r.Vhost, r.StatusCode, r.Size); err != nil {
27 return "", err
34 statusCodeColor := white
35 if r.StatusCode == 200 {
36 statusCodeColor = green
37 } else if r.StatusCode >= 300 && r.StatusCode < 400 {
38 statusCodeColor = cyan
39 } else if r.StatusCode >= 400 && r.StatusCode < 500 {
40 statusCodeColor = yellow
41 } else if r.StatusCode >= 500 && r.StatusCode < 600 {
42 statusCodeColor = red
2843 }
2944
30 s := buf.String()
31 return s, nil
45 statusCode := statusCodeColor(fmt.Sprintf("Status: %d", r.StatusCode))
46
47 location := r.Header.Get("Location")
48 locationString := ""
49 if location != "" {
50 locationString = blue(fmt.Sprintf(" [--> %s]", location))
51 }
52
53 return fmt.Sprintf("%s: %s %s [Size: %d]%s\n", statusText, r.Vhost, statusCode, r.Size, locationString), nil
3254 }
88 )
99
1010 // ParseExtensions parses the extensions provided as a comma separated list
11 func ParseExtensions(extensions string) (libgobuster.StringSet, error) {
11 func ParseExtensions(extensions string) (libgobuster.Set[string], error) {
12 ret := libgobuster.NewSet[string]()
13
1214 if extensions == "" {
13 return libgobuster.StringSet{}, fmt.Errorf("invalid extension string provided")
15 return ret, nil
1416 }
1517
16 ret := libgobuster.NewStringSet()
1718 for _, e := range strings.Split(extensions, ",") {
1819 e = strings.TrimSpace(e)
1920 // remove leading . from extensions
2324 }
2425
2526 // ParseCommaSeparatedInt parses the status codes provided as a comma separated list
26 func ParseCommaSeparatedInt(inputString string) (libgobuster.IntSet, error) {
27 func ParseCommaSeparatedInt(inputString string) (libgobuster.Set[int], error) {
28 ret := libgobuster.NewSet[int]()
29
2730 if inputString == "" {
28 return libgobuster.IntSet{}, fmt.Errorf("invalid string provided")
31 return ret, nil
2932 }
3033
31 ret := libgobuster.NewIntSet()
3234 for _, c := range strings.Split(inputString, ",") {
3335 c = strings.TrimSpace(c)
3436 i, err := strconv.Atoi(c)
3537 if err != nil {
36 return libgobuster.IntSet{}, fmt.Errorf("invalid string given: %s", c)
38 return libgobuster.NewSet[int](), fmt.Errorf("invalid string given: %s", c)
3739 }
3840 ret.Add(i)
3941 }
88
99 func TestParseExtensions(t *testing.T) {
1010 t.Parallel()
11
1211 var tt = []struct {
1312 testName string
1413 extensions string
15 expectedExtensions libgobuster.StringSet
14 expectedExtensions libgobuster.Set[string]
1615 expectedError string
1716 }{
18 {"Valid extensions", "php,asp,txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
19 {"Spaces", "php, asp , txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
20 {"Double extensions", "php,asp,txt,php,asp,txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
21 {"Leading dot", ".php,asp,.txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
22 {"Empty string", "", libgobuster.NewStringSet(), "invalid extension string provided"},
17 {"Valid extensions", "php,asp,txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
18 {"Spaces", "php, asp , txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
19 {"Double extensions", "php,asp,txt,php,asp,txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
20 {"Leading dot", ".php,asp,.txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
21 {"Empty string", "", libgobuster.NewSet[string](), "invalid extension string provided"},
2322 }
2423
2524 for _, x := range tt {
25 x := x // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
2626 t.Run(x.testName, func(t *testing.T) {
27 t.Parallel()
2728 ret, err := ParseExtensions(x.extensions)
2829 if x.expectedError != "" {
2930 if err != nil && err.Error() != x.expectedError {
3839
3940 func TestParseCommaSeparatedInt(t *testing.T) {
4041 t.Parallel()
41
4242 var tt = []struct {
4343 testName string
4444 stringCodes string
45 expectedCodes libgobuster.IntSet
45 expectedCodes libgobuster.Set[int]
4646 expectedError string
4747 }{
48 {"Valid codes", "200,100,202", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
49 {"Spaces", "200, 100 , 202", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
50 {"Double codes", "200, 100, 202, 100", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
51 {"Invalid code", "200,AAA", libgobuster.NewIntSet(), "invalid string given: AAA"},
52 {"Invalid integer", "2000000000000000000000000000000", libgobuster.NewIntSet(), "invalid string given: 2000000000000000000000000000000"},
53 {"Empty string", "", libgobuster.NewIntSet(), "invalid string provided"},
48 {"Valid codes", "200,100,202", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
49 {"Spaces", "200, 100 , 202", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
50 {"Double codes", "200, 100, 202, 100", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
51 {"Invalid code", "200,AAA", libgobuster.NewSet[int](), "invalid string given: AAA"},
52 {"Invalid integer", "2000000000000000000000000000000", libgobuster.NewSet[int](), "invalid string given: 2000000000000000000000000000000"},
53 {"Empty string", "", libgobuster.NewSet[int](), "invalid string provided"},
5454 }
5555
5656 for _, x := range tt {
57 x := x // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
5758 t.Run(x.testName, func(t *testing.T) {
59 t.Parallel()
5860 ret, err := ParseCommaSeparatedInt(x.stringCodes)
5961 if x.expectedError != "" {
6062 if err != nil && err.Error() != x.expectedError {
7173 var tt = []struct {
7274 testName string
7375 extensions string
74 expectedExtensions libgobuster.StringSet
76 expectedExtensions libgobuster.Set[string]
7577 expectedError string
7678 }{
77 {"Valid extensions", "php,asp,txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
78 {"Spaces", "php, asp , txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
79 {"Double extensions", "php,asp,txt,php,asp,txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
80 {"Leading dot", ".php,asp,.txt", libgobuster.StringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
81 {"Empty string", "", libgobuster.NewStringSet(), "invalid extension string provided"},
79 {"Valid extensions", "php,asp,txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
80 {"Spaces", "php, asp , txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
81 {"Double extensions", "php,asp,txt,php,asp,txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
82 {"Leading dot", ".php,asp,.txt", libgobuster.Set[string]{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
83 {"Empty string", "", libgobuster.NewSet[string](), "invalid extension string provided"},
8284 }
8385
8486 for _, x := range tt {
87 x := x // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
8588 b.Run(x.testName, func(b2 *testing.B) {
8689 for y := 0; y < b2.N; y++ {
8790 _, _ = ParseExtensions(x.extensions)
9497 var tt = []struct {
9598 testName string
9699 stringCodes string
97 expectedCodes libgobuster.IntSet
100 expectedCodes libgobuster.Set[int]
98101 expectedError string
99102 }{
100 {"Valid codes", "200,100,202", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
101 {"Spaces", "200, 100 , 202", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
102 {"Double codes", "200, 100, 202, 100", libgobuster.IntSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
103 {"Invalid code", "200,AAA", libgobuster.NewIntSet(), "invalid string given: AAA"},
104 {"Invalid integer", "2000000000000000000000000000000", libgobuster.NewIntSet(), "invalid string given: 2000000000000000000000000000000"},
105 {"Empty string", "", libgobuster.NewIntSet(), "invalid string string provided"},
103 {"Valid codes", "200,100,202", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
104 {"Spaces", "200, 100 , 202", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
105 {"Double codes", "200, 100, 202, 100", libgobuster.Set[int]{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
106 {"Invalid code", "200,AAA", libgobuster.NewSet[int](), "invalid string given: AAA"},
107 {"Invalid integer", "2000000000000000000000000000000", libgobuster.NewSet[int](), "invalid string given: 2000000000000000000000000000000"},
108 {"Empty string", "", libgobuster.NewSet[int](), "invalid string string provided"},
106109 }
107110
108111 for _, x := range tt {
112 x := x // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
109113 b.Run(x.testName, func(b2 *testing.B) {
110114 for y := 0; y < b2.N; y++ {
111115 _, _ = ParseCommaSeparatedInt(x.stringCodes)
00 package helper
11
22 import (
3 "math/rand"
4 "time"
3 "crypto/rand"
4 "math/big"
55 )
66
7 // molint:gochecknoglobals
78 var userAgents = [...]string{
89 "Mozilla/5.0 (X11; Linux i686; rv:64.0) Gecko/20100101 Firefox/64.0",
910 "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0",
46654666 }
46664667
46674668 // GetRandomUserAgent picks a random user agent from a predefined list
4668 func GetRandomUserAgent() string {
4669 rand.Seed(time.Now().Unix())
4670 return userAgents[rand.Intn(len(userAgents))]
4669 func GetRandomUserAgent() (string, error) {
4670 n, err := rand.Int(rand.Reader, big.NewInt(int64(len(userAgents))))
4671 if err != nil {
4672 return "", err
4673 }
4674 return userAgents[n.Int64()], err
46714675 }
11
22 import (
33 "bytes"
4 "errors"
45 "fmt"
56 "io"
6 "sort"
77 "strings"
88 )
99
10 // IntSet is a set of Ints
11 type IntSet struct {
12 Set map[int]bool
10 // Set is a set of Ts
11 type Set[T comparable] struct {
12 Set map[T]bool
1313 }
1414
15 // StringSet is a set of Strings
16 type StringSet struct {
17 Set map[string]bool
18 }
19
20 // NewStringSet creates a new initialized StringSet
21 func NewStringSet() StringSet {
22 return StringSet{Set: map[string]bool{}}
15 // NewSSet creates a new initialized Set
16 func NewSet[T comparable]() Set[T] {
17 return Set[T]{Set: map[T]bool{}}
2318 }
2419
2520 // Add an element to a set
26 func (set *StringSet) Add(s string) bool {
21 func (set *Set[T]) Add(s T) bool {
2722 _, found := set.Set[s]
2823 set.Set[s] = true
2924 return !found
3025 }
3126
3227 // AddRange adds a list of elements to a set
33 func (set *StringSet) AddRange(ss []string) {
28 func (set *Set[T]) AddRange(ss []T) {
3429 for _, s := range ss {
3530 set.Set[s] = true
3631 }
3732 }
3833
3934 // Contains tests if an element is in a set
40 func (set *StringSet) Contains(s string) bool {
35 func (set *Set[T]) Contains(s T) bool {
4136 _, found := set.Set[s]
4237 return found
4338 }
4439
4540 // ContainsAny checks if any of the elements exist
46 func (set *StringSet) ContainsAny(ss []string) bool {
41 func (set *Set[T]) ContainsAny(ss []T) bool {
4742 for _, s := range ss {
4843 if set.Set[s] {
4944 return true
5348 }
5449
5550 // Length returns the length of the Set
56 func (set *StringSet) Length() int {
51 func (set *Set[T]) Length() int {
5752 return len(set.Set)
5853 }
5954
6055 // Stringify the set
61 func (set *StringSet) Stringify() string {
56 func (set *Set[T]) Stringify() string {
6257 values := make([]string, len(set.Set))
6358 i := 0
6459 for s := range set.Set {
65 values[i] = s
60 values[i] = fmt.Sprint(s)
6661 i++
6762 }
6863 return strings.Join(values, ",")
69 }
70
71 // NewIntSet creates a new initialized IntSet
72 func NewIntSet() IntSet {
73 return IntSet{Set: map[int]bool{}}
74 }
75
76 // Add adds an element to a set
77 func (set *IntSet) Add(i int) bool {
78 _, found := set.Set[i]
79 set.Set[i] = true
80 return !found
81 }
82
83 // Contains tests if an element is in a set
84 func (set *IntSet) Contains(i int) bool {
85 _, found := set.Set[i]
86 return found
87 }
88
89 // Stringify the set
90 func (set *IntSet) Stringify() string {
91 values := make([]int, len(set.Set))
92 i := 0
93 for s := range set.Set {
94 values[i] = s
95 i++
96 }
97 sort.Ints(values)
98
99 delim := ","
100 return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(values)), delim), "[]")
101 }
102
103 // Length returns the length of the Set
104 func (set *IntSet) Length() int {
105 return len(set.Set)
10664 }
10765
10866 func lineCounter(r io.Reader) (int, error) {
11573 count += bytes.Count(buf[:c], lineSep)
11674
11775 switch {
118 case err == io.EOF:
76 case errors.Is(err, io.EOF):
11977 return count, nil
12078
12179 case err != nil:
11
22 import (
33 "errors"
4 "fmt"
45 "strings"
56 "testing"
67 "testing/iotest"
78 )
89
9 func TestNewStringSet(t *testing.T) {
10 if NewStringSet().Set == nil {
11 t.Fatal("newStringSet returned nil Set")
10 func TestNewSet(t *testing.T) {
11 t.Parallel()
12 if NewSet[string]().Set == nil {
13 t.Fatal("NewSet[string] returned nil Set")
14 }
15
16 if NewSet[int]().Set == nil {
17 t.Fatal("NewSet[int] returned nil Set")
1218 }
1319 }
1420
15 func TestNewIntSet(t *testing.T) {
16 if NewIntSet().Set == nil {
17 t.Fatal("newIntSet returned nil Set")
21 func TestSetAdd(t *testing.T) {
22 t.Parallel()
23 x := NewSet[string]()
24 x.Add("test")
25 if len(x.Set) != 1 {
26 t.Fatalf("Unexpected string size. Should have 1 Got %v", len(x.Set))
27 }
28
29 y := NewSet[int]()
30 y.Add(1)
31 if len(y.Set) != 1 {
32 t.Fatalf("Unexpected int size. Should have 1 Got %v", len(y.Set))
1833 }
1934 }
2035
21 func TestStringSetAdd(t *testing.T) {
22 x := NewStringSet()
36 func TestSetAddDouble(t *testing.T) {
37 t.Parallel()
38 x := NewSet[string]()
39 x.Add("test")
2340 x.Add("test")
2441 if len(x.Set) != 1 {
25 t.Fatalf("Unexpected size. Should have 1 Got %v", len(x.Set))
42 t.Fatalf("Unexpected string size. Should be 1 (unique) Got %v", len(x.Set))
43 }
44
45 y := NewSet[int]()
46 y.Add(1)
47 y.Add(1)
48 if len(y.Set) != 1 {
49 t.Fatalf("Unexpected int size. Should be 1 (unique) Got %v", len(y.Set))
2650 }
2751 }
2852
29 func TestStringSetAddDouble(t *testing.T) {
30 x := NewStringSet()
31 x.Add("test")
32 x.Add("test")
33 if len(x.Set) != 1 {
34 t.Fatalf("Unexpected size. Should have 1 Got %d", len(x.Set))
53 func TestSetAddRange(t *testing.T) {
54 t.Parallel()
55 x := NewSet[string]()
56 x.AddRange([]string{"string1", "string2"})
57 if len(x.Set) != 2 {
58 t.Fatalf("Unexpected string size. Should have 2 Got %v", len(x.Set))
59 }
60
61 y := NewSet[int]()
62 y.AddRange([]int{1, 2})
63 if len(y.Set) != 2 {
64 t.Fatalf("Unexpected int size. Should have 2 Got %v", len(y.Set))
3565 }
3666 }
3767
38 func TestStringSetAddRange(t *testing.T) {
39 x := NewStringSet()
40 x.AddRange([]string{"string1", "string2"})
68 func TestSetAddRangeDouble(t *testing.T) {
69 t.Parallel()
70 x := NewSet[string]()
71 x.AddRange([]string{"string1", "string2", "string1", "string2"})
4172 if len(x.Set) != 2 {
42 t.Fatalf("Unexpected size. Should have 2 Got %d", len(x.Set))
73 t.Fatalf("Unexpected string size. Should be 2 (unique) Got %v", len(x.Set))
74 }
75
76 y := NewSet[int]()
77 y.AddRange([]int{1, 2, 1, 2})
78 if len(y.Set) != 2 {
79 t.Fatalf("Unexpected int size. Should be 2 (unique) Got %v", len(y.Set))
4380 }
4481 }
4582
46 func TestStringSetAddRangeDouble(t *testing.T) {
47 x := NewStringSet()
48 x.AddRange([]string{"string1", "string2", "string1", "string2"})
49 if len(x.Set) != 2 {
50 t.Fatalf("Unexpected size. Should have 2 Got %d", len(x.Set))
51 }
52 }
53
54 func TestStringSetContains(t *testing.T) {
55 x := NewStringSet()
83 func TestSetContains(t *testing.T) {
84 t.Parallel()
85 x := NewSet[string]()
5686 v := []string{"string1", "string2", "1234", "5678"}
5787 x.AddRange(v)
58 for _, y := range v {
59 if !x.Contains(y) {
60 t.Fatalf("Did not find value %s in array. %v", y, x.Set)
88 for _, i := range v {
89 if !x.Contains(i) {
90 t.Fatalf("Did not find value %s in array. %v", i, x.Set)
91 }
92 }
93
94 y := NewSet[int]()
95 v2 := []int{1, 2312, 123121, 999, -99}
96 y.AddRange(v2)
97 for _, i := range v2 {
98 if !y.Contains(i) {
99 t.Fatalf("Did not find value %d in array. %v", i, y.Set)
61100 }
62101 }
63102 }
64103
65 func TestStringSetContainsAny(t *testing.T) {
66 x := NewStringSet()
104 func TestSetContainsAny(t *testing.T) {
105 t.Parallel()
106 x := NewSet[string]()
67107 v := []string{"string1", "string2", "1234", "5678"}
68108 x.AddRange(v)
69109 if !x.ContainsAny(v) {
74114 if x.ContainsAny([]string{"mmmm", "nnnnn"}) {
75115 t.Fatal("Found unexpected values")
76116 }
117
118 y := NewSet[int]()
119 v2 := []int{1, 2312, 123121, 999, -99}
120 y.AddRange(v2)
121 if !y.ContainsAny(v2) {
122 t.Fatalf("Did not find any")
123 }
124
125 // test not found
126 if y.ContainsAny([]int{9235, 2398532}) {
127 t.Fatal("Found unexpected values")
128 }
77129 }
78130
79 func TestStringSetStringify(t *testing.T) {
80 x := NewStringSet()
131 func TestSetStringify(t *testing.T) {
132 t.Parallel()
133 x := NewSet[string]()
81134 v := []string{"string1", "string2", "1234", "5678"}
82135 x.AddRange(v)
83136 z := x.Stringify()
84137 // order is random
85 for _, y := range v {
86 if !strings.Contains(z, y) {
87 t.Fatalf("Did not find value %q in %q", y, z)
138 for _, i := range v {
139 if !strings.Contains(z, i) {
140 t.Fatalf("Did not find value %q in %q", i, z)
141 }
142 }
143
144 y := NewSet[int]()
145 v2 := []int{1, 2312, 123121, 999, -99}
146 y.AddRange(v2)
147 z = y.Stringify()
148 // order is random
149 for _, i := range v2 {
150 if !strings.Contains(z, fmt.Sprint(i)) {
151 t.Fatalf("Did not find value %q in %q", i, z)
88152 }
89153 }
90154 }
91155
92 func TestIntSetAdd(t *testing.T) {
93 x := NewIntSet()
94 x.Add(1)
95 if len(x.Set) != 1 {
96 t.Fatalf("Unexpected size. Should have 1 Got %d", len(x.Set))
97 }
98 }
99
100 func TestIntSetAddDouble(t *testing.T) {
101 x := NewIntSet()
102 x.Add(1)
103 x.Add(1)
104 if len(x.Set) != 1 {
105 t.Fatalf("Unexpected size. Should have 1 Got %d", len(x.Set))
106 }
107 }
108
109 func TestIntSetContains(t *testing.T) {
110 x := NewIntSet()
111 v := []int{1, 2, 3, 4}
112 for _, y := range v {
113 x.Add(y)
114 }
115 for _, y := range v {
116 if !x.Contains(y) {
117 t.Fatalf("Did not find value %d in array. %v", y, x.Set)
118 }
119 }
120 }
121
122 func TestIntSetStringify(t *testing.T) {
123 x := NewIntSet()
124 v := []int{1, 3, 2, 4}
125 expected := "1,2,3,4"
126 for _, y := range v {
127 x.Add(y)
128 }
129 z := x.Stringify()
130 // should be sorted
131 if expected != z {
132 t.Fatalf("Expected %q got %q", expected, z)
133 }
134 }
135
136156 func TestLineCounter(t *testing.T) {
157 t.Parallel()
137158 var tt = []struct {
138159 testName string
139160 s string
146167 {"Empty", "", 1},
147168 }
148169 for _, x := range tt {
170 x := x // NOTE: https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
149171 t.Run(x.testName, func(t *testing.T) {
172 t.Parallel()
150173 r := strings.NewReader(x.s)
151174 l, err := lineCounter(r)
152175 if err != nil {
160183 }
161184
162185 func TestLineCounterError(t *testing.T) {
186 t.Parallel()
163187 r := iotest.TimeoutReader(strings.NewReader("test"))
164188 _, err := lineCounter(r)
165189 if !errors.Is(err, iotest.ErrTimeout) {
55 "errors"
66 "fmt"
77 "io"
8 "io/ioutil"
98 "net/http"
109 "net/url"
1110 "strings"
2019 // HTTPClient represents a http object
2120 type HTTPClient struct {
2221 client *http.Client
23 context context.Context
2422 userAgent string
2523 defaultUserAgent string
2624 username string
2826 headers []HTTPHeader
2927 cookies string
3028 method string
29 host string
3130 }
3231
3332 // RequestOptions is used to pass options to a single individual request
3837 }
3938
4039 // NewHTTPClient returns a new HTTPClient
41 func NewHTTPClient(c context.Context, opt *HTTPOptions) (*HTTPClient, error) {
40 func NewHTTPClient(opt *HTTPOptions) (*HTTPClient, error) {
4241 var proxyURLFunc func(*http.Request) (*url.URL, error)
4342 var client HTTPClient
4443 proxyURLFunc = http.ProxyFromEnvironment
7574 InsecureSkipVerify: opt.NoTLSValidation,
7675 },
7776 }}
78 client.context = c
7977 client.username = opt.Username
8078 client.password = opt.Password
8179 client.userAgent = opt.UserAgent
8684 if client.method == "" {
8785 client.method = http.MethodGet
8886 }
87 // Host header needs to be set separately
88 for _, h := range opt.Headers {
89 if h.Name == "Host" {
90 client.host = h.Value
91 break
92 }
93 }
8994 return &client, nil
9095 }
9196
9297 // Request makes an http request and returns the status, the content length, the headers, the body and an error
9398 // if you want the body returned set the corresponding property inside RequestOptions
94 func (client *HTTPClient) Request(fullURL string, opts RequestOptions) (*int, int64, http.Header, []byte, error) {
95 resp, err := client.makeRequest(fullURL, opts.Host, opts.Body)
99 func (client *HTTPClient) Request(ctx context.Context, fullURL string, opts RequestOptions) (int, int64, http.Header, []byte, error) {
100 resp, err := client.makeRequest(ctx, fullURL, opts.Host, opts.Body)
96101 if err != nil {
97102 // ignore context canceled errors
98 if errors.Is(client.context.Err(), context.Canceled) {
99 return nil, 0, nil, nil, nil
103 if errors.Is(ctx.Err(), context.Canceled) {
104 return 0, 0, nil, nil, nil
100105 }
101 return nil, 0, nil, nil, err
106 return 0, 0, nil, nil, err
102107 }
103108 defer resp.Body.Close()
104109
105110 var body []byte
106111 var length int64
107112 if opts.ReturnBody {
108 body, err = ioutil.ReadAll(resp.Body)
113 body, err = io.ReadAll(resp.Body)
109114 if err != nil {
110 return nil, 0, nil, nil, fmt.Errorf("could not read body %w", err)
115 return 0, 0, nil, nil, fmt.Errorf("could not read body %w", err)
111116 }
112117 length = int64(len(body))
113118 } else {
114119 // DO NOT REMOVE!
115120 // absolutely needed so golang will reuse connections!
116 length, err = io.Copy(ioutil.Discard, resp.Body)
121 length, err = io.Copy(io.Discard, resp.Body)
117122 if err != nil {
118 return nil, 0, nil, nil, err
123 return 0, 0, nil, nil, err
119124 }
120125 }
121126
122 return &resp.StatusCode, length, resp.Header, body, nil
127 return resp.StatusCode, length, resp.Header, body, nil
123128 }
124129
125 func (client *HTTPClient) makeRequest(fullURL, host string, data io.Reader) (*http.Response, error) {
130 func (client *HTTPClient) makeRequest(ctx context.Context, fullURL, host string, data io.Reader) (*http.Response, error) {
126131 req, err := http.NewRequest(client.method, fullURL, data)
127132 if err != nil {
128133 return nil, err
129134 }
130135
131136 // add the context so we can easily cancel out
132 req = req.WithContext(client.context)
137 req = req.WithContext(ctx)
133138
134139 if client.cookies != "" {
135140 req.Header.Set("Cookie", client.cookies)
136141 }
137142
143 // Use host for VHOST mode on a per request basis, otherwise the one provided from headers
138144 if host != "" {
139145 req.Host = host
146 } else if client.host != "" {
147 req.Host = client.host
140148 }
141149
142150 if client.userAgent != "" {
156164
157165 resp, err := client.client.Do(req)
158166 if err != nil {
159 if ue, ok := err.(*url.Error); ok {
167 var ue *url.Error
168 if errors.As(err, &ue) {
160169 if strings.HasPrefix(ue.Err.Error(), "x509") {
161170 return nil, fmt.Errorf("invalid certificate: %w", ue.Err)
162171 }
22 import (
33 "bytes"
44 "context"
5 "crypto/rand"
56 "fmt"
6 "math/rand"
7 "math/big"
78 "net/http"
89 "net/http/httptest"
910 "testing"
2526 return ts
2627 }
2728
28 func randomString(length int) string {
29 func randomString(length int) (string, error) {
2930 var letter = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
3031 letterLen := len(letter)
3132
3233 b := make([]byte, length)
3334 for i := range b {
34 b[i] = letter[rand.Intn(letterLen)]
35 n, err := rand.Int(rand.Reader, big.NewInt(int64(letterLen)))
36 if err != nil {
37 return "", err
38 }
39 b[i] = letter[n.Int64()]
3540 }
36 return string(b)
41 return string(b), nil
3742 }
3843
3944 func TestRequest(t *testing.T) {
40 ret := randomString(100)
45 t.Parallel()
46 ret, err := randomString(100)
47 if err != nil {
48 t.Fatal(err)
49 }
4150 h := httpServerT(t, ret)
4251 defer h.Close()
4352 var o HTTPOptions
44 c, err := NewHTTPClient(context.Background(), &o)
53 c, err := NewHTTPClient(&o)
4554 if err != nil {
4655 t.Fatalf("Got Error: %v", err)
4756 }
48 status, length, _, body, err := c.Request(h.URL, RequestOptions{ReturnBody: true})
57 status, length, _, body, err := c.Request(context.Background(), h.URL, RequestOptions{ReturnBody: true})
4958 if err != nil {
5059 t.Fatalf("Got Error: %v", err)
5160 }
52 if *status != 200 {
61 if status != 200 {
5362 t.Fatalf("Invalid status returned: %d", status)
5463 }
5564 if length != int64(len(ret)) {
6170 }
6271
6372 func BenchmarkRequestWithoutBody(b *testing.B) {
64 h := httpServerB(b, randomString(10000))
73 r, err := randomString(10000)
74 if err != nil {
75 b.Fatal(err)
76 }
77 h := httpServerB(b, r)
6578 defer h.Close()
6679 var o HTTPOptions
67 c, err := NewHTTPClient(context.Background(), &o)
80 c, err := NewHTTPClient(&o)
6881 if err != nil {
6982 b.Fatalf("Got Error: %v", err)
7083 }
7184 for x := 0; x < b.N; x++ {
72 _, _, _, _, err := c.Request(h.URL, RequestOptions{ReturnBody: false})
85 _, _, _, _, err := c.Request(context.Background(), h.URL, RequestOptions{ReturnBody: false})
7386 if err != nil {
7487 b.Fatalf("Got Error: %v", err)
7588 }
7790 }
7891
7992 func BenchmarkRequestWitBody(b *testing.B) {
80 h := httpServerB(b, randomString(10000))
93 r, err := randomString(10000)
94 if err != nil {
95 b.Fatal(err)
96 }
97 h := httpServerB(b, r)
8198 defer h.Close()
8299 var o HTTPOptions
83 c, err := NewHTTPClient(context.Background(), &o)
100 c, err := NewHTTPClient(&o)
84101 if err != nil {
85102 b.Fatalf("Got Error: %v", err)
86103 }
87104 for x := 0; x < b.N; x++ {
88 _, _, _, _, err := c.Request(h.URL, RequestOptions{ReturnBody: true})
105 _, _, _, _, err := c.Request(context.Background(), h.URL, RequestOptions{ReturnBody: true})
89106 if err != nil {
90107 b.Fatalf("Got Error: %v", err)
91108 }
93110 }
94111
95112 func BenchmarkNewHTTPClient(b *testing.B) {
96 h := httpServerB(b, randomString(500))
113 r, err := randomString(500)
114 if err != nil {
115 b.Fatal(err)
116 }
117 h := httpServerB(b, r)
97118 defer h.Close()
98119 var o HTTPOptions
99120 for x := 0; x < b.N; x++ {
100 _, err := NewHTTPClient(context.Background(), &o)
121 _, err := NewHTTPClient(&o)
101122 if err != nil {
102123 b.Fatalf("Got Error: %v", err)
103124 }
00 package libgobuster
1
2 import "context"
13
24 // GobusterPlugin is an interface which plugins must implement
35 type GobusterPlugin interface {
46 Name() string
5 RequestsPerRun() int
6 PreRun() error
7 Run(string, chan<- Result) error
7 PreRun(context.Context) error
8 ProcessWord(context.Context, string, *Progress) error
9 AdditionalWords(string) []string
810 GetConfigString() (string, error)
911 }
1012
13 // Result is an interface for the Result object
1114 type Result interface {
1215 ResultToString() (string, error)
1316 }
88 "strings"
99 "sync"
1010 "time"
11
12 "github.com/fatih/color"
1113 )
1214
1315 // PATTERN is the pattern for wordlist replacements in pattern file
2426
2527 // Gobuster is the main object when creating a new run
2628 type Gobuster struct {
27 Opts *Options
28 context context.Context
29 RequestsExpected int
30 RequestsIssued int
31 RequestsCountMutex *sync.RWMutex
32 plugin GobusterPlugin
33 resultChan chan Result
34 errorChan chan error
35 LogInfo *log.Logger
36 LogError *log.Logger
29 Opts *Options
30 plugin GobusterPlugin
31 LogInfo *log.Logger
32 LogError *log.Logger
33 Progress *Progress
3734 }
3835
3936 // NewGobuster returns a new Gobuster object
40 func NewGobuster(c context.Context, opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
37 func NewGobuster(opts *Options, plugin GobusterPlugin) (*Gobuster, error) {
4138 var g Gobuster
4239 g.Opts = opts
4340 g.plugin = plugin
44 g.RequestsCountMutex = new(sync.RWMutex)
45 g.context = c
46 g.resultChan = make(chan Result)
47 g.errorChan = make(chan error)
4841 g.LogInfo = log.New(os.Stdout, "", log.LstdFlags)
49 g.LogError = log.New(os.Stderr, "[ERROR] ", log.LstdFlags)
42 g.LogError = log.New(os.Stderr, color.New(color.FgRed).Sprint("[ERROR] "), log.LstdFlags)
43 g.Progress = NewProgress()
5044
5145 return &g, nil
5246 }
5347
54 // Results returns a channel of Results
55 func (g *Gobuster) Results() <-chan Result {
56 return g.resultChan
57 }
58
59 // Errors returns a channel of errors
60 func (g *Gobuster) Errors() <-chan error {
61 return g.errorChan
62 }
63
64 func (g *Gobuster) incrementRequests() {
65 g.RequestsCountMutex.Lock()
66 g.RequestsIssued += g.plugin.RequestsPerRun()
67 g.RequestsCountMutex.Unlock()
68 }
69
70 func (g *Gobuster) worker(wordChan <-chan string, wg *sync.WaitGroup) {
48 func (g *Gobuster) worker(ctx context.Context, wordChan <-chan string, wg *sync.WaitGroup) {
7149 defer wg.Done()
7250 for {
7351 select {
74 case <-g.context.Done():
52 case <-ctx.Done():
7553 return
7654 case word, ok := <-wordChan:
7755 // worker finished
7856 if !ok {
7957 return
8058 }
81 g.incrementRequests()
59 g.Progress.incrementRequests()
8260
8361 wordCleaned := strings.TrimSpace(word)
8462 // Skip "comment" (starts with #), as well as empty lines
8765 }
8866
8967 // Mode-specific processing
90 err := g.plugin.Run(wordCleaned, g.resultChan)
68 err := g.plugin.ProcessWord(ctx, wordCleaned, g.Progress)
9169 if err != nil {
9270 // do not exit and continue
93 g.errorChan <- err
71 g.Progress.ErrorChan <- err
9472 continue
9573 }
9674
9775 select {
98 case <-g.context.Done():
76 case <-ctx.Done():
9977 case <-time.After(g.Opts.Delay):
10078 }
10179 }
11896 return nil, fmt.Errorf("failed to get number of lines: %w", err)
11997 }
12098
121 g.RequestsIssued = 0
99 // calcutate expected requests
100 g.Progress.IncrementTotalRequests(lines)
122101
123 // calcutate expected requests
124 g.RequestsExpected = lines
125 if g.Opts.PatternFile != "" {
126 g.RequestsExpected += lines * len(g.Opts.Patterns)
102 // call the function once with a dummy entry to receive the number
103 // of custom words per wordlist word
104 customWordsLen := len(g.plugin.AdditionalWords("dummy"))
105 if customWordsLen > 0 {
106 origExpected := g.Progress.RequestsExpected()
107 inc := origExpected * customWordsLen
108 g.Progress.IncrementTotalRequests(inc)
127109 }
128
129 g.RequestsExpected *= g.plugin.RequestsPerRun()
130110
131111 // rewind wordlist
132112 _, err = wordlist.Seek(0, 0)
136116 return bufio.NewScanner(wordlist), nil
137117 }
138118
139 // Start the busting of the website with the given
119 // Run the busting of the website with the given
140120 // set of settings from the command line.
141 func (g *Gobuster) Start() error {
142 defer close(g.resultChan)
143 defer close(g.errorChan)
121 func (g *Gobuster) Run(ctx context.Context) error {
122 defer close(g.Progress.ResultChan)
123 defer close(g.Progress.ErrorChan)
144124
145 if err := g.plugin.PreRun(); err != nil {
125 if err := g.plugin.PreRun(ctx); err != nil {
146126 return err
147127 }
148128
154134 // Create goroutines for each of the number of threads
155135 // specified.
156136 for i := 0; i < g.Opts.Threads; i++ {
157 go g.worker(wordChan, &workerGroup)
137 go g.worker(ctx, wordChan, &workerGroup)
158138 }
159139
160140 scanner, err := g.getWordlist()
165145 Scan:
166146 for scanner.Scan() {
167147 select {
168 case <-g.context.Done():
148 case <-ctx.Done():
169149 break Scan
170150 default:
171151 word := scanner.Text()
176156 for _, w := range perms {
177157 select {
178158 // need to check here too otherwise wordChan will block
179 case <-g.context.Done():
159 case <-ctx.Done():
160 break Scan
161 case wordChan <- w:
162 }
163 }
164
165 for _, w := range g.plugin.AdditionalWords(word) {
166 select {
167 // need to check here too otherwise wordChan will block
168 case <-ctx.Done():
180169 break Scan
181170 case wordChan <- w:
182171 }
1212 NoProgress bool
1313 NoError bool
1414 Quiet bool
15 WildcardForced bool
1615 Verbose bool
1716 Delay time.Duration
1817 }
55
66 // BasicHTTPOptions defines only core http options
77 type BasicHTTPOptions struct {
8 UserAgent string
9 Proxy string
10 Timeout time.Duration
8 UserAgent string
9 Proxy string
10 NoTLSValidation bool
11 Timeout time.Duration
12 RetryOnTimeout bool
13 RetryAttempts int
1114 }
1215
1316 // HTTPOptions is the struct to pass in all http options to Gobuster
1417 type HTTPOptions struct {
1518 BasicHTTPOptions
16 Password string
17 URL string
18 Username string
19 Cookies string
20 Headers []HTTPHeader
21 FollowRedirect bool
22 NoTLSValidation bool
23 Method string
19 Password string
20 URL string
21 Username string
22 Cookies string
23 Headers []HTTPHeader
24 FollowRedirect bool
25 Method string
2426 }
0 package libgobuster
1
2 import "sync"
3
4 type Progress struct {
5 requestsExpectedMutex *sync.RWMutex
6 requestsExpected int
7 requestsCountMutex *sync.RWMutex
8 requestsIssued int
9 ResultChan chan Result
10 ErrorChan chan error
11 }
12
13 func NewProgress() *Progress {
14 var p Progress
15 p.requestsIssued = 0
16 p.requestsExpectedMutex = new(sync.RWMutex)
17 p.requestsCountMutex = new(sync.RWMutex)
18 p.ResultChan = make(chan Result)
19 p.ErrorChan = make(chan error)
20 return &p
21 }
22
23 func (p *Progress) RequestsExpected() int {
24 p.requestsExpectedMutex.RLock()
25 defer p.requestsExpectedMutex.RUnlock()
26 return p.requestsExpected
27 }
28
29 func (p *Progress) RequestsIssued() int {
30 p.requestsCountMutex.RLock()
31 defer p.requestsCountMutex.RUnlock()
32 return p.requestsIssued
33 }
34
35 func (p *Progress) incrementRequests() {
36 p.requestsCountMutex.Lock()
37 defer p.requestsCountMutex.Unlock()
38 p.requestsIssued++
39 }
40
41 func (p *Progress) IncrementTotalRequests(by int) {
42 p.requestsCountMutex.Lock()
43 defer p.requestsCountMutex.Unlock()
44 p.requestsExpected += by
45 }
11
22 const (
33 // VERSION contains the current gobuster version
4 VERSION = "3.1.0"
4 VERSION = "3.2.0-dev"
55 )
+0
-150
make.bat less more
0 @echo off
1
2 SET ARG=%1
3 SET TARGET=.\build
4 SET BUILDARGS=-ldflags="-s -w" -trimpath
5
6 IF "%ARG%"=="test" (
7 CALL :Test
8 GOTO Done
9 )
10
11 IF "%ARG%"=="clean" (
12 del /F /Q %TARGET%\*.*
13 go clean ./...
14 echo Done.
15 GOTO Done
16 )
17
18 IF "%ARG%"=="windows" (
19 CALL :Windows
20 GOTO Done
21 )
22
23 IF "%ARG%"=="darwin" (
24 CALL :Darwin
25 GOTO Done
26 )
27
28 IF "%ARG%"=="linux" (
29 CALL :Linux
30 GOTO Done
31 )
32
33 IF "%ARG%"=="update" (
34 CALL :Update
35 GOTO Done
36 )
37
38 IF "%ARG%"=="fmt" (
39 CALL :Fmt
40 GOTO Done
41 )
42
43 IF "%ARG%"=="lint" (
44 CALL :Lint
45 GOTO Done
46 )
47
48 IF "%ARG%"=="all" (
49 CALL :Fmt
50 CALL :Update
51 CALL :Lint
52 CALL :Test
53 CALL :Darwin
54 CALL :Linux
55 CALL :Windows
56 GOTO Done
57 )
58
59 IF "%ARG%"=="" (
60 go build -o .\gobuster.exe
61 GOTO Done
62 )
63
64 GOTO Done
65
66 :Test
67 set GO111MODULE=on
68 set CGO_ENABLED=0
69 echo Testing ...
70 go test -v ./...
71 echo Done
72 EXIT /B 0
73
74 :Lint
75 set GO111MODULE=on
76 echo Linting ...
77 go get -u github.com/golangci/golangci-lint@master
78 golangci-lint run ./...
79 rem remove test deps
80 go mod tidy
81 echo Done
82
83 :Fmt
84 set GO111MODULE=on
85 echo Formatting ...
86 go fmt ./...
87 echo Done.
88 EXIT /B 0
89
90 :Update
91 set GO111MODULE=on
92 echo Updating ...
93 go get -u
94 go mod tidy -v
95 echo Done.
96 EXIT /B 0
97
98 :Darwin
99 set GOOS=darwin
100 set GOARCH=amd64
101 set GO111MODULE=on
102 set CGO_ENABLED=0
103 echo Building for %GOOS% %GOARCH% ...
104 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
105 mkdir %DIR% 2> NUL
106 go build %BUILDARGS% -o %DIR%\gobuster
107 set GOARCH=386
108 echo Building for %GOOS% %GOARCH% ...
109 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
110 mkdir %DIR% 2> NUL
111 go build %BUILDARGS% -o %DIR%\gobuster
112 echo Done.
113 EXIT /B 0
114
115 :Linux
116 set GOOS=linux
117 set GOARCH=amd64
118 set GO111MODULE=on
119 set CGO_ENABLED=0
120 echo Building for %GOOS% %GOARCH% ...
121 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
122 mkdir %DIR% 2> NUL
123 go build %BUILDARGS% -o %DIR%\gobuster
124 set GOARCH=386
125 echo Building for %GOOS% %GOARCH% ...
126 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
127 mkdir %DIR% 2> NUL
128 go build %BUILDARGS% -o %DIR%\gobuster
129 echo Done.
130 EXIT /B 0
131
132 :Windows
133 set GOOS=windows
134 set GOARCH=amd64
135 set GO111MODULE=on
136 set CGO_ENABLED=0
137 echo Building for %GOOS% %GOARCH% ...
138 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
139 mkdir %DIR% 2> NUL
140 go build %BUILDARGS% -o %DIR%\gobuster.exe
141 set GOARCH=386
142 echo Building for %GOOS% %GOARCH% ...
143 set DIR=%TARGET%\gobuster-%GOOS%-%GOARCH%
144 mkdir %DIR% 2> NUL
145 go build %BUILDARGS% -o %DIR%\gobuster.exe
146 echo Done.
147 EXIT /B 0
148
149 :Done