Securinets Friendly CTF 2026 / Misc WRITEUP
APPROVED
No further action was requested.
Walkthrough
Challenge
The player receives only the extensionless file final. It is a valid TeX DVI document composed entirely of rules, so it requires no external fonts.
Mechanism
DVI is a small page-description virtual machine. Its horizontal w and x register instructions both store a signed distance and move the cursor by that distance. Because this document never reuses either register with w0 or x0, the following instruction families render identically:
w1..w4 distance -> hidden bit 0
x1..x4 distance -> hidden bit 1
The operand width is independently varied between three and four bytes. NOPs, ordinary right/down movements, stack operations, and random y/z vertical choices provide unrelated bytecode noise.
The four physical pages are deliberately stored out of order. The first of the ten counters in each bop instruction gives its logical page number.
Solution
- Run
file finalto identify TeX DVI version 2. - Parse the preamble and each
bop/eoppage body. - Record
0for explicitw1..w4instructions and1forx1..x4. - Sort the bit arrays by each page's first
bopcounter. - Concatenate them and pack bits most-significant-bit first.
The resulting data is framed as:
"SEAL" | uint16 flag length | flag | CRC32(flag)
Run the reference solver:
python3 solver.py
Output:
Securinets{apparently_f1nal_d0es_n0t_mean_st0p_l00king}
Regenerate the handout with python3 make.py.
Reference solver
The reference solver supplied with the writeup:
#!/usr/bin/env python3
from pathlib import Path
import struct
import zlib
MAGIC = b"SEAL"
def unpack_bits(bits: list[int]) -> bytes:
output = bytearray()
for offset in range(0, len(bits) - 7, 8):
value = 0
for bit in bits[offset:offset + 8]:
value = (value << 1) | bit
output.append(value)
return bytes(output)
def extract_pages(data: bytes) -> list[tuple[int, list[int]]]:
if data[:2] != bytes([247, 2]):
raise ValueError("not a version-2 DVI file")
position = 14
comment_length = data[position]
position += 1 + comment_length
pages = []
while position < len(data):
opcode = data[position]
position += 1
if opcode == 248:
break
if opcode == 138:
continue
if opcode != 139:
raise ValueError(f"unexpected top-level opcode {opcode}")
counters = struct.unpack(">10i", data[position:position + 40])
position += 44 # ten counters plus previous-page pointer
bits = []
while True:
opcode = data[position]
position += 1
if opcode == 140:
break
if opcode <= 127 or opcode in (138, 141, 142, 147, 152, 161, 166):
continue
if 128 <= opcode <= 131 or 133 <= opcode <= 136:
position += (opcode - 127) if opcode <= 131 else (opcode - 132)
elif opcode in (132, 137):
position += 8
elif 143 <= opcode <= 146:
position += opcode - 142
elif 148 <= opcode <= 151:
bits.append(0)
position += opcode - 147
elif 153 <= opcode <= 156:
bits.append(1)
position += opcode - 152
elif 157 <= opcode <= 160:
position += opcode - 156
elif 162 <= opcode <= 165:
position += opcode - 161
elif 167 <= opcode <= 170:
position += opcode - 166
elif 171 <= opcode <= 234:
pass
elif 235 <= opcode <= 238:
position += opcode - 234
elif 239 <= opcode <= 242:
size = opcode - 238
length = int.from_bytes(data[position:position + size], "big")
position += size + length
else:
raise ValueError(f"unsupported page opcode {opcode}")
pages.append((counters[0], bits))
return pages
def solve(path: Path) -> bytes:
pages = sorted(extract_pages(path.read_bytes()), key=lambda item: item[0])
if [number for number, _ in pages] != list(range(len(pages))):
raise ValueError("logical page counters are incomplete")
packed = unpack_bits([bit for _, page_bits in pages for bit in page_bits])
if not packed.startswith(MAGIC):
raise ValueError("bad payload marker")
length = struct.unpack(">H", packed[4:6])[0]
flag = packed[6:6 + length]
stored_crc = struct.unpack(">I", packed[6 + length:10 + length])[0]
if zlib.crc32(flag) & 0xFFFFFFFF != stored_crc:
raise ValueError("payload CRC mismatch")
return flag
if __name__ == "__main__":
print(solve(Path(__file__).with_name("final")).decode("ascii"))Flag
Securinets{apparently_f1nal_d0es_n0t_mean_st0p_l00king}