-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhash_images.py
More file actions
93 lines (73 loc) · 2.54 KB
/
Copy pathhash_images.py
File metadata and controls
93 lines (73 loc) · 2.54 KB
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
#!/usr/bin/env python3
"""Compute favicon fingerprint hashes for a local file or URL.
Demonstrates how the Shodan/FOFA favicon hash is derived and shows the
accompanying MD5, SHA256, and perceptual hash values.
Usage:
python scripts/hash_images.py favicon.ico
python scripts/hash_images.py https://example.com/favicon.ico
Requirements:
pip install mmh3 Pillow imagehash # imagehash/Pillow optional (phash only)
"""
import argparse
import base64
import hashlib
import io
import sys
import urllib.request
from pathlib import Path
try:
import mmh3
except ImportError:
mmh3 = None
try:
from PIL import Image
import imagehash as _imagehash
except ImportError:
Image = None
_imagehash = None
def fetch_bytes(source: str) -> bytes:
if source.startswith("http://") or source.startswith("https://"):
req = urllib.request.Request(source, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.read()
return Path(source).read_bytes()
def favicon_hash(raw: bytes) -> int | None:
"""MurmurHash3 of the base64-encoded favicon bytes — the Shodan/FOFA hash."""
if mmh3 is None:
return None
return mmh3.hash(base64.encodebytes(raw))
def perceptual_hash(raw: bytes) -> str | None:
if Image is None or _imagehash is None:
return None
try:
with Image.open(io.BytesIO(raw)) as img:
return str(_imagehash.phash(img))
except Exception:
return None
def main() -> None:
parser = argparse.ArgumentParser(
description="Compute favicon hashes from a local file or URL.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=__doc__,
)
parser.add_argument("source", help="Path to a local favicon file or a URL")
args = parser.parse_args()
try:
raw = fetch_bytes(args.source)
except Exception as e:
print(f"Error: could not load '{args.source}': {e}", file=sys.stderr)
sys.exit(1)
fh = favicon_hash(raw)
md5 = hashlib.md5(raw).hexdigest()
sha256 = hashlib.sha256(raw).hexdigest()
ph = perceptual_hash(raw)
print(f"favicon_hash : {fh if fh is not None else '(install mmh3)'}")
print(f"md5 : {md5}")
print(f"sha256 : {sha256}")
print(f"phash : {ph if ph is not None else '(install Pillow + imagehash)'}")
if fh is not None:
print()
print(f"Shodan : http.favicon.hash:{fh}")
print(f"FOFA : icon_hash=\"{fh}\"")
if __name__ == "__main__":
main()