-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfavinfo.go
300 lines (256 loc) · 8.61 KB
/
favinfo.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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
package main
import (
"crypto/md5"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/twmb/murmur3"
"github.com/rix4uni/favinfo/banner"
)
// getFaviconUrls extracts all favicons from the given URL.
func getFaviconUrls(baseURL string, client *http.Client, source bool) ([]string, error) {
// Send GET request to the base URL with the custom client (which includes timeout)
resp, err := client.Get(baseURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Parse the HTML
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, err
}
// Parse the base URL to handle relative paths
base, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
// Slice to hold favicon URLs
var favicons []string
// Find all <link rel="icon"> and <link rel="shortcut icon"> elements
doc.Find("link[rel='icon'], link[rel=\"icon\"], link[rel='shortcut icon'], link[rel=\"shortcut icon\"]").Each(func(i int, s *goquery.Selection) {
// Get the href attribute (favicon path)
href, exists := s.Attr("href")
if exists {
// If the href is an absolute URL (starts with http), use it directly
var absoluteURL string
if strings.HasPrefix(href, "http") {
absoluteURL = href
} else {
// If it's a relative URL, resolve it using the base URL
absoluteURL = base.ResolveReference(&url.URL{Path: href}).String()
}
// Remove everything after .png or .ico (strip query parameters)
if strings.Contains(absoluteURL, ".png") {
absoluteURL = strings.Split(absoluteURL, ".png")[0] + ".png"
} else if strings.Contains(absoluteURL, ".ico") {
absoluteURL = strings.Split(absoluteURL, ".ico")[0] + ".ico"
}
// Append the cleaned URL
favicons = append(favicons, absoluteURL)
// If source flag is set, print the [Scraped] message
if source {
fmt.Printf("[Scraped]: %s\n", absoluteURL)
}
}
})
// If no favicons were found, try the /favicon.ico path
if len(favicons) == 0 {
// Try adding "/favicon.ico" to the base domain
faviconURL := base.ResolveReference(&url.URL{Path: "/favicon.ico"}).String()
// Check if this URL returns a 200 status code
resp, err := client.Get(faviconURL)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// If status code is 200, return the favicon URL
if resp.StatusCode == 200 {
favicons = append(favicons, faviconURL)
// If source flag is set, print the [Added] message
if source {
fmt.Printf("[Added]: %s\n", faviconURL)
}
}
}
return favicons, nil
}
// loadFingerprintMap loads the fingerprint mapping from the fingerprint.json file.
func loadFingerprintMap(fileName string) (map[string]string, error) {
file, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer file.Close()
var fingerprintMap map[string]string
decoder := json.NewDecoder(file)
err = decoder.Decode(&fingerprintMap)
if err != nil {
return nil, err
}
return fingerprintMap, nil
}
// calculateMurmurHash processes the favicon data and calculates the Murmur3 hash.
func calculateMurmurHash(faviconBytes []byte) int32 {
// Base64 encode the favicon content
base64Content := base64.StdEncoding.EncodeToString(faviconBytes)
// Split the base64 string into chunks as done in the original code
chunkSize := 76
var chunks []string
for i := 0; i*chunkSize+chunkSize < len(base64Content); i++ {
chunks = append(chunks, base64Content[i*chunkSize:i*chunkSize+chunkSize])
}
// Add the last chunk
lastChunk := base64Content[len(chunks)*chunkSize:]
chunks = append(chunks, lastChunk)
// Combine all chunks into a single string
finalString := ""
for _, chunk := range chunks {
finalString = finalString + chunk + "\n"
}
// Calculate the Murmur3 hash of the final string
return int32(murmur3.StringSum32(finalString))
}
// calculateMD5 calculates the MD5 hash of the favicon data.
func calculateMD5(faviconBytes []byte) string {
hash := md5.New()
hash.Write(faviconBytes)
return fmt.Sprintf("%x", hash.Sum(nil))
}
// calculateSHA256 calculates the SHA256 hash of the favicon data.
func calculateSHA256(faviconBytes []byte) string {
hash := sha256.New()
hash.Write(faviconBytes)
return fmt.Sprintf("%x", hash.Sum(nil))
}
func ensureProtocol(input string, client *http.Client) string {
if !strings.HasPrefix(input, "http://") && !strings.HasPrefix(input, "https://") {
// Try HTTPS first
testURL := "https://" + input
resp, err := client.Head(testURL) // Use HEAD to check availability quickly
if err == nil && resp.StatusCode == http.StatusOK {
return testURL
}
// Fallback to HTTP
return "http://" + input
}
return input
}
func main() {
// Define the flags
timeout := flag.Duration("timeout", 10*time.Second, "Set the HTTP request timeout duration")
version := flag.Bool("version", false, "Print the version of the tool and exit.")
silent := flag.Bool("silent", false, "Silent mode.")
source := flag.Bool("source", false, "Enable source output for where the url coming from scraped or added /favicon.ico")
userAgent := flag.String("H", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", "Set the User-Agent header for HTTP requests")
fingerprintPath := flag.String("fingerprint", "", "Path to the fingerprint.json file (default: $HOME/.config/favinfo/fingerprint.json or ./fingerprint.json)")
flag.Parse()
if *version {
banner.PrintBanner()
banner.PrintVersion()
return
}
if !*silent {
banner.PrintBanner()
}
// Create a custom HTTP client with the specified timeout
client := &http.Client{
Timeout: *timeout,
}
// Add the User-Agent header to the client request
client.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
// Modify the default transport to include the User-Agent header
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DisableKeepAlives: false,
}
// Determine the path to fingerprint.json
var fingerprintFilePath string
if *fingerprintPath != "" {
// Use the custom path provided via the flag
fingerprintFilePath = *fingerprintPath
} else {
// Get the user's home directory
homeDir, err := os.UserHomeDir()
if err != nil {
fmt.Println("Error getting home directory:", err)
return
}
// Check for fingerprint.json in $HOME/.config/favinfo/
configPath := homeDir + "/.config/favinfo/fingerprint.json"
if _, err := os.Stat(configPath); err == nil {
fingerprintFilePath = configPath
} else if _, err := os.Stat("fingerprint.json"); err == nil {
// Fall back to fingerprint.json in the current directory
fingerprintFilePath = "fingerprint.json"
} else {
fmt.Println("Error: fingerprint.json not found in $HOME/.config/favinfo/ or current directory.")
return
}
}
// Load the fingerprint map
fingerprintMap, err := loadFingerprintMap(fingerprintFilePath)
if err != nil {
fmt.Printf("Error loading fingerprint.json from %s: %v\n", fingerprintFilePath, err)
return
}
// Read URL(s) from stdin
var input string
for {
_, err := fmt.Scanln(&input)
if err != nil {
break
}
// Set User-Agent header
req, err := http.NewRequest("GET", input, nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
req.Header.Set("User-Agent", *userAgent)
// Fetch the favicons
input = ensureProtocol(input, client)
favicons, err := getFaviconUrls(input, client, *source)
if err != nil {
fmt.Printf("Error fetching favicons: %v\n", err)
continue
}
// Process each favicon URL
for _, faviconURL := range favicons {
// Fetch the favicon content
faviconBytes, err := fetchFavicon(faviconURL, client)
if err != nil {
fmt.Printf("Error fetching favicon content: %v\n", err)
continue
}
// Calculate the hashes
murmurHash := calculateMurmurHash(faviconBytes)
md5Hash := calculateMD5(faviconBytes)
sha256Hash := calculateSHA256(faviconBytes)
// Find the technology based on the Murmur3 hash
tech := fingerprintMap[fmt.Sprintf("%d", murmurHash)]
// Print the results
fmt.Printf("%s [%d] [%s] [%s] [%s]\n", faviconURL, murmurHash, md5Hash, sha256Hash, tech)
}
}
}
// fetchFavicon fetches the favicon from the given URL.
func fetchFavicon(url string, client *http.Client) ([]byte, error) {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
response, err := client.Get(url)
if err != nil {
return nil, err
}
defer response.Body.Close()
return ioutil.ReadAll(response.Body)
}