← Back to EHAX 2026

I Guess Bro

Reverse Engineering 50 pts

Challenge Type

Reverse Engineering - RISC-V 64-bit ELF binary (statically linked, stripped)

Tools Used

  • file, strings - initial recon
  • radare2 - disassembly and static analysis
  • Python 3 - decryption and verification

Analysis

Initial Recon

$ file chall
chall: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV),
       statically linked, stripped

The binary is a RISC-V executable, meaning it cannot run natively on x86_64. No QEMU was available, so pure static analysis was required.

String Analysis

strings revealed:

  • Two fake flags: EH4X{n0t_th3_r34l_fl4g} and EH4X{try_h4rd3r_buddy}
  • Prompts: "Can you guess the flag?", "Enter the flag:"
  • Validation messages: "Wrong length!", "Correct! You guessed it!", "Wrong!"
  • The expected input length is 35 characters (checked via li a5, 35 / bne)

Main Function (0x1037e)

  1. Prints banner and reads input (max 128 bytes via fgets)
  2. Strips newline
  3. Checks input length == 35
  4. Calls flag verification at 0x10732

Verification Function (0x10732)

This function performs four checks:

  1. Fake flag rejection - Uses strcmp against both decoy flags. If input matches either, it rejects.
  2. Anti-timing check - Runs a computational loop (sum of odd numbers to 99999) and measures execution time to detect debuggers.
  3. Three validation functions called in sequence:

Check 1: XOR Decryption (0x105cc)

35 bytes of encrypted data are embedded in the binary at file offset 0x47BC8. The decryption algorithm:

decrypted[i] = encrypted[i] ^ (7*i & 0xFF) ^ 0xA5

The decrypted result is compared byte-by-byte against the user's input.

Check 2: Format + Checksum (0x10622)

  • Verifies EH4X{...} wrapper (checks bytes at positions 0-4 and 34)
  • Computes sum of all 35 ASCII byte values
  • Verifies checksum == 3243

Check 3: Custom Hash (0x10700)

  • Calls compute_hash(input, 35) which uses a xoshiro-style mixing function
  • Initial seed: 0xDEADBEEF
  • Each byte is shifted by (i % 8) * 8 bits, XOR'd into state
  • State is mixed via: rotate_left_13(state ^ (constant >> i)) ^ 0xebfa848108987eb0
  • Final hash must equal 0x81cf06f4a08cb5ef

Decryption

Extracting the 35 encrypted bytes from the binary and applying the XOR decryption:

with open('chall', 'rb') as f:
    f.seek(0x47BC8)
    encrypted = f.read(35)

decrypted = bytearray(35)
counter = 0
for i in range(35):
    decrypted[i] = (encrypted[i] ^ (counter & 0xFF) ^ 0xA5) & 0xFF
    counter += 7

print(decrypted.decode())