← Back to Writeups
CUSTOMMediumReversing

Task of the Ancient Rus

XESXOR9/10/20264 min read
#reversing#custom#medium#transform_inversion#archive_password_recovery#trailer_carving#red_herring_elimination#binary_diffing#slack_space_decoding#windows

Task of the Ancient Rus

Platform: hackerlab | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Windows | Author: XESXOR | Date: 2026-04-06 | Status: Solved Techniques: transform_inversion, archive_password_recovery, trailer_carving, red_herring_elimination, binary_diffing, slack_space_decoding

Summary

Task: a multi-stage archive challenge mixes an AES-encrypted ZIP, fake ancient-style C source, a decoy assembly file, an MP3 with an appended clue, and a modified NASM executable. Solution: invert the checker to recover the ZIP password, ignore the asm red herring, carve the MP3 trailer hint, diff nasm.exe against the official build, and decode the hidden blob from .data slack.

Task of the Ancient Rus — hackerlab

Description

Задание древних Русов

English summary: the archive contains several layers of misdirection: obfuscated fake-C code, an AES-encrypted ZIP, a decoy assembly file, an MP3 with hidden trailing data, and a modified nasm.exe. The goal is to separate real clues from red herrings and recover the final CODEBY{...} flag.

Analysis

Challenge Summary

This is a multi-stage reverse challenge built around progressive clue validation. The real path is: ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  1. reverse the fake C checker to recover the password for flag.zip,
  2. ignore the decoy flag.asm,
  3. inspect the MP3 trailer for a plaintext hint,
  4. compare the bundled nasm.exe with the official NASM release,
  5. decode the altered bytes hidden in PE slack space.

Reconnaissance

The initial ZIP contained:

  • flag.zip
  • ancientslavic.h
  • not_important.c
  • Из архива истории.mp3

Early file triage showed that flag.zip was AES-encrypted, so the most likely starting point was the nearby source code. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

ancientslavic.h defines Russian/Cyrillic macros and exposes a 32-byte constant array through бронь1..бронь32. not_important.c uses those macros to disguise a simple byte-by-byte checker.

Solution

Step 1: Reverse the fake C checker and recover the ZIP password

The checker reads user input, transforms each byte, and compares the result to the hardcoded array прав.

The effective forward transform for byte x at position i is:

text
x ^= 3
x += 7
x += 7
x -= 9
x ^= 9
x += i
x ^= i
x ^= i
x ^= prev
prev = x

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

The two consecutive x ^= i operations cancel out, but they are still present in the source and help sell the obfuscation.

The exact reference array is:

python
correct = [
    0x71,0x33,0x4d,0x65,0x11,0x27,0xa2,0xee,
    0x9e,0xf2,0xc9,0xa3,0xd7,0x55,0x32,0x43,
    0x3b,0x56,0x06,0x87,0xe0,0x9b,0xf9,0x86,
    0xdf,0x47,0xc1,0xb5,0xef,0x7f,0xff,0xd9,
]

Reversing the transform yields the original password bytes:

python
#!/usr/bin/env python3

correct = [0x71,0x33,0x4d,0x65,0x11,0x27,0xa2,0xee,0x9e,0xf2,0xc9,0xa3,0xd7,0x55,0x32,0x43,0x3b,0x56,0x06,0x87,0xe0,0x9b,0xf9,0x86,0xdf,0x47,0xc1,0xb5,0xef,0x7f,0xff,0xd9]
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

prev = 0
out = []
for i, c in enumerate(correct):
    t = c ^ prev
    t = (t - i) & 0xff
    t = t ^ 9
    orig = ((t - 5) & 0xff) ^ 3
    out.append(orig)
    prev = c

print(bytes(out))

Output:

text
b'p@s$w0rD_f0R_tHe_S1aViC_@rcH1ve\n'

The trailing newline comes from fgets, so the actual ZIP password is:

text
p@s$w0rD_f0R_tHe_S1aViC_@rcH1ve

Step 2: Extract the nested archive

Use the recovered password to unpack flag.zip:

bash
7z x -y -p'p@s$w0rD_f0R_tHe_S1aViC_@rcH1ve' flag.zip

‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

This extracts:

  • flag.asm
  • nasm.exe
  • Фрагмент летописи.txt

Step 3: Confirm that flag.asm is a decoy

Reading flag.asm and concatenating the mov al, <num> immediates produces:

text
Th1S_1S_n0T_@_fl@G

So flag.asm is explicitly a red herring. It is worth checking because it looks like a classic assembly-encoded flag, but the recovered string directly tells us not to trust it.

Step 4: Inspect the MP3 and recover the hidden clue from the trailer

The MP3 does not contain the final flag directly. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

  • exiftool only shows theme-like metadata.
  • ffmpeg / ffprobe show that the audio decodes, but the file ends with invalid trailing data.

That is the important clue: the audio itself is fine, but there is extra data appended after the legitimate MP3 stream.

Near the end of the file, at offset 436543 in a 436583-byte file, the last 40 appended bytes contain plaintext:

text
Look for ancientslavic.h on the Internet

This trailer clue explains the challenge design:

  • the MP3 is not the flag container,
  • flag.asm is not the real payload,
  • the author wants the solver to stop chasing decoys and focus on the fake ancient-source theme and the hidden tooling logic around it. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Step 5: Compare nasm.exe with the official NASM build

The extracted nasm.exe is a PE32+ Windows console executable. It is clearly not a stock binary.

The reference build used for comparison was the official NASM 2.16.01 Windows x64 package:

text
https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-2.16.01-win64.zip

Hashes:

  • local nasm.exe: a5a61b4ab76b51acec376539364e7245a757607b2467d9ac56987de931c3d164
  • official nasm.exe: edd81d22451246758450f1d5bd7f4c64937ed6cee06f3c2bbbba82401b32c6d4 ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Only 65 bytes differ, all near raw offset 0x4ecb0 inside .data slack space. That is a classic sign of a minimally patched legitimate binary used as a hidden flag carrier.

Step 6: Decode the hidden blob from .data slack

The interesting region starts at 0x4ecb0 and contains this 37-byte blob:

text
413d44434227493f5eb09f0f633e542d544051392d6652103b2d3156125f541045525f644b

The decoding logic is simple:

python
#!/usr/bin/env python3

blob = bytes.fromhex(
    '413d44434227493f5eb09f0f633e542d544051392d6652103b2d3156125f541045525f644b'
)
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

print(bytes((((b + 0x99) & 0xff) ^ 0x99) for b in blob))

Output:

text
CODEBY{REDACTED}

This is the real flag. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍