-
Notifications
You must be signed in to change notification settings - Fork 6
/
getfavicon
executable file
·101 lines (91 loc) · 3.81 KB
/
getfavicon
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
#!/usr/bin/env python3
# get favicon data; hash (md5 & mmh3), full path location, external search urls (shodan, censys, binaryedge, zoomeye, fofa)
import argparse
import logging
import hashlib
import requests
import mmh3
import codecs
from bs4 import BeautifulSoup
from urllib.parse import urljoin
URL_PATTERN = {
"shodan": "https://www.shodan.io/search?query=http.favicon.hash%3A",
"censys": "https://search.censys.io/search?resource=hosts&q=services.http.response.favicons.md5_hash%3A",
"binaryedge": "https://app.binaryedge.io/services/query?query=web.favicon.mmh3%3A",
"zoomeye": "https://www.zoomeye.org/searchResult?q=iconhash%3A",
"fofa": "https://en.fofa.info/result?qbase64="
}
log = logging.getLogger(__name__)
def setup_logging(debug):
level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(format="%(levelname)s: %(message)s", level=level)
def get_hash(encodedfavicon, algorithm):
if algorithm == "mmh3":
return mmh3.hash(encodedfavicon)
elif algorithm == "md5":
return hashlib.md5(encodedfavicon).hexdigest()
def get_favicon_64(domain, requestobject):
domain = domain.rstrip('/')
try:
soup = BeautifulSoup(requestobject.text, features="lxml")
except Exception as e:
log.error(f"Error parsing HTML: {str(e)}")
return None, None
icon_link = soup.find("link", rel=lambda x: x and x.lower() in ["shortcut icon", "icon"])
if icon_link is None:
location = urljoin(domain, '/favicon.ico')
elif icon_link["href"].startswith('data:image/'):
return icon_link["href"].split(',')[1], None
else:
location = urljoin(domain, icon_link["href"])
log.debug(f'favicon location is: {location}')
try:
favicondata = requests.get(location)
except Exception as e:
log.error(f"error fetching favicon: {str(e)}")
return None, None
if favicondata is None or favicondata.status_code != 200:
log.info(f'favicon location ({location}) returned no response or invalid status code')
return None, None
favicon64 = codecs.encode(favicondata.content, "base64")
return favicon64, favicondata.content
def print_and_log(message, level="info"):
if level == "info":
log.info(message)
elif level == "error":
log.error(message)
def print_search_url(hash, source):
message = f'{source}: {URL_PATTERN[source]}{hash}'
print_and_log(message)
def main():
parser = argparse.ArgumentParser(description="retrieve and hash the favicon from a given domain.")
parser.add_argument("domain", help="the domain to fetch the favicon from.")
parser.add_argument("--debug", action="store_true", help="enable debug logging.")
args = parser.parse_args()
setup_logging(args.debug)
try:
request_object = requests.get(args.domain)
except Exception as e:
log.error(f"failed to fetch the domain: {str(e)}")
return
if request_object.status_code != 200:
log.error(f"failed to fetch the domain. Status code: {request_object.status_code}")
return
favicon_64, favicon_raw = get_favicon_64(args.domain, request_object)
if favicon_64 is None:
log.warning("failed to retrieve the favicon.")
return
favicon_mmh3 = get_hash(favicon_64, "mmh3")
favicon_md5 = get_hash(favicon_64, "md5")
print_search_url(favicon_mmh3, "shodan")
print_search_url(favicon_md5, "censys")
print_search_url(favicon_mmh3, "binaryedge")
print_search_url(favicon_mmh3, "zoomeye")
fofa_str = f'icon_hash={favicon_mmh3}'
fofa_64 = codecs.encode(fofa_str.encode(), "base64").decode('utf-8')
print_search_url(fofa_64, "fofa")
print(f'favicon mmh3 hash: {favicon_mmh3}')
print(f'favicon md5 hash: {favicon_md5}')
print(f'favicon location: {args.domain}/favicon.ico')
if __name__ == "__main__":
main()