forked from OJ/gobuster
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
216 lines (194 loc) · 6.02 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package main
//----------------------------------------------------
// Gobuster -- by OJ Reeves
//
// A crap attempt at building something that resembles
// dirbuster or dirb using Go. The goal was to build
// a tool that would help learn Go and to actually do
// something useful. The idea of having this compile
// to native code is also appealing.
//
// Run: gobuster -h
//
// Please see THANKS file for contributors.
// Please see LICENSE file for license details.
//
//----------------------------------------------------
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"github.com/OJ/gobuster/gobusterdir"
"github.com/OJ/gobuster/gobusterdns"
"github.com/OJ/gobuster/libgobuster"
"golang.org/x/crypto/ssh/terminal"
)
func ruler() {
fmt.Println("=====================================================")
}
func banner() {
fmt.Printf("Gobuster v%s OJ Reeves (@TheColonial)\n", libgobuster.VERSION)
}
func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup) {
defer wg.Done()
var f *os.File
var err error
if filename != "" {
f, err = os.Create(filename)
if err != nil {
log.Fatalf("error on creating output file: %v", err)
}
}
for r := range g.Results() {
s, err := r.ToString(g)
if err != nil {
log.Fatal(err)
}
if s != "" {
g.ClearProgress()
s = strings.TrimSpace(s)
fmt.Println(s)
if f != nil {
err = writeToFile(f, s)
if err != nil {
log.Fatalf("error on writing output file: %v", err)
}
}
}
}
}
func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup) {
defer wg.Done()
for e := range g.Errors() {
if !g.Opts.Quiet {
g.ClearProgress()
log.Printf("[!] %v", e)
}
}
}
func progressWorker(c context.Context, g *libgobuster.Gobuster) {
tick := time.NewTicker(1 * time.Second)
for {
select {
case <-tick.C:
g.PrintProgress()
case <-c.Done():
return
}
}
}
func writeToFile(f *os.File, output string) error {
_, err := f.WriteString(fmt.Sprintf("%s\n", output))
if err != nil {
return fmt.Errorf("[!] Unable to write to file %v", err)
}
return nil
}
func main() {
var outputFilename string
o := libgobuster.NewOptions()
flag.IntVar(&o.Threads, "t", 10, "Number of concurrent threads")
flag.StringVar(&o.Mode, "m", "dir", "Directory/File mode (dir) or DNS mode (dns)")
flag.StringVar(&o.Wordlist, "w", "", "Path to the wordlist")
flag.StringVar(&o.StatusCodes, "s", "200,204,301,302,307,403", "Positive status codes (dir mode only)")
flag.StringVar(&outputFilename, "o", "", "Output file to write results to (defaults to stdout)")
flag.StringVar(&o.URL, "u", "", "The target URL or Domain")
flag.StringVar(&o.Cookies, "c", "", "Cookies to use for the requests (dir mode only)")
flag.StringVar(&o.Username, "U", "", "Username for Basic Auth (dir mode only)")
flag.StringVar(&o.Password, "P", "", "Password for Basic Auth (dir mode only)")
flag.StringVar(&o.Extensions, "x", "", "File extension(s) to search for (dir mode only)")
flag.StringVar(&o.UserAgent, "a", "", "Set the User-Agent string (dir mode only)")
flag.StringVar(&o.Proxy, "p", "", "Proxy to use for requests [http(s)://host:port] (dir mode only)")
flag.DurationVar(&o.Timeout, "to", 10*time.Second, "HTTP Timeout in seconds (dir mode only)")
flag.BoolVar(&o.Verbose, "v", false, "Verbose output (errors)")
flag.BoolVar(&o.ShowIPs, "i", false, "Show IP addresses (dns mode only)")
flag.BoolVar(&o.ShowCNAME, "cn", false, "Show CNAME records (dns mode only, cannot be used with '-i' option)")
flag.BoolVar(&o.FollowRedirect, "r", false, "Follow redirects")
flag.BoolVar(&o.Quiet, "q", false, "Don't print the banner and other noise")
flag.BoolVar(&o.Expanded, "e", false, "Expanded mode, print full URLs")
flag.BoolVar(&o.NoStatus, "n", false, "Don't print status codes")
flag.BoolVar(&o.IncludeLength, "l", false, "Include the length of the body in the output (dir mode only)")
flag.BoolVar(&o.UseSlash, "f", false, "Append a forward-slash to each directory request (dir mode only)")
flag.BoolVar(&o.WildcardForced, "fw", false, "Force continued operation when wildcard found")
flag.BoolVar(&o.InsecureSSL, "k", false, "Skip SSL certificate verification")
flag.BoolVar(&o.NoProgress, "np", false, "Don't display progress")
flag.Parse()
// Prompt for PW if not provided
if o.Username != "" && o.Password == "" {
fmt.Printf("[?] Auth Password: ")
passBytes, err := terminal.ReadPassword(int(syscall.Stdin))
// print a newline to simulate the newline that was entered
// this means that formatting/printing after doesn't look bad.
fmt.Println("")
if err != nil {
log.Fatal("[!] Auth username given but reading of password failed")
}
o.Password = string(passBytes)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var plugin libgobuster.GobusterPlugin
switch o.Mode {
case libgobuster.ModeDir:
plugin = gobusterdir.GobusterDir{}
case libgobuster.ModeDNS:
plugin = gobusterdns.GobusterDNS{}
}
gobuster, err := libgobuster.NewGobuster(ctx, o, plugin)
if err != nil {
log.Fatalf("[!] %v", err)
}
if !o.Quiet {
fmt.Println("")
ruler()
banner()
ruler()
c, err := gobuster.GetConfigString()
if err != nil {
log.Fatalf("error on creating config string: %v", err)
}
fmt.Println(c)
ruler()
log.Println("Starting gobuster")
ruler()
}
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
for range signalChan {
// caught CTRL+C
if !gobuster.Opts.Quiet {
fmt.Println("\n[!] Keyboard interrupt detected, terminating.")
}
cancel()
}
}()
var wg sync.WaitGroup
wg.Add(2)
go errorWorker(gobuster, &wg)
go resultWorker(gobuster, outputFilename, &wg)
if !o.Quiet && !o.NoProgress {
go progressWorker(ctx, gobuster)
}
if err := gobuster.Start(); err != nil {
log.Printf("[!] %v", err)
} else {
// call cancel func to free ressources and stop progressFunc
cancel()
// wait for all output funcs to finish
wg.Wait()
}
if !o.Quiet {
gobuster.ClearProgress()
ruler()
log.Println("Finished")
ruler()
}
}