Shellshock(破殼)系列漏洞簡要分析及檢測

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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章