ums (RE4_ums)
ums (RE4_ums)
Platform: rev_kids20 | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-06-15 | Status: Solved Techniques: deterministic_pie_base, red_herring_elimination, ret2win, return_address_overwrite, stack_buffer_overflow
Summary
Task: 32-bit x86 PIE ELF User Management System menu app where get_flag enforces deliberately unsatisfiable record checks (id==2, age==25, name, passport). Solution: ignore the RE red herring — get_flag reads the password via unbounded scanf("%s") into a stack buffer with no canary; ret2win overwrite of saved EIP (96 bytes pad) to obfuscated_print_the_flag at deterministic PIE base 0x004010b5 prints the flag.
ums (RE4_ums) — rev_kids20
Description
Presenting the mighty User Management System. Can you pwn it and access the flag vault?
A 32-bit x86 PIE ELF ums.elf (NOT stripped) implementing a menu-driven User
Management System:
1 = List users
2 = Add user
3 = Delete user
4 = Get flag
5 = Exit
The flag is printed by obfuscated_print_the_flag after get_flag passes its
checks. Goal: make the program print the flag.
Analysis
Data layout
USERS is an array of 120-byte (0x78) records:
| Offset | Field | Type |
|---|---|---|
+0x00 | id | int |
+0x04 | passport | int64 |
+0x0c | name[52] | char |
+0x40 | age | int |
+0x50 | password[40] | char |
Pre-loaded records:
- id 1 — "Gianni Reversace", password
qwerty1212 - id 2 — "Tanya Pwnina", age 8
The next-id counter starts at 3, so any user added through the menu gets
id >= 3.
The intended check is a red herring (unsatisfiable)
get_flag (sub @ 0xca3) prompts for an id and a password, finds the record
with that id, and requires all of:
id == 2passwordmatchesage == 25id == age/10(⇒ id must be 2)name == "Flag_Holder"passportwith(p/100000) == 2*(p%100000)
The only id == 2 record is Tanya (wrong fields, unknown password). Added users
always get id >= 3, so a forged id == 2 record cannot be created through the
menu. The "intended RE puzzle" (forge id==2 via add/delete logic) is
deliberately impossible.
Eliminated dead-ends (documented for future readers)
delete_user'srep movsdcopies0x24dwords = 144 = the full record stride, so it is a clean shift (no exploitable 24-byte overrun).- forward-only
scanf("%s")overflows in add/list paths can only write a record atindex == USER_COUNT, just outside theget_flagscan range. next_id/USER_COUNTcorruption paths are all blocked by the%sno-null-byte + tail-only-add constraints.
The real vulnerability
get_flag reads the password with scanf("%s") into a stack buffer at
[ebp-0x5c] with no length bound and no stack canary
(__stack_chk_fail is absent from the binary).
Distance from the buffer to the saved return address:
0x5c + 4 = 96 bytes
Overwriting the saved EIP with the address of obfuscated_print_the_flag
redirects execution straight into the flag printer, bypassing all the checks.
The scanf("%s") NUL terminator lands at ebp+8 (past the 4-byte return slot),
so the overwrite is clean. This is a classic ret2win stack buffer overflow —
and the challenge title literally says "pwn it".
Solution
Deterministic PIE base under qemu-user
The PIE base is deterministic under the Docker i386/ubuntu:focal qemu-user
setup: 0x00400000 (verified via QEMU_LOG=page across runs). So the win
function address is fixed:
0x00400000 + 0x10b5 (obfuscated_print_the_flag) = 0x004010b5
Recovering buffered output
Output is pipe-buffered and the routine SIGSEGVs after printing (discarding
the buffer). Fix by forcing unbuffered output with stdbuf -o0.
Exploit
stdin sequence: menu 4 (Get flag), identify as id 2 (value ignored by the
overwrite), then the password = 96 bytes padding + little-endian 0x004010b5,
then 5 (Exit):
bash read-only • copy allowedpython3 -c "import sys; sys.stdout.buffer.write(b'4\n2\n'+b'A'*96+(0x4010b5).to_bytes(4,'little')+b'\n5\n')" > exploit_ret.txt docker run --rm -i --platform linux/386 -v "$PWD":/w -w /w i386/ubuntu:focal \ sh -c 'stdbuf -o0 ./ums.elf < exploit_ret.txt'
Output ends with:
flag: REDACTED
Submitted via POST flag; both the bare REDACTED and
flag{...} forms were accepted.
Note for ARM macOS: running a 32-bit x86 ELF requires Docker
i386/ubuntu:focal(slow qemu emulation — run in background, and kill hung containers if Docker reports "no space left on device").