標籤:
20125133 馬國祥
SET-UID程式漏洞實驗一、實驗描述
Set-UID 是Unix系統中的一個重要的安全機制。當一個Set-UID程式啟動並執行時候,它被假設為具有擁有者的許可權。例如,如果程式的擁有者是root,那麼任何人運行這個程式時都會獲得程式擁有者的許可權。Set-UID允許我們做許多很有趣的事情,但是不幸的是,它也是很多壞事情的罪魁禍首。
因此本次實驗的目標有兩點:
1.欣賞好的方面,理解為什麼Set-UID是需要的,以及它是如何被執行的。
2.注意壞的方面,理解它潛在的安全性問題。
二、實驗內容
這是一個探索性的實驗,你的任務是在Linux環境中和Set-UID機制”玩遊戲“,你需要在Linux中完成接下來的實驗任務:
2.1 猜測為什麼“passwd”,“chsh”,“su”,和“sudo”命令需要Set-UID機制,如果它們沒有這些機制的話,會發生什麼,如果你不熟悉這些程式,你可以通話閱讀使用手冊來熟悉它們,如果你拷貝這些命令到自己的目錄下,這些程式就不會是Set-UID程式,運行這些拷貝的程式,觀察將會發生什麼。
從上面的可以看出:將passwd拷貝到/tmp/下,許可權發生了變化(在原目錄下suid位被設定),複件沒有了修改密碼的許可權。
對於“chsh”,“su”,和“sudo”命令,把這些程式拷貝到使用者目錄下,同樣不再具有root許可權。
2.2 在linux環境下運行Set-UID 程式,同時描述並且解釋你的觀察結果2.2.1 以root方式登入,拷貝/bin/zsh 到/tmp, 同時設定拷貝到tmp目錄下的zsh為set-uid root許可權,然後以普通使用者登入,運行/tmp/zsh。你會得到root許可權嗎?請描述你的結果。 2.2.2 拷貝/bin/bash到/tmp目錄,同時設定/tmp目錄下的bash為Set-UID root許可權,然後以普通使用者登入,運行/tmp/bash。你會得到root許可權嗎?請描述你的結果。
可見,同樣的操作,運行複製的zsh可以獲得root許可權,而bash不能。
2.3 從上面步驟可以看出,/bin/bash有某種內在的保護機制可以阻止Set-UID機制的濫用。為了能夠體驗這種內在的保護機制出現之前的情形,我們打算使用另外一種shell程式——/bin/zsh。在一些linux的發行版中(比如Redora和Ubuntu),/bin/sh實際上是/bin/bash的符號連結。為了使用zsh,我們需要把/bin/sh連結到/bin/zsh。
下面的指令將會把預設的shell指向zsh:
$sudo suPassword:#cd /bin#rm sh#ln -s zsh sh
2.4 PATH環境變數的設定
system(const char * cmd)系統調用函數被內嵌到一個程式中執行一個命令,system()調用/bin/sh來執行shell程式,然後shell程式去執行cmd命令。但是在一個Set-UID程式中system()函數調用shell是非常危險的,這是因為shell程式的行為可以被環境變數影響,比如PATH;而這些環境變數可以在使用者的控制當中。通過控制這些變數,用心險惡的使用者就可以控制Set-UID程式的行為。
下面的Set-UID程式被用來執行/bin/ls命令;然後程式員可以為ls命令使用相對路徑,而不是絕對路徑。
int main(){ system("ls"); return 0;}
2.4.1 你能夠設定這個Set-UID程式運行你自己的代碼而不是/bin/ls嗎?如果你能的話,你的代碼具有root許可權嗎?描述並解釋你的觀察。
可以具有root許可權,把/bin/sh拷貝到/tmp目錄下面重新命名為ls(先要確保/bin/目錄下的sh 符號連結到zsh,而不是bash),將環境變數PATH設定為目前的目錄/tmp,運行編譯的程式test。就可以獲得root許可權:
2.4.2 修改/bin/sh使得其返回到/bin/bash,重複上面的攻擊,你仍然可以獲得root許可權嗎?描述並解釋你的觀察。
可見修改sh串連回bash,運行test程式不能使普通使用者獲得root許可權。
2.5 sytem()和execve()的不同
首先確保/bin/sh指向zsh
背景:Bob在為一家審計代理處工作,他正在調查一家公司是否存在詐騙行為。為了這個目的,他需要閱讀這家公司在Unix系統中的所有檔案;另一方面,為了保護系統的可靠性,他不能修改任何一個檔案。為了達到這個目的,Vince——系統的超級使用者為他寫了一個SET-ROOT-UID程式,並且給了Bob可以執行它的許可權。這個程式需要Bob在命令列中打出一個檔案名稱,然後運行/bin/cat命令顯示這個檔案。既然這個程式是以root許可權啟動並執行,它就可以顯示Bob想看的任何一個檔案。然而,既然這個程式沒有寫操作,Vince很確信Bob不能用這個程式修改任何檔案。
#include <string.h>#include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]){ char *v[3]; if(argc < 2) { printf("Please type a file name.\n"); return 1; } v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = 0; //Set q = 0 for Question a, and q = 1 for Question b int q = 0; if (q == 0) { char *command = malloc(strlen(v[0]) + strlen(v[1]) + 2); sprintf(command, "%s %s", v[0], v[1]); system(command); } else execve(v[0], v, 0); return 0 ;}
2.5.1 程式中有 q=0。程式會使用system()調用命令列。這個命令安全碼?如果你是Bob,你能對系統的完整性妥協嗎?你能重新移動一個對你沒有寫入權限的檔案嗎?
這個命令不安全,Bob可能會出於好奇或者個人利益驅使閱讀或者修改只有root使用者才可以啟動並執行一些檔案。比如中:file檔案只有root使用者有讀寫權限,但普通使用者通過運行該程式,閱讀並重新命名了file檔案:
2.5.2 如果令q=1;剛才的攻擊還會有效嗎?請描述並解釋你的觀察。
修改為q=1後,不會有效。前面步驟之所以有效,是因為system()函數調用/bin/sh,連結至zsh,具有root許可權執行了cat file檔案後,接著執行mv file file_new命令。
而當令q=1, execve()函數會把file; mv file file_new 看成是一個檔案名稱,系統會提示不存在這個檔案:
2.6 LD_PRELOAD環境變數
為了保證Set-UID程式在LD_PRELOAD環境的操縱下是安全的,動態連結器會忽略環境變數,但是在某些條件下是例外的,在下面的任務中,我們猜測這些特殊的條件到底是什麼。
1、讓我們建立一個動態連結程式庫。把下面的程式命名為mylib.c,放在/tmp目錄下。在函數庫libc中重載了sleep函數:
#include <stdio.h>void sleep (int s){ printf("I am not sleeping!\n");}
2、我們用下面的命令編譯上面的程式(注意區別l和1):
gcc -fPIC -g -c mylib.cgcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.1 mylib.o –lc
3、把下面的程式命名為myprog.c,放在/tmp目錄下:
int main(){ sleep(1); return 0;}
請在下面的條件下運行這些程式,並觀察結果。基於這些觀察告訴我們連結器什麼時候會忽略LD_PRELOAD環境變數,解釋原因。
2.6.1 把myprog編譯成一個普通使用者下的程式在普通使用者下運行
可見,它會使用LD_PRELOAD環境變數,重載sleep函數:
2.6.2 把myprog編譯成一個Set-UID root的程式在普通使用者下運行
在這種情況下,忽略LD_PRELOAD環境變數,不重載sleep函數,使用系統內建的sleep函數:
2.6.3 把myprog編譯成一個Set-UID root的程式在root下運行
在這種情況下,使用LD_PRELOAD環境變數,使用重載的sleep函數:
2.6.4在一個普通使用者下把myprog編譯成一個Set-UID 普通使用者的程式在另一個普通使用者下運行
在這種情況下,不會重載sleep函數:
由以上四種情況可見:只有使用者自己建立的程式自己去運行,才會使用LD_PRELOAD環境變數,重載sleep函數,否則的話忽略LD_PRELOAD環境變數,不會重載sleep函數。
2.7 消除和清理特權
為了更加安全,Set-UID程式通常會調用setuid()系統調用函數永久的清除它們的root許可權。然而有些時候,這樣做是遠遠不夠的。在root使用者下,在/tmp目錄建立一個空檔案zzz。在root使用者下將下面代碼命名為test.c,放在/tmp目錄下,編譯這個程式,給這個程式設定root許可權。在一個普通的使用者下,運行這個程式。描述你所觀察到的情況,/tmp/zzz這個檔案會被修改嗎?解釋你的觀察。
代碼:
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>void main(){ int fd; //Assume that /tmp/zzz is an important system file, //and it is owned by root with permission 0644 fd = open("/tmp/zzz", O_RDWR | O_APPEND); // Simulate the tasks conducted by the program sleep(1); // After the task, the root privileges are no longer needed, //it’s time to relinquish the root privileges permanently. setuid(getuid()); // getuid() returns the real uid if (fork()) { // In the parent process close (fd); exit(0); } else { // in the child process //Now, assume that the child process is compromised, malicious //attackers have injected the following statements //into this process write (fd, "shiyanlou!", 10); close (fd); }}
結果
檔案被修改了,原因在於設定uid前,zzz檔案就已經被開啟了。只要將語句setuid(getuid())移至調用open函數之前,就能避免這個問題。
三、練習
在實驗樓環境安步驟進行實驗,並
四、總結
Set-UID 是Unix系統中的一個重要的安全機制。當一個Set-UID程式啟動並執行時候,它被假設為具有擁有者的許可權。例如,如果程式的擁有者是root,那麼任何人運行這個程式時都會獲得程式擁有者的許可權。 通過本次實驗,對Set_UID產生了較為初步的認識。同時,也對資訊安全方面的保障有了新的認識。這次實驗過程中遇到了很多問題,在同學的協助下一一克服困難完成了實驗。希望今後自己多多努力。
SET-UID程式漏洞實驗