← Back to Writeups
CUSTOMMediumReversing

Task of the Ancient Rus

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

Task of the Ancient Rus

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: transform_inversion, archive_password_recovery, trailer_carving, red_herring_elimination, binary_diffing, slack_space_decoding

Summary

Walkthrough of the CUSTOM Reversing Task of the Ancient Rus. 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

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. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key Takeaways / Lessons

  • Mastering transform_inversion, archive_password_recovery, trailer_carving, red_herring_elimination, binary_diffing, slack_space_decoding was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-04-06 • CUSTOM • Task of the Ancient Rus • Difficulty: Medium