1.漏洞簡介
Bash 4.3以及之前的版本在處理某些構造的環境變量時存在安全漏洞,向環境變量值內的函數定義後添加多餘的字符串會觸發此漏洞,攻擊者可利用此漏洞改變或繞過環境限制,以執行任意的shell命令,甚至完全控制目標系統
受到該漏洞影響的bash使用的環境變量是通過函數名稱來調用的,以“(){”開頭通過環境變量來定義的。而在處理這樣的“函數環境變量”的時候,並沒有以函數結尾“}”爲結束,而是一直執行其後的shell命令。這個漏洞導致了CVE-2014-6271,CVE-2014-7169,CVE-2014-6277,CVE-2014-6278,CVE-2014-7186,CVE-2014-7187六個CVE的爆發。
2.POC驗證
2.1 CVE 2014-6271
最開始的shell測試腳本如下,主要的代碼是第五行,它先使用env命令將() { :;}; echo vulnerable
賦值給環境變量x,隨後啓動一個新的bash進程並執行命令echo test
,並將錯誤信息重定向到null,再查找有無’vulnerable’這個字符串輸出,如果輸出成功則表示存在漏洞:
#!/bin/bash
EXITCODE=0
# CVE-2014-6271
CVE20146271=$(env 'x=() { :;}; echo vulnerable' bash -c "echo test" 2>/dev/null | grep 'vulnerable' | wc -l)
echo -n "CVE-2014-6271 (original shellshock): "
if [ $CVE20146271 -gt 0 ]; then
echo -e "\033[91mVULNERABLE\033[39m"
EXITCODE=$((EXITCODE+1))
else
echo -e "\033[92mnot vulnerable\033[39m"
fi
執行結果如下,由於在新啓動的bash進程會繼承父進程的環境變量,所以x也被繼承,導致了函數後面的echo vulnerable
被執行:
2.2 CVE 2014-7169
打過第一次官網補丁之後還是能夠繞過,腳本如下:
#!/bin/bash
EXITCODE=0
# CVE-2014-7169
CVE20147169=$(rm -f echo; env X='() { (a)=>\' bash -c "echo date" 2>/dev/null; cat echo 2> /dev/null | grep 'vulnerable' | wc -l)
echo -n "CVE-2014-7169 (taviso bug): "
if [ $CVE20147169 -gt 0 ]; then
echo -e "\033[91mVULNERABLE\033[39m"
EXITCODE=$((EXITCODE+8))
else
echo -e "\033[92mnot vulnerable\033[39m"
fi
主要代碼也是第五行,首先也是利用env命令新生成一個環境變量X,X的值是() { (a)=>\
,而這個shell函數並不完整,接着bash -c啓動新的一個bash進程,當其加載環境變量X時便會出錯,出錯的字符(a)=
便不會在緩衝區中,緩衝區中就只剩下了>\
,而新啓動的bash進程又將echo date
輸出到緩衝區,這樣緩衝區中便是:
>\
echo date
這就相當於執行命令:
date > echo
執行結果如下:
3.漏洞原理及修複分析
3.1 CVE 2014-6271
漏洞原理
這個漏洞原理就是在bash解析環境變量x=() { :;}; echo vulnerable
的時候將其命令類型設爲cm_connection,也就是由多條命令組成的,隨後執行execute_connection:
case cm_connection:
exec_result = execute_connection (command, asynchronous,
pipe_in, pipe_out, fds_to_close);
break;
由於兩條指令連接的使用分號,execute_connection中執行的代碼如下,隨後他便會重新執行execute_command_internal函數,而傳入的指令則是() { :;};
:
case ';':
... ...
exec_result = execute_command_internal (command->value.Connection->second,
asynchronous, pipe_in, pipe_out,
fds_to_close);
executing_list--;
break;
函數定義這條指令執行完成再執行後面的echo vulnerable指令,最終通過execute_simple_command執行完成:
case cm_simple:
{
... ...
exec_result =
execute_simple_command (command->value.Simple, pipe_in, pipe_out,
asynchronous, fds_to_close);
line_number = save_line_number;
... ...
}
所以本質上說這個漏洞是傳入命令的邊界沒有控制好,導致定義完函數繼續執行後續指令。
修複分析
針對這個漏洞的補丁在ftp://ftp.gnu.org/gnu/bash/bash-4.1-patches/bash41-012,查看補丁修改的代碼,主要是三處。第一處是在builtins/common.h文件的宏定義中加入了兩個值,用來表示定義的是函數狀態和只允許一條命令執行:
#define SEVAL_FUNCDEF 0x080 /* only allow function definitions */
#define SEVAL_ONECMD 0x100 /* only allow a single command */
隨後在variables.c文件的initialize_shell_variables函數中調用parse_and_execute之前進行了對函數名進行了合法性檢查,而且直接將上面兩個標誌傳入parse_and_execute函數中:
if (legal_identifier (name))
parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
最後是builtins/evalstring.c的parse_and_execute函數增加了如下代碼,如果當前的flags包括SEVAL_FUNCDEF也就是說正在處理的應該是函數,那麼判斷命令類型是不是函數類型,像POC中的在函數後面還定義了命令,從上一節漏洞原理可以看到整條命令的類型是cm_connection,遇到自然就無法繞過了:
if ((flags & SEVAL_FUNCDEF) && command->type != cm_function_def)
{
internal_warning ("%s: ignoring function definition attempt", from_file);
should_jump_to_top_level = 0;
last_result = last_command_exit_value = EX_BADUSAGE;
break;
}
修復之後同一個POC就運行失敗了:
3.2 CVE 2014-7169
漏洞原理
這個漏洞的POC是env x='() { (a)=>\' ./bash_vuln -c "echo date"
,bash_vuln是重新編譯的修補了CVE 2014-6271漏洞的程序。與CVE 2014-6271類似,當bash初始化的時候解析環境變量x會調用parse_and_execute函數,然後在這個函數中調用parse_command函數解析傳入的命令:
int
parse_and_execute (string, from_file, flags)
{
... ...
if (parse_command () == 0)
{ ... ...
}
}
parse_command函數代碼,在這個函數中則會調用yyparse函數,這個函數由yacc語法分析器分析parse.y文件生成的,會對bash_input流也就是bash的輸入命令進行解析,環境變量x的值’() { (a)=>\’就是在這裏解析的識貨報錯,但是由於報錯的是’(a)=’,並不會影響到後面的’>\’字符,因此這兩個字符也就保留了下來:
int
parse_command ()
{
int r;
char *command_to_execute;
... ...
current_command_line_count = 0;
r = yyparse ();
... ...
return (r);
}
而後初始化完成,bash_vuln執行命令echo date則會再次調用parse_command函數進行語法解析,’>\’和後面傳入的’echo date’便組成了命令’date > echo’,導致echo文件生成。
修複分析
針對這個漏洞的補丁在ftp://ftp.gnu.org/gnu/bash/bash-4.1-patches/bash41-013,只在parse.y文件中的reset_parser函數添加了一行代碼,這個函數是用來重置命令的語法解析器的。查看eol_ungetc_lookahead的定義,這個變量是表明上一次的bash命令輸入是否全部處理完了,如果是0則處理完成,否則取上次未處理完的字符,再去讀取新的命令:
*** 2812,2815 ****
--- 2812,2817 ----
word_desc_to_read = (WORD_DESC *)NULL;
+ eol_ungetc_lookahead = 0;
+
current_token = '\n'; /* XXX */
last_read_token = '\n';
在未打補丁的程序中當處理完惡意構造的函數環境變量之後,eol_ungetc_lookahead變量並不爲0,所以在處理下一次命令時會加上上次未處理完的命令,補丁直接在重置語法解析器的時候將eol_ungetc_lookahead變量置爲0,則不會讀取上一次剩下的>\
字符,而是直接解析echo date
,打上補丁之後的運行結果:
4.檢測規則
4.1 CVE 2014-6271檢測規則
這個漏洞的檢測規則如下:
alert http any any -> $HOME_NET any (msg:"Volex – Possible CVE-2014-6271 bash Vulnerability Requested (header)" ; flow:established,to_server; content:"() {"; http_header; threshold:type limit, track by_src, count 1, seconds 120; classtype:current-event; sid:130162711; rev:1;)
規則檢測的是發動攻擊的http request包,檢測http頭部是否有”() {“字符。
參考鏈接
http://www.antiy.com/response/CVE-2014-6271.html
http://blog.knownsec.com/2014/09/bash_3-0-4-3-command-exec-patch-bypass-analysis/
http://seclists.org/fulldisclosure/2014/Oct/9
http://blog.sina.com.cn/s/blog_48ab118d0102v2u2.html
http://www.percona.com/blog/2014/09/26/bash-bug-giving-you-shellshock-cve-2014-6271-update/