← Back to Writeups
CUSTOMMediumReversing

hitrugan

XESXOR9/2/20263 min read
#reversing#custom#medium#conditional_jump_patch#jne_to_nop#static_binary_patch#win_branch_force

hitrugan

Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: conditional_jump_patch, jne_to_nop, static_binary_patch, win_branch_force

Summary

Walkthrough of the CUSTOM Reversing hitrugan. Task: 64-bit PIE keygen crackme that compares hex serial input against transform_rand_value(rand()) where the PRNG is time-seeded, giving one attempt per run so the leaked serial cannot be replayed. S

Description

Survival of the hitriest. Enter the correct serial to get the flag.

A 64-bit x86-64 PIE ELF hitrugan.elf (not stripped), keygen-style. It prints:

[[[ GENERATING UNIQUE SERIAL NUMBER ]]]
[[[ ENTER SERIAL TO GET THE FLAG: ____ ]]]
[[[ VALIDATING ]]]

On a wrong serial it leaks the expected value:

Haha, you fail! Correct serial was %llx, you sucker!!!

Goal: get the flag by entering the correct serial.

Analysis

Relevant functions: main @0xff1, transform_rand_value @0x980, obfuscated_print_the_flag @0x1139, plus _1_stringEncoder / _3_stringEncoder. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

main logic:

  1. srand(time(NULL)) — PRNG seeded with the current time.
  2. Reads the serial with scanf("%llx", &input) (hexadecimal input).
  3. Computes v = transform_rand_value(rand()) and masks to 48 bits: v &= 0xffffffffffff.
  4. Compares input == v. If equal → obfuscated_print_the_flag(). Otherwise prints the failure message that leaks the correct %llx serial.

transform_rand_value @0x980 is a long deterministic chain of mul/add/xor/rol/ror reductions masked to 48 bits. It is fully deterministic given rand(). ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The trap: because the PRNG is seeded with time(NULL), the expected serial changes every run, and the program reads input once per process. So:

  • You cannot leak-then-replay: the leaked "correct serial" is only printed after the (already failed) comparison, and the process exits.
  • Re-running produces a different serial (new time seed), so the previously leaked value is stale.

Reversing the full PRNG (glibc random()) + transform_rand_value chain to predict the serial for the current time-seed before submitting is possible but slow and fragile. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

This is a classic "win function guarded by one conditional jump" pattern. Instead of predicting the time-seeded serial, force the success branch.

The conditional jump right after the serial compare is:

jne <fail>      ; opcode 75 18, at file offset 0x10f9

Patch it to two NOPs (90 90) so execution always falls through into obfuscated_print_the_flag regardless of input.

bash
# verify the guard jump
xxd -s 0x10f9 -l 2 hitrugan.elf      # -> 7518  (jne +0x18)
python
#!/usr/bin/env python3
# Patch the jne guarding the flag printer into two NOPs.
data = bytearray(open("hitrugan.elf", "rb").read())
off = 0x10f9
assert data[off:off+2] == b"\x75\x18", "unexpected bytes at guard jump"
data[off:off+2] = b"\x90\x90"          # jne -> nop; nop
open("hitrugan_patched.elf", "wb").write(data)
print("patched", hex(off))

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

Run the patched binary (under Docker amd64 / qemu-user on a non-x86 host). Any input works because the comparison no longer matters:

bash
echo 0 | docker run --rm -i --platform linux/amd64 \
  -v "$PWD":/w -w /w ubuntu:22.04 ./hitrugan_patched.elf

Output:

[[[ GENERATING UNIQUE SERIAL NUMBER ]]]
[[[ ENTER SERIAL TO GET THE FLAG: ____ ]]]
[[[ VALIDATING ]]]
flag: REDACTED

Note: calling obfuscated_print_the_flag directly via gdb does not work here — gdb/ptrace is unavailable under qemu-user in Docker. The 2-byte jne→nop patch is the cleanest reliable solve. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering conditional_jump_patch, jne_to_nop, static_binary_patch, win_branch_force was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-06-15 • CUSTOM • hitrugan • Difficulty: Medium