-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
146 lines (128 loc) · 3.38 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
package main
import (
"bufio"
"flag"
"fmt"
"net/http"
"os"
"strings"
"sync"
"time"
)
// Options defines the options for the scanner
type Options struct {
URL string
Wordlist []string
Threads int
Verbose bool
}
func main() {
url := flag.String("url", "", "The base URL to fuzz")
wordlistFile := flag.String("wordlist", "", "Path to the wordlist file")
threads := flag.Int("threads", 150, "Number of concurrent threads (default 150)")
verbose := flag.Bool("verbose", false, "Enable verbose output")
flag.Parse()
if *url == "" || *wordlistFile == "" {
fmt.Println("URL and wordlist are required")
flag.Usage()
return
}
*url = strings.TrimRight(*url, "/")
wordlist, err := readWordlist(*wordlistFile)
if err != nil {
fmt.Printf("Error reading wordlist: %v\n", err)
return
}
options := Options{
URL: *url,
Wordlist: wordlist,
Threads: *threads,
Verbose: *verbose,
}
client := createHTTPClient(*threads)
processURLs(client, options)
}
// createHTTPClient creates a custom HTTP client
func createHTTPClient(maxConnsPerHost int) *http.Client {
return &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
MaxIdleConnsPerHost: maxConnsPerHost,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// Prevent following redirects
return http.ErrUseLastResponse
},
}
}
// processURLs handles the concurrent processing of URLs
func processURLs(client *http.Client, options Options) {
var wg sync.WaitGroup
urls := make(chan string, options.Threads)
for i := 0; i < options.Threads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for url := range urls {
checkAndPrintURL(client, url, options.Verbose, options.URL)
}
}()
}
for _, path := range options.Wordlist {
urls <- fmt.Sprintf("%s/%s", options.URL, path)
}
close(urls)
wg.Wait()
}
// readWordlist reads the wordlist from a file
func readWordlist(filePath string) ([]string, error) {
fmt.Printf("Reading wordlist from %s\n", filePath)
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("could not open wordlist file: %w", err)
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading wordlist file: %w", err)
}
if len(lines) == 0 {
return nil, fmt.Errorf("wordlist is empty")
}
return lines, nil
}
// checkAndPrintURL checks a single URL using the provided HTTP client and prints results
func checkAndPrintURL(client *http.Client, url string, verbose bool, baseURL string) {
resp, err := client.Get(url)
if err != nil {
if verbose {
fmt.Printf("Error fetching %s: %v\n", url, err)
}
return
}
defer resp.Body.Close()
// Filter based on verbose flag
if !verbose && (resp.StatusCode >= 400 && resp.StatusCode < 500) {
return // Ignore 400-499 status codes if not in verbose mode
}
result := Result{
URL: baseURL,
Path: strings.TrimPrefix(url, baseURL+"/"),
Verbose: verbose,
Header: resp.Header,
StatusCode: resp.StatusCode,
Size: resp.ContentLength,
Found: resp.StatusCode >= 200 && resp.StatusCode < 300,
}
output, err := result.ResultToString()
if err != nil {
fmt.Printf("Error converting result to string: %v\n", err)
return
}
fmt.Print(output)
}