← Back to Writeups
CUSTOMEasyReversing

ron (RE3_ron)

XESXOR9/10/20263 min read
#reversing#custom#easy#hardcoded_key_extraction#rc4_keystream_recovery#stripped_disasm_idiom_recognit#symmetric_cipher_decryption

ron (RE3_ron)

Platform: rev_kids20 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-06-15 | Status: Solved Techniques: hardcoded_key_extraction, rc4_keystream_recovery, stripped_disasm_idiom_recognition, symmetric_cipher_decryption

Summary

Task: stripped 64-bit PIE ELF that RC4-encrypts a 24-byte input with hardcoded key 'blood' and compares to an embedded ciphertext. Solution: recognize the RC4 KSA/PRGA idioms (the 'ron'=Ron Rivest pun), then exploit RC4 symmetry to recover the flag by running RC4(key='blood', ciphertext).

ron (RE3_ron) — rev_kids20

Description

RE3_ron (display name "ron"), category Shtrikh17/Reverse. Platform: rev-kids20.forkbomb.ru

A stripped 64-bit PIE ELF named ron. Running it prints Give me the key: , reads a line, and validates it. On success it prints Correct!. The goal is to recover the accepted key, which is the flag.

Analysis

main is at 0x13ef. Behavior:

  1. fgets(input, 0xff, stdin), strip trailing newline.
  2. Require strlen(input) == 0x18 (24); otherwise print Wrong length and exit.
  3. Set pointer A = .rodata 0x2023 = "blood" (the RC4 key), pointer B = 0x2029 = a 24-byte expected ciphertext blob.
  4. Call 0x138f(rdi="blood", rsi=input, rdx=outbuf):
    • 0x1199 — RC4 KSA: initialize a 256-byte state and mix it with the key using key[i % keylen].
    • 0x1272 — RC4 PRGA: i/j pointer swaps, emit keystream byte S[(S[i] + S[j]) & 0xff], XOR over input into outbuf.
    • So outbuf = RC4_encrypt(key="blood", input).
  5. Compare outbuf byte-by-byte against the 24-byte blob at 0x2029; if all equal -> Correct!. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

The decisive clues:

  • Task name "ron" = Ron Rivest, inventor of RC4.
  • The two unmistakable RC4 idioms in the stripped disassembly:
    • KSA: a 256-entry array filled 0..255, then a loop j = (j + S[i] + key[i % keylen]) & 0xff; swap(S[i], S[j]).
    • PRGA: i = (i+1)&0xff; j = (j+S[i])&0xff; swap; out ^= S[(S[i]+S[j])&0xff].
  • The hardcoded key string "blood" sits in .rodata immediately before the ciphertext (0x2023 then 0x2029).

.rodata contents:

  • 0x2023: "blood\0"
  • 0x2029: ciphertext = e7 eb 7f 68 13 fe d6 40 e3 d1 00 14 60 f0 64 9a 04 b8 59 72 79 38 ed 18 (24 bytes)
  • strings: "Give me the key: ", "Wrong length", "Wrong", "Correct!" ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Solution

RC4 is a symmetric stream cipher: encryption and decryption are the same operation (XOR with the keystream). The program computes RC4(key="blood", flag) == ciphertext. Therefore the flag is simply RC4(key="blood", ciphertext) — no input brute force, no comparison oracle needed.

python
#!/usr/bin/env python3
def rc4(key, data):
    S = list(range(256)); j = 0
    for i in range(256):                      # KSA
        j = (j + S[i] + key[i % len(key)]) & 0xff
        S[i], S[j] = S[j], S[i]
    out = bytearray(); i = j = 0
    for b in data:                            # PRGA
        i = (i + 1) & 0xff
        j = (j + S[i]) & 0xff
        S[i], S[j] = S[j], S[i]
        out.append(b ^ S[(S[i] + S[j]) & 0xff])
    return bytes(out)
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

ct = bytes.fromhex("e7eb7f6813fed640e3d1001460f0649a04b8597279 38ed18".replace(" ", ""))
print(rc4(b"blood", ct))   # -> b'spbctf{REDACTED}'

The result is 24 bytes, matching the 0x18 length gate. Feeding spbctf{REDACTED} to the binary prints Correct!. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍