Skip to content

Instantly share code, notes, and snippets.

@timmc
Last active April 6, 2022 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timmc/f11dbe7c6cfc618062aa93207fb0daa4 to your computer and use it in GitHub Desktop.
Save timmc/f11dbe7c6cfc618062aa93207fb0daa4 to your computer and use it in GitHub Desktop.
Avvo reverse attempts
"""
Attempt to reverse-engineer Avvo.com's hash algorithm,
given a leaked record and a known password.
Sample for validation of hash checker:
$ cat ~/tmp/ram/sample-record.txt
some@email,1199546,3e6139dd5bba6c8617c112abdb026a648e9bf592,45aa026ed8621c824dfa0acbb478d3eef87020bd,NULL
$ cat ~/tmp/ram/sample-password.txt
hello
$ python3 ~/adhoc/avvo.py ~/tmp/ram/sample-record.txt ~/tmp/ram/sample-password.txt
FOUND! Matched second hash: input=b'3e6139dd5bba6c8617c112abdb026a648e9bf592;some@email_1199546=hello'
"""
import base64
import hashlib
import itertools
import sys
def intercalate(xs, ys):
"""
Interleave xs and ys, where ys is one element shorter than xs.
"""
return [el for pair in zip(xs, list(ys) + [None]) for el in pair][:-1]
def main(record_path, password_path):
with open(record_path, 'rb') as f:
record = f.read()
(email, seqid, hex1, hex2, _) = record.split(b',')
bytes1 = bytes.fromhex(hex1.decode())
bytes2 = bytes.fromhex(hex2.decode())
with open(password_path, 'rb') as f:
password = f.read().split(b'\n')[0]
symbols = [c.encode() for c in "`-=[]\\;',./~!@#$%^&*()_+{}|:\"<>? "]
delimiters = symbols + [b'']
def check(pieces):
input = b''.join(pieces)
h = hashlib.sha1()
h.update(input)
if h.digest() == bytes1:
print(f"FOUND! Matched first hash: {input=!r}")
exit(0)
if h.digest() == bytes2:
print(f"FOUND! Matched second hash: {input=!r}")
exit(0)
def check_parts(parts):
"""
Given 1+ pieces of data, try all permutations and in combination with
various delimiters.
"""
for perm in itertools.permutations(parts):
for delims in itertools.product(delimiters, repeat=len(parts)-1):
check(intercalate(perm, delims))
def check_all(password_variant):
combos = [
[password_variant],
[password_variant, email],
[password_variant, seqid],
[password_variant, email, seqid],
]
for combo in combos:
check_parts(combo)
for salt in [hex1, hex2, bytes1, bytes2]:
check_parts(combo + [salt])
check_all(password)
check_all(password.hex().encode())
check_all(base64.b64encode(password))
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: avvo.py <record_file> <password_file>")
exit(0)
main(sys.argv[1], sys.argv[2])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment