← Back to Writeups
CUSTOMEasyReversing

RE4 what

XESXOR9/10/20263 min read
#reversing#custom#easy#little_endian_reconstruction#magic_constant_extraction#partial_strncmp_compare#static_analysis#linux

RE4 what

Platform: rev_kids20 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-06-15 | Status: Solved Techniques: little_endian_reconstruction, magic_constant_extraction, partial_strncmp_compare, static_analysis

Summary

Task: 64-bit non-stripped ELF that reads an integer then a string, validates the flag via a 20-byte strncmp plus a hidden dword check. Solution: extract the .rodata prefix 'flag_cafe_0129837465' and append the little-endian magic constant 0x61626364 = 'dcba' to satisfy the final cmp.

RE4 what — rev_kids20

Description

Enter some number and some string. Valid string is the flag

A 64-bit ELF what.elf is given (not stripped, non-PIE, base 0x400000). The flag is the string that passes the binary's validation. It is submitted to the platform via POST flag.

Analysis

Disassembly of main (0x4006fd) reveals a simple but two-staged input flow:

  1. buf = malloc(0x1c) — a 28-byte buffer.
  2. scanf("%d\n", buf) (format at 0x400854 is "%d\n") reads an integer into the first dword of buf (buf[0..3]).
  3. That integer is then loaded into eax and used as the size limit for fgets(buf+4, eax, stdin) — i.e. the number you type controls how many bytes of the string fgets will read.
  4. strncmp(buf+4, "flag_cafe_0129837465", 0x14) compares the first 20 bytes of the string to the constant at 0x400858. Mismatch → prints "Nope!1" (0x40086d) and returns.
  5. On match: printf("%x\n", *(uint*)(buf+0x18)) prints the dword at buf+0x18, then cmp eax, 0x61626364. Equal → "OK!" (0x400878), otherwise "Nope!" (0x40087c). ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Key offset reasoning: the string lives at buf+4. buf+0x18 = buf+24, which is offset 20..23 of the string — the four bytes immediately after the 20-byte prefix. The check cmp eax, 0x61626364 compares that dword to a magic constant.

Because x86-64 is little-endian, the dword 0x61626364 in memory is the byte sequence 0x64, 0x63, 0x62, 0x61 = 'd', 'c', 'b', 'a' = the ASCII string "dcba".

.rodata strings

OffsetString
0x400854"%d\n"
0x400858"flag_cafe_0129837465"
0x40086d"Nope!1"
0x400874"%x\n"
0x400878"OK!"
0x40087c"Nope!"
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

To reach the "OK!" branch the string must:

  • Begin with the 20-byte prefix flag_cafe_0129837465 (satisfies strncmp).
  • Have its bytes at offset 20..23 equal 0x61626364 little-endian = "dcba".

So the full valid flag is the prefix plus the "dcba" suffix:

REDACTED

The integer entered first must be large enough for fgets to read all 24+ bytes of the string (e.g. 30).

Runtime confirmation

bash
# Run under linux/amd64 (e.g. Docker on Apple Silicon)
printf '30\nREDACTED\n' | ./what.elf
# Output:
# 61626364
# OK!

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

The printed 61626364 is exactly the dword read at buf+0x18, confirming the suffix bytes line up with the magic constant.

Reconstruction snippet

python
#!/usr/bin/env python3
import struct
prefix = "flag_cafe_0129837465"          # from .rodata @0x400858 (20 bytes, strncmp len 0x14)
magic  = 0x61626364                        # cmp eax, 0x61626364
suffix = struct.pack("<I", magic).decode() # little-endian -> "dcba"
print(prefix + suffix)                     # REDACTED

Note: the platform rejected the prefix-only string flag_cafe_0129837465 (which only satisfies the strncmp). The accepted flag is the full valid string including the dcba suffix that triggers the final OK! branch. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍