Category: Reverse Engineering
Flag: UVT{S0m3_s3cR3tZ_4r_nVr_m3Ant_t0_B_SHRD}
Challenge Description
Congratulations earthling! You found the culprit that deleted those files…
By investigating the USB further, a team member found out that there is a program that would unlock the airlock of that spaceship.
Your mission is to reconstruct the access chain, verify the airlock authentication path and recover the hidden evidence that explains who triggered the wipe, why it was done and what was meant to stay buried.
Analysis
This challenge is the second half of the same USB storyline, so the right mindset was continuity of evidence, not continuity of assumptions. I ignored prior candidate strings and rebuilt the auth path from airlockauth plus artifacts to see what plaintext the binary workflow actually yields.
file "/home/rei/Downloads/airlockauth"/home/rei/Downloads/airlockauth: ELF 64-bit LSB pie executable, x86-64, dynamically linked, strippedchecksec "/home/rei/Downloads/airlockauth"[*] '/home/rei/Downloads/airlockauth'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
SHSTK: Enabled
IBT: EnabledThat output matters because it confirms this is a stripped, hardened checker binary. There is no obvious exploitation route, so the fastest path is to recover logic and required external inputs.
strings -a "/home/rei/Downloads/airlockauth" | rg -i "seed32\.bin|nav\.bc|payload\.enc|signal verified|access denied|UVT\{"UVT{ubH
seed32.bin
nav.bc
payload.enc
signal verified
access deniedThis string set immediately exposes the solve shape: the program depends on three side files and only returns success/fail status, while the partial UVT{... fragment hints at transformed payload content rather than a literal embedded flag.
printf "test\n" | /home/rei/Downloads/airlockauthmissing seedwc -c \
"/home/rei/Downloads/space_usb_extract/user/seed32.bin" \
"/home/rei/Downloads/space_usb_extract/user/nav.bc" \
"/home/rei/Downloads/space_usb_extract/user/payload.enc"32 /home/rei/Downloads/space_usb_extract/user/seed32.bin
256 /home/rei/Downloads/space_usb_extract/user/nav.bc
40 /home/rei/Downloads/space_usb_extract/user/payload.encRunning from the wrong directory fails immediately, which confirms relative-path loading. The 32/256/40 sizes also line up cleanly with a SHA-256-based key schedule and a short encrypted payload.
rg -n "ASTRA9-BRO-1337|openat\(AT_FDCWD, \"seed32.bin\"|openat\(AT_FDCWD, \"nav.bc\"|openat\(AT_FDCWD, \"payload.enc\"|signal verified" \
"/home/rei/Downloads/space_usb_extract/user/trace.txt"50:239155 read(0, "ASTRA9-BRO-1337\n", 4096) = 16
51:239155 openat(AT_FDCWD, "seed32.bin", O_RDONLY) = 3
58:239155 openat(AT_FDCWD, "nav.bc", O_RDONLY) = 3
65:239155 openat(AT_FDCWD, "payload.enc", O_RDONLY) = 3
120:239155 write(1, "signal verified\n", 16) = 16To remove ambiguity, I cross-checked the same values in ltrace.txt; it shows fgets("ASTRA9-BRO-1337\\n"), newline stripping with strcspn, and the same three fopen(..., "rb") calls before puts("signal verified").
rg -n "fgets\(|strcspn\(|fopen\(\"seed32.bin\"|fopen\(\"nav.bc\"|fopen\(\"payload.enc\"|puts\(\"signal verified\"|ASTRA9-BRO-1337" \
"/home/rei/Downloads/space_usb_extract/user/ltrace.txt"1:fgets("ASTRA9-BRO-1337\n", 256, 0x7fa1f03f68e0) = 0x7ffcd2685bf0
2:strcspn("ASTRA9-BRO-1337\n", "\r\n") = 15
3:fopen("seed32.bin", "rb") = 0x55a8c0d6b320
10:fopen("nav.bc", "rb") = 0x55a8c0d6b320
17:fopen("payload.enc", "rb") = 0x55a8c0d6b320
30:strlen("ASTRA9-BRO-1337") = 15
42:puts("signal verified") = 16So the token and file arguments in this solve are evidence-derived, not inferred: exact token from runtime input, exact filenames from runtime open calls, then verified with an actual successful run.

printf "ASTRA9-BRO-1337\n" | /home/rei/Downloads/space_usb_extract/user/airlockauthsignal verifiedWith verifier success confirmed, I reproduced the decryption logic exactly as recovered during reversing: SHA256(nav.bc), then SHA256(seed32.bin || token || nav_hash), then XOR payload.enc with the repeating 32-byte digest.
import hashlib
from pathlib import Path
base = Path("/home/rei/Downloads/space_usb_extract/user")
seed = (base / "seed32.bin").read_bytes()
nav = (base / "nav.bc").read_bytes()
payload = (base / "payload.enc").read_bytes()
token = b"ASTRA9-BRO-1337"
nav_hash = hashlib.sha256(nav).digest()
key = hashlib.sha256(seed + token + nav_hash).digest()
plain = bytes(c ^ key[i % 32] for i, c in enumerate(payload))
print(plain.decode())UVT{S0m3_s3cR3tZ_4r_nVr_m3Ant_t0_B_SHRD}Btw I cross-check the earlier writeup and confirm it was the same string that behaved as bait in the previous challenge.

Solution
# solve.py
#!/usr/bin/env python3.12
import hashlib
from pathlib import Path
def main() -> None:
base = Path("/home/rei/Downloads/space_usb_extract/user")
seed = (base / "seed32.bin").read_bytes()
nav = (base / "nav.bc").read_bytes()
payload = (base / "payload.enc").read_bytes()
token = b"ASTRA9-BRO-1337"
nav_hash = hashlib.sha256(nav).digest()
key = hashlib.sha256(seed + token + nav_hash).digest()
plain = bytes(c ^ key[i % 32] for i, c in enumerate(payload))
print(plain.decode())
if __name__ == "__main__":
main()python3.12 solve.pyUVT{S0m3_s3cR3tZ_4r_nVr_m3Ant_t0_B_SHRD}