shmalidator
shmalidator
Platform: rev_kids20 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-06-15 | Status: Solved Techniques: little_endian_dword_reconstruction, orphaned_function_recovery, static_disassembly
Summary
Task: stripped x86-64 ELF key validator whose three call instructions had corrupted rel32 offsets (point at garbage addresses), causing a SEGFAULT. Solution: locate the orphaned validator function bodies still present in .text, read the little-endian dword cmp constants at input+0/+4/+8, and concatenate them into the key REDACTED.
shmalidator — rev_kids20
Description
When I wrote this task, rain dropped into my code and changed my three called functions. Now it's all wet and does not work :( I tried 2^2^2^billion of keys but it only Segfaulted. What is the correct key?
A 64-bit ELF shmalidator.elf (x86-64, dynamically linked, stripped) reads a key via fgets (0x40 bytes), validates it, and prints Valid or Invalid. Running it as intended just SEGFAULTs. The goal is to recover the correct key. Flag is submitted via POST flag on rev-kids20.forkbomb.ru.
Analysis
main is at 0x4006ad. It does:
fgets(buf, 0x40, stdin)- Three sequential
callinstructions, each passingbufand AND-ing the boolean results together. - If all three return true → prints
Valid, otherwiseInvalid.
The catch: the three call instructions point at garbage addresses — 0x2fda2584, 0x32526487, 0x11f3f5b3. These are not real functions; the relative call offsets (the rel32 bytes) were corrupted ("rain dropped in"), so the program jumps into the void and segfaults. The challenge narrative literally describes corrupted call instructions, not corrupted logic.
Key insight: the intended validator function bodies are still present in .text — only the call offsets were clobbered. Disassembling the .text region between the CRT stubs and main reveals three small orphaned functions that were the intended targets:
func1 @ 0x40062d: cmp dword [input+0], 0x59353433 ; LE -> "345Y"
func2 @ 0x400650: cmp dword [input+4], 0x4b433450 ; LE -> "P4CK"
func3 @ 0x400677: cmp dword [input+8], 0x30473141 ; LE -> "A1G0"
Each function returns 1 if its 4-byte slice of the input matches its constant, 0 otherwise. Since main ANDs all three, the key must satisfy all three constraints on consecutive offsets +0 / +4 / +8.
Solution
Each cmp compares a little-endian dword. Decode each 32-bit constant back to its ASCII bytes (low byte first) and concatenate in offset order:
0x59353433→ bytes33 34 35 59→"345Y"0x4b433450→ bytes50 34 43 4b→"P4CK"0x30473141→ bytes41 31 47 30→"A1G0"
python read-only • copy allowed#!/usr/bin/env python3 import struct key = b''.join(struct.pack('<I', v) for v in (0x59353433, 0x4b433450, 0x30473141)) print(key.decode()) # REDACTED
The 12-character key REDACTED satisfies all three checks. The server accepted both the bare form and the flag{...} wrapped form.