-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfavhunt.py
84 lines (69 loc) · 3.05 KB
/
favhunt.py
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
import json
import requests
import codecs
import mmh3
import argparse
import urllib3
import sys
from concurrent import futures
from pathlib import Path
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def get_hash(domain):
response = requests.get(domain, verify=False, timeout=10)
favicon = codecs.encode(response.content, "base64")
hash = str(mmh3.hash(favicon))
return domain, hash
def main():
parser = argparse.ArgumentParser(description="Find favicon hashes to fingerprint services")
parser.add_argument("-l", help="List of domains", required=False, dest="list")
parser.add_argument("-o", help="JSON output filename", required=False, dest="output")
parser.add_argument("-f", help="Path to fingerprints json file (default: fingerprints.json)", required=False, default="fingerprints.json", dest="fingerprints")
parser.add_argument("-t", help="Threads (default: 20)", type=int, required=False, default=20, dest="threads")
parser.add_argument("-j", "--json", help="Stdout output in JSON", action="store_true")
parser.add_argument("-s", "--silent", help="Only shows results with matched fingerprints", action="store_true")
parser.add_argument("-v", "--verbose", help="Verbose output", action="store_true", required=False)
args = parser.parse_args()
urls = []
threads = []
data = []
output = {}
if args.list:
if Path(args.list).exists():
with open(args.list, 'r') as file:
data = file.read().splitlines()
else:
print("File not found")
return
else:
for line in sys.stdin:
data.append(line)
for domain in data:
if domain.strip()[-1] == '/':
urls.append(f'{domain.strip()}favicon.ico')
else:
urls.append(f'{domain.strip()}/favicon.ico')
with open(args.fingerprints, 'r') as file:
fingerprints = json.load(file)
with futures.ThreadPoolExecutor(args.threads) as executor:
for url in urls:
threads.append(executor.submit(get_hash, domain=url))
for future in futures.as_completed(threads):
try:
domain, hash = future.result()
matched_fingerprint = "None"
if hash in fingerprints.keys():
matched_fingerprint = fingerprints[hash]
output[domain] = {"hash": hash, "matched_fingerprint": matched_fingerprint}
if (args.silent and not matched_fingerprint == "None") or (not args.silent):
if args.json:
print('{"domain": "%s", "fingerprint": "%s", "hash": "%s"}' %(domain, matched_fingerprint, hash))
else:
print(f'[{matched_fingerprint}][{hash}] {domain}')
except Exception as e:
if args.verbose:
print(f'[{type(e).__name__}] {domain}')
if args.output:
with open(args.output, 'w') as file:
json.dump(output, file)
if __name__ == '__main__':
main()