這回的 bash 漏洞很有趣,定義一個 script 函式竟然能夠被自動觸發執行。CVE-2014-6271 漏洞揭露後,經過數日,許多人不斷地在找尋可利用此漏洞的方法,試圖可以進行遠端入侵或本地提權等,也有不少人瘋狂找尋 bash 或其它 shell 的類似問題,當然各資安相關媒體或廠商也不斷 "提醒" 大家要盡快升級 bash,彷彿有了 bash 就會被入侵一般。
目前 shellshock wiki 描述及收集了詳細的資訊,這篇 BASH 代碼注入的安全漏洞 也針對漏洞作了詳細說明,以及 DevCore 的 Shaolin 寫的這篇。所以我打算針對漏洞成因,以及漏洞利用環境作探討。
漏洞原因
在 bash 的原始碼 variables.c 中可以看到 bash 在初始化環境變數時,會掃瞄環境變數中是否有函式定義,其函式定義的 keywords 為 "() {",見第 11 行。若有函式定義的字串,應該僅轉換為函式內容,不應該執行該函式,見第 21 行。
Patch 的方式可見 patch ,主要是導入兩個參數定義
+ #define SEVAL_FUNCDEF 0x080 /* only allow function definitions */ + #define SEVAL_ONECMD 0x100 /* only allow a single command */
然後在原來呼叫 parse_and_execute() 的地方改用
parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
並且在 parse_and_execute() 裡再去檢查是否有 SEVAL_FUNCDEF|SEVAL_ONECMD。
遠端執行指令碼
想要利用這個 bash 漏洞,就要非常了解 Linux 的環境變數繼承關係。事實上,在 Unix 系統中以環境變數來傳遞參數或資料是很平常的事,因為環境變數是 key=value 格式,又具有行程間繼承的作用。
因此,要找到可利用的情況,首先要找尋一個網路服務程式,此程式會將網路上接收的資料或請求(Request)轉換為環境變數,若是網路服務程式再透過 execle()/execvpe()/execve() 或是 system()/popen() 來執行其它程式時,因為上述函式會繼承環境變數的關係,才有成功利用的機會。
以 httpd 為例,其實 httpd 並不會接收遠端傳來的環境變數,而是 httpd 在 mod_rewrite 模組執行時將 HTTP Request 中的欄位轉換填入對應的環境變數中,再經由 mod_cgi 模組執行 CGI 程式。所以 CGI 程式可以透過環境變數的方式取得 HTTP Request 所需要的內容。
例如,將 HTTP Request 中的 User-Agent 欄位的內容,轉換為環境變數為 HTTP_USER_AGENT 的內容,以便任何語言開發的 CGI 程式可以透過環境變數 HTTP_USER_AGENT 取得 User-Agent 欄位內容。
C 語言 CGI 範例 |
在此 C 語言的範例中,即便 system() 中執行的命令是固定,不可被 Command Injection,但透過 CVE-2014-6271,則可以執行任意命令,因為 system() 是透過 /bin/sh -c 來執行 /bin/hostname。
事實上,不只 C 語言開發的 CGI,任何語言開發的 CGI 程式透過 httpd mod_cgi 執行,都可能會被利用此漏洞。不過若是直接用 bash script 開發的 CGI 程式,則不需要再透過呼叫 system(),直接就可以利用此漏洞。
bash script 語言 CGI 範例 |
經過測試 mod_cgi 搭配各語言,發現 perl 和 ruby 在某些情況下無法利用此漏洞。原因是,
perl 和 ruby 在進行 system()/popen() 之類的呼叫時,會先經過最佳化,判斷是否只是執行外部程式而非真的 script 命令,只有是 script 命令才會透過 /bin/sh -c 來執行,否則會直接執行(execve)該外部程式。
例如 perl 語言中:
system ("/bin/env > /dev/null");
system("/bin/uname -a");
第一行的命令字串中因為有 > 重新導向輸出的符號,因此會透過 /bin/sh -c 來執行該命令,但是第二行的命令字串中,僅是直接執行 /bin/uname,因此並不會透過 /bin/sh -c 來執行該命令,也就不會造成漏洞利用。
下列是我在 CentOS 6.4 + Apache 2.2.15 測試了各語言版本 CGI 程式,搭配不同的環境,所測試的結果:
V: 可直接利用或透過 system()/popen() 利用 O: 在未最佳化執行的 system()/popen() 情況下可利用 X: 不可被利用 |
我建議 Linux 伺服器管理者優先將 Linux 伺服器作 bash 升級,並且將網站環境作分析,評估不再使用 mod_cgi 或 mod_fastcgi。另外也請將 Linux 作安全性強化,可參考 Kenduest Lee (小州)的 Security Enhanced Linux。
另外一個例子是透過 DHCP 發送含執行代碼的封包,讓 Linux 的 dhclient 被利用此漏洞來入侵。從 Internet Systems Consortium DHCP Distribution Version 4.2.4 的原始碼來看,的確是 dhclient.c 會從 DHCP 封包中將參數值透過內建的 client_envadd() 轉換成環境變數,再經由 execve() 執行了 dhclient-script (bash script),所以當然也就中獎了。下列兩道指令可用來檢測系統上的 dhclient 是否會被此漏洞影響,當然 patch bash 才是王道。
$ /sbin/dhclient - 2>&1 | grep "ISC"
This version of ISC DHCP is based on the release available
$ which dhclient-script
/sbin/dhclient-script
另一個漏洞 CVE-2014-7169
env X='() { (a)=>\' sh -c "echo date"; cat echo
這行環境變數的設定,會使得原來應該是 echo date,顯示 "date" 字串,變成執行 "date" 指令。從 bash 原始碼中可發現,在 shell_getc (y.tab.c) 讀取輸入字串遇到錯誤時會呼叫 reset_parser() (y.tab.c),然而控制字元讀取位置的 global 變數 int eol_ungetc_lookahead 卻沒有重置歸 0,修補方式請看 patch。
所以 () { (a)=>\ 是用 = 字元故意製造錯誤,字元讀取位置因為沒被重設,還留在 > 字元,因此在讀取 "echo date" 後原本要執行 "echo" 變成了讀取 ">echo date",執行後結果變成執行 "date" 命令,將結果輸出至 "echo" 檔案中。
這個漏洞要利用的條件限制更多,而且不像 CVE-2014-6271 可以指定任何命令執行,而只能讓原本要執行的命令作改變,有點類似當年 Unix IFS 的問題。
結論
[References]
1. https://access.redhat.com/articles/1200223
2. https://securityblog.redhat.com/2014/09/26/frequently-asked-questions-about-the-shellshock-bash-flaws/
3. http://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052
4. http://www.cnblogs.com/LittleHann/p/3992778.html
5. http://www.trendmicro.com/cloud-content/us/pdfs/security-intelligence/white-papers/wp-shellshock.pdf
6. http://jaxbot.me/articles/cases-where-bash-shellshock-is-safe-09-25-2014
7. http://security.stackexchange.com/questions/68448/where-is-bash-shellshock-vulnerability-in-source-code