From: Georgios Kontaxis Date: Tue, 10 Nov 2015 16:34:13 +0000 (-0500) Subject: hstsPreloadChromium X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=ed3afa5249a4bbc3065d30306103c83a45c81124;p=hstsPreloadChromium.git hstsPreloadChromium --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..537424a --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: all clean + +all: db.sqlite3 + +chromium_hsts_list.dat: + bash get_list.sh + +db.sqlite3: chromium_hsts_list.dat + python makedb.py + +clean: + rm -i chromium_hsts_list.dat db.sqlite3 diff --git a/README.md b/README.md index e1600f6..f54f9db 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ # hstsPreloadChromium + +``` +usage: hstsPreloadChromium.py [-h] [--verbose] E [E ...] + +Look up entries in the Chromium HSTS preload list. + +positional arguments: + E Entry to look up. + +optional arguments: + -h, --help show this help message and exit + --verbose, -v Output information on the lookup process. +``` diff --git a/get_list.sh b/get_list.sh new file mode 100644 index 0000000..e0aa7a8 --- /dev/null +++ b/get_list.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# https://blog.mozilla.org/security/2012/11/01/preloading-hsts/ +URL="https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT" + +curl -L "${URL}" | base64 --decode | + egrep -v "^([ ]*\/\/|$)" > "chromium_hsts_list.dat"; diff --git a/hstsPreloadChromium.py b/hstsPreloadChromium.py new file mode 100755 index 0000000..b87ca70 --- /dev/null +++ b/hstsPreloadChromium.py @@ -0,0 +1,113 @@ +#!/usr/bin/python -u + +# kontaxis 2015-11-03 + +# Chromium maintains an HSTS preload list so that URLs for domains +# supporting HSTS are always automatically rewritten from HTTP to HTTPS +# (if necessary). This eliminates the vulnerability window the first time +# someone visits such domain over HTTP->HTTPS. Mozilla Firefox uses the +# same preload list to implement the same functionality. +# +# This program will take a hostname as input and print it in its output +# if the hostname is found on the HSTS preload list. Otherwise it will +# print nothing. + +# References: +# - https://blog.mozilla.org/security/2012/11/01/preloading-hsts/ + +from __future__ import print_function + +import argparse +import os +import sys +import sqlite3 + +class hstsPreloadChromium: + verbose = False + + _dbConnCursor = None + + def __init__(self, dbPath): + conn = sqlite3.connect(dbpath) + conn.text_factory = str + self._dbConnCursor = conn.cursor() + + def lookup(self, entries): + hits = [] + + for hostname in entries: + self.verbose and print("hsts '%s' : " % hostname, end="") + + self._dbConnCursor.execute('SELECT domain from hsts where domain=?', + (hostname,)) + match = self._dbConnCursor.fetchone() + if match: + hits.append(hostname) + self.verbose and print("TRUE") + continue + + # Lookup was a miss. + self.verbose and print("FALSE") + + # Look for ever shorter wildcards. + labels = hostname.strip(".").split(".") + + for i in range(1, len(labels)): + hsts_wild = ".".join(["*"] + labels[i:len(labels)]) + + self.verbose and print("hsts '%s' : " % hsts_wild, end="") + + self._dbConnCursor.execute('SELECT domain from hsts where domain=?', + (hsts_wild,)) + match = self._dbConnCursor.fetchone() + if match: + hits.append(hostname) + self.verbose and print("TRUE") + break + + # Wildcard lookup was a miss. + self.verbose and print("FALSE") + + return hits + + +if __name__ == "__main__": + + # Parse arguments. + parser = argparse.ArgumentParser( + description="Look up entries in the Chromium HSTS preload list.") + + parser.add_argument("--verbose", "-v", + action="store_const", const=True, default=False, + help = "Output information on the lookup process.") + + parser.add_argument("entries", metavar="E", nargs="+", + help="Entry to look up.") + + args = parser.parse_args() + + # Make sure the SQLite3 database file exists in the same directory. + dirname = os.path.dirname(sys.argv[0]) + dbpath = os.path.join(dirname, "db.sqlite3") + + if not os.path.exists(dbpath): + print("ERROR. Path '%s' is unavailable." % dbpath, file=sys.stderr) + sys.exit(-1) + + if not os.path.isfile(dbpath): + print("ERROR. Path '%s' is not a file." % dbpath, file=sys.stderr) + sys.exit(-1) + + hsts = hstsPreloadChromium(dbpath) + hsts.verbose = args.verbose + + hits = hsts.lookup(args.entries) + for hit in hits: + print("%s" % hit) + + # Success + if hits: + sys.exit(0) + + # Failure + sys.exit(1) diff --git a/makedb.py b/makedb.py new file mode 100644 index 0000000..5978966 --- /dev/null +++ b/makedb.py @@ -0,0 +1,58 @@ +#!/usr/bin/python -u + +# kontaxis 2015-11-03 + +# References: +# - https://blog.mozilla.org/security/2012/11/01/preloading-hsts/ + +from __future__ import print_function + +import json +import os +import sqlite3 +import sys +import time + +dirname = os.path.dirname(sys.argv[0]) + +# Populate hsts records array +hsts = [] + +f = file(os.path.join(dirname, "chromium_hsts_list.dat"), "r") +j = json.loads(f.read()) +f.close() + +for entry in j["entries"]: + if not "mode" in entry or entry["mode"] != "force-https": + continue + # We expect a name. + if not "name" in entry: + continue + hsts.append((entry["name"],)) + if not "include_subdomains" in entry or entry["include_subdomains"] != True: + continue + hsts.append(("*.%s" % entry["name"],)) + +# Make it happen +conn = sqlite3.connect("db.sqlite3") +conn.text_factory = str +c = conn.cursor() + +# Create schema. +c.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", + ("last_generated",)) +match = c.fetchone() +if not match: + c.execute("CREATE TABLE last_generated (epoch integer);") + c.execute("CREATE TABLE hsts (domain text);") + c.execute("CREATE INDEX hsts_domain on hsts (domain);") + +c.execute('DELETE FROM last_generated'); +c.execute('INSERT INTO last_generated VALUES(?)', + (str(int(time.time())),)) + +c.execute('DELETE FROM hsts'); +c.executemany('INSERT INTO hsts VALUES (?)', hsts) + +conn.commit() +conn.close()