$ cat writeup.md…
$ cat writeup.md…
fortid2026
Task: a Python 3.10 .pyc validates a FortID{...} flag via a length-preserving T9 keypad transform and a SHA-256 gate. Solution: identify the pyc magic, disassemble with marshal+dis, invert the reversed T9 touchtone string using a wordlist and the Nokia theme, and confirm against the SHA-256 digest.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
A flirty "texting" chatbot prompt keeps asking you for the flag. You are given a single file
chall.pyc.
English summary: a compiled Python bytecode file checks a submitted FortID{...} flag. We must reverse the validation logic, invert its transforms, and recover the unique flag that satisfies every check.
The .pyc header starts with magic bytes 6f 0d 0d 0a. Little-endian 0x0d6f = 3439, which is the Python 3.10 (3.10b1) magic number used for all 3.10.x releases (confirmed against the python-xdis magics table). Loading the marshalled code object under any other interpreter raises bad marshal data, so a Python 3.10 interpreter is required.
For Python 3.7+ the code object begins after a 16-byte header:
import marshal, dis data = open('chall.pyc', 'rb').read() code = marshal.loads(data[16:]) # skip 16-byte header dis.dis(code)
user_input must have length 81.startswith('FortID{') and endswith('}').words = user_input[7:-1] strips the wrapper._; each resulting word must be all lowercase ASCII letters OR all digits (two all(... in string.ascii_lowercase) / string.digits list comprehensions).words = '-'.join(words.split('_')) — underscores become dashes.t9text.t9.get_touchtone(words)[::-1] == '33696-7383-3824-4-9465664238-36-32347-878267-8766-348-55487-74-0133-24566'sha256(user_input.encode('utf-8')).hexdigest() == '3576c495f89236515fbbfbfb519dceb9899122e92460d418d2ce37a1ff357e0c't9text.get_touchtone maps each letter to its phone-keypad (T9) digit (a/b/c→2, d/e/f→3, …, w/x/y/z→9) and passes through digits and dashes unchanged. The mapping is length-preserving, so the length-81 constraint holds for any valid letter/digit choice — length alone does not narrow the answer.
Reverse the target string to obtain the forward touchtone, then split on -:
66542-3310-47-78455-843-6678-762878-74323-63-8324665649-4-4283-3837-69633
This yields 14 groups.
...