← All writeups

Hash cracking

autobots (crypto / password cracking)

Flag: P2P{INNOVATIONCENTRE1995!} Recovered password: Innovationcentre1995!

Category: Hash cracking / password recovery Provided file: 1774262947_autobots.zip (a.k.a. autobots_chall) Solved: 2026-05-30

Challenge description:

An employee at the Innovation Centre has left on "less than good" terms, we need to get access to his account. Can you recover his password from the provided hash? A wordlist has already been generated, but brute force attempts are failing.

Whilst enumerating the system a password policy was identified that consists of

  • 15+ Characters
  • Must start with a capital letter
  • Must end in four digits, followed by a symbol

Flag Format: P2P{RECOVEREDPASSWORD} (All Uppercase)


1. Recon

$ unzip -l 1774262947_autobots.zip
  autobots/hash.txt        (33 bytes)
  autobots/wordlist.txt    (25968 bytes, 3303 lines)

$ cat autobots/hash.txt
b372f82c401bcd4ac06232f57dfef159

2. Reading the challenge correctly

The key sentence is "brute force attempts are failing." The password is 15+ characters, so naive brute force is computationally hopeless — that's the intended trap.

The leaked password policy is actually the attack recipe. It tells us the exact shape of every valid password:

[ Capital-start word ] + [ 4 digits ] + [ 1 symbol ]      total length >= 15

This is a classic hybrid attack: take each word from the provided wordlist, capitalize it, and append a 4-digit number plus a symbol.

Search-space reduction: since the total must be ≥ 15 and the suffix is exactly 5 chars (dddd + symbol), the base word must be ≥ 10 characters. That trims the 3303-word list down to 425 usable candidate words.

Candidate space:

425 words  ×  10,000 (0000–9999)  ×  32 symbols  =  136,000,000 candidates

Symbol set = the 32 ASCII specials in Python's string.punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ (here, !).


3. Tooling problem (and the workaround)

The usual tools were unavailable on the box:

So I wrote a small multiprocessing hashlib cracker in pure Python instead.

crack.py:

#!/usr/bin/env python3
import sys, hashlib, string, multiprocessing as mp

TARGET_HEX = "b372f82c401bcd4ac06232f57dfef159"
HASHTYPE   = sys.argv[1] if len(sys.argv) > 1 else "md5"
MINLEN     = int(sys.argv[2]) if len(sys.argv) > 2 else 15

target   = bytes.fromhex(TARGET_HEX)
SYMS     = string.punctuation                                   # 32 symbols
SUFFIXES = [(("%04d" % d) + s).encode() for d in range(10000) for s in SYMS]  # 320,000

def md5_digest(b):  return hashlib.md5(b).digest()
def ntlm_digest(b): return hashlib.new('md4', b.decode().encode('utf-16le')).digest()
DIGEST = md5_digest if HASHTYPE == "md5" else ntlm_digest

def load_words():
    seen, out = set(), []
    for line in open("wordlist.txt", encoding='utf-8', errors='ignore'):
        w = line.rstrip('\r\n')
        if not w: continue
        cap = w[0].upper() + w[1:]                # enforce capital start
        if not ('A' <= cap[0] <= 'Z'): continue
        if len(cap) + 5 < MINLEN: continue        # word + dddd + symbol must reach MINLEN
        if cap in seen: continue
        seen.add(cap); out.append(cap.encode())
    return out

def worker(word):
    for suf in SUFFIXES:
        if DIGEST(word + suf) == target:
            return word.decode() + suf.decode()
    return None

def main():
    words = load_words()
    with mp.Pool(mp.cpu_count()) as pool:
        for res in pool.imap_unordered(worker, words, chunksize=1):
            if res:
                print("[+] FOUND:", res)
                print("[+] verify:", DIGEST(res.encode()).hex())
                pool.terminate(); return
    print("[-] not found")

if __name__ == "__main__":
    main()

Notes:


4. Run

$ python3 crack.py md5 15
[*] type=md5 minlen=15 words=425 suffixes=320000 candidates=136,000,000
    ...400/425 words done
[+] FOUND: Innovationcentre1995!
[+] md5('Innovationcentre1995!') verify = b372f82c401bcd4ac06232f57dfef159

real    2m59s   (4 cores)

Base word innovationcentre is line 2946 of the wordlist; capitalized → Innovationcentre, then 1995 + !.


5. Verification

Policy rule Innovationcentre1995!
15+ characters ✓ 21
Starts with a capital letter I
Ends in four digits, then a symbol 1995 + !
MD5 == target b372f82c401bcd4ac06232f57dfef159
$ python3 -c "import hashlib; print(hashlib.md5(b'Innovationcentre1995!').hexdigest())"
b372f82c401bcd4ac06232f57dfef159

Flag format requires the recovered password in all uppercase:

P2P{INNOVATIONCENTRE1995!}

6. Takeaways