2023-07-09 08:24:37 +00:00
|
|
|
import requests
|
|
|
|
import hashlib
|
|
|
|
|
|
|
|
|
2023-07-13 06:52:32 +00:00
|
|
|
def buildhash(url=None, debug=False, method='GET', timeout=5):
|
|
|
|
"""Build a HHHash from an HTTP request to specific url.
|
|
|
|
|
|
|
|
Keyword arguments:
|
2023-07-15 08:27:12 +00:00
|
|
|
- `url` -- the url to build the HHHash from the response headers (default None)
|
|
|
|
- `debug` -- output the headers returned before hashing (default False)
|
|
|
|
- `method` -- HTTP method to use (GET or HEAD) (default GET)
|
|
|
|
- `timeout` -- default timeout for the connect/read timeout of request (default 2)
|
|
|
|
|
|
|
|
For more details about the [HHHash algorithm](https://www.foo.be/2023/07/HTTP-Headers-Hashing_HHHash).
|
|
|
|
"""
|
2023-07-09 08:24:37 +00:00
|
|
|
if url is None:
|
|
|
|
return False
|
2023-07-13 06:52:32 +00:00
|
|
|
if method == 'GET':
|
2023-08-22 12:32:57 +00:00
|
|
|
r = requests.get(url, timeout=timeout, allow_redirects=False)
|
2023-07-13 06:52:32 +00:00
|
|
|
elif method == 'HEAD':
|
2023-08-22 12:32:57 +00:00
|
|
|
r = requests.head(url, timeout=timeout, allow_redirects=False)
|
2023-07-13 06:52:32 +00:00
|
|
|
else:
|
|
|
|
return False
|
2023-07-09 08:24:37 +00:00
|
|
|
hhhash = ""
|
|
|
|
for header in r.headers.keys():
|
|
|
|
hhhash = f"{hhhash}:{header}"
|
|
|
|
m = hashlib.sha256()
|
|
|
|
if debug:
|
|
|
|
print(hhhash[1:])
|
|
|
|
m.update(hhhash[1:].encode())
|
|
|
|
digest = m.hexdigest()
|
|
|
|
return f"hhh:1:{digest}"
|
2023-07-12 20:29:12 +00:00
|
|
|
|
|
|
|
def hash_from_banner(banner, debug=False):
|
2023-07-15 08:27:12 +00:00
|
|
|
"""Create a HHHash from an already fetched banner. Lines without colons will be skipped.
|
2023-07-14 14:13:37 +00:00
|
|
|
|
|
|
|
Keyword arguments:
|
2023-07-15 08:27:12 +00:00
|
|
|
- `banner` -- HTTP banner as a string
|
|
|
|
- `debug` -- output the headers returned before hashing
|
|
|
|
|
|
|
|
Example usage:
|
|
|
|
|
|
|
|
```
|
2023-07-12 20:29:12 +00:00
|
|
|
>>> hash_from_banner('''HTTP/1.1 200 OK
|
|
|
|
... Content-Type: text/html; charset=ISO-8859-1
|
|
|
|
... Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-iV-j91UJEG2jNx4j6EeTug' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
|
|
|
|
... P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
|
|
|
|
... Date: Wed, 12 Jul 2023 20:23:42 GMT
|
|
|
|
... Server: gws
|
|
|
|
... X-XSS-Protection: 0
|
|
|
|
... X-Frame-Options: SAMEORIGIN
|
|
|
|
... Transfer-Encoding: chunked
|
|
|
|
... Expires: Wed, 12 Jul 2023 20:23:42 GMT
|
|
|
|
... Cache-Control: private
|
|
|
|
... Set-Cookie: <removed>
|
|
|
|
... Set-Cookie: <removed>
|
|
|
|
... Set-Cookie: <removed>
|
|
|
|
... Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000''')
|
|
|
|
hhh:1:d9576f3e7a381562f7d18a514ab095fa8699e96891d346d0042f83e942373215
|
2023-07-15 08:27:12 +00:00
|
|
|
|
|
|
|
```
|
2023-07-12 20:29:12 +00:00
|
|
|
"""
|
|
|
|
hhhash = ""
|
|
|
|
for line in banner.splitlines():
|
2023-07-14 14:13:37 +00:00
|
|
|
if ":" not in line:
|
2023-07-12 20:29:12 +00:00
|
|
|
continue
|
2023-07-14 14:13:37 +00:00
|
|
|
|
|
|
|
header, _ = line.split(":", maxsplit=1)
|
|
|
|
hhhash = f"{hhhash}:{header.strip()}"
|
2023-07-12 20:29:12 +00:00
|
|
|
if debug:
|
|
|
|
print(hhhash[1:])
|
|
|
|
m = hashlib.sha256()
|
|
|
|
m.update(hhhash[1:].encode())
|
|
|
|
digest = m.hexdigest()
|
|
|
|
return f"hhh:1:{digest}"
|