SKIP TO MAIN CONTENT

[ WRITEUP NODE / FIELD REPORTS ]

SOLVED CHALLENGES & FIELD ANALYSIS

SECURITY RESEARCH KNOWLEDGE TECHNIQUES
B3S/WRITEUPS/CYBERGAMES-KENYA-2026-INTERGALACTIC-BEACONV0
← BACK TO ARCHIVE
EVENT: CyberGames Kenya 2026CATEGORY: Reverse EngineeringPOINTS: 500 PTS

Cybergames Kenya 2026 - Intergalactic Beaconv0

AUTHORED BY:@bealthguy8/15/2026

Intergalactic - Beacon v0

Summary

The binary is a stripped 64-bit PIE ELF that always prints 72 bytes, but that output is not the flag directly.

There are two layers:

  1. main builds a 72-byte beacon buffer.
  2. If a success bit is not set, the buffer is transformed into a decoy before being written.

The intended solve path for the fixed challenge is to recover the internal success-path plaintext generated by the state machine inside sub_2d90 and identify the seed that makes the internal hash equal the target constant.

Final flag:

SK-CERT{pl4y_w1th_f33db4ck_l00p5_d1e_w1th_0b5cur1ty}

Initial Recon

The workspace only contained one binary:

./beaconv0

Basic checks showed:

  • stripped PIE executable
  • dynamically linked
  • imports included getenv, getpid, syscall, getrandom, clock_gettime, strstr, write

Running it normally produced 72 bytes of binary output, not text.

Main Function Behavior

The top-level flow is:

  • collect several anti-analysis signals
  • derive a 16-bit seed from ptrace
  • derive another value from getrandom or clock_gettime
  • initialize a small context
  • run a larger state machine that fills a 72-byte buffer
  • if the state machine marked success and no anti-analysis checks fired, write the raw 72-byte buffer
  • otherwise mutate the buffer and write a decoy

The anti-analysis checks were:

  • ptrace(PTRACE_TRACEME)-style syscall behavior
  • environment variables:
    • LD_PRELOAD
    • LD_AUDIT
    • DYLD_INSERT_LIBRARIES
  • parent-vs-self pid consistency
  • /proc/self/status parsing for TracerPid:

Under a debugger, the success path is suppressed even if the internal buffer is correct.

Key Observation

The real gate is in sub_2d90.

At the end of that function:

  • 72 plaintext bytes have been produced internally
  • an internal rolling hash at offset +0x100 is compared against 0xd43a15c5
  • if it matches, offset +0x114 is set to 1

That success bit is what main later checks.

So the problem reduces to:

  1. emulate sub_2d90
  2. recover the plaintext bytes passed through the internal output routine
  3. brute-force the 16-bit seed
  4. stop when the internal hash matches 0xd43a15c5

Why I Used Unicorn

I initially reversed a large part of the state machine manually, but sub_2d90 is dense and heavily mixed.

The cleaner approach was:

  • use the real machine code
  • relocate the PIE in memory
  • apply ELF R_X86_64_RELATIVE relocations
  • emulate only the target function with Unicorn
  • hook the internal output helper at 0x401c10

That helper receives each plaintext byte in RDX. By intercepting it, I could collect the true 72-byte success-path plaintext without reproducing the entire algorithm in Python.

Solver Approach

The included solver is:

What it does:

  1. maps the binary into Unicorn memory
  2. applies relative relocations
  3. sets up synthetic stack, FS canary area, and scratch regions
  4. emulates sub_2d90
  5. hooks 0x401c10 to capture plaintext bytes
  6. brute-forces the 16-bit seed space
  7. checks whether the state-machine success bit was set

The winning seed was:

0x5eed

The solver output for that seed is:

VOID::SECTOR-7::SK-CERT{pl4y_w1th_f33db4ck_l00p5_d1e_w1th_0b5cur1ty}::OK

Reproduction

Run:

python3 solve_beacon.py 0x5ee0 0x5ef0

Expected output:

seed=0x5eed
hash=0xd43a15c5
out=72
VOID::SECTOR-7::SK-CERT{pl4y_w1th_f33db4ck_l00p5_d1e_w1th_0b5cur1ty}::OK

Flag

SK-CERT{pl4y_w1th_f33db4ck_l00p5_d1e_w1th_0b5cur1ty}