bashに深刻な脆弱性 (CVE-2014-6271, CVE-2014-7169)

bashにコード・インジェクション攻撃を許す深刻な脆弱性が見つかりました。(CVE-2014-6271, CVE-2014-7169)

bashを起動する際に、環境変数に特殊な文字列を設定することで、任意の処理を起動することが可能になります。既に修正パッチの配布が開始されていて、GNUのダウンロード・サイトやOSのディストリビュータよりダウンロードできます。

CVE-2014-6271

それでは、CVE-2014-6271について、bashソースコード (variables.c) を追いながら見ていくことにしましょう。

/* 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;
{
  char *name, *string, *temp_string;
  int c, char_index, string_index, string_length, ro;
  SHELL_VAR *temp_var;

  create_variable_tables ();

  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) */

initialize_shell_variables関数では、まず起動時に設定された環境変数を順に読み取り、区切り文字である「=」で名前 (name) と値 (string) に分割しています。char_indexは区切り文字「=」がある位置を指しています。

      temp_var = (SHELL_VAR *)NULL;

      /* 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);

環境変数の値 (string) の先頭から4文字が「() {」にマッチした場合は、名前 (name) 、半角スペース、値 (string) を順に連結したものを temp_string としています。例えば、環境変数 func の値が '() {echo abc}' であった場合、temp_string の内容は「func() {echo abc}」になります。

          if (posixly_correct == 0 || legal_identifier (name))
            parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

そして、temp_string を bashスクリプトとして解析、実行しています。

この機能を使用することで、下記のようにシェル変数だけでなくシェル関数もサブ・シェルに送ることができます。

$ export func='() {echo abc}'
$ bash -c 'func'
abc

しかし、下記のコードを実行するとどうなるでしょうか。

$ env x='() {:;}; echo vulnerable'  bash -c 'echo this is a test'

temp_string の内容が「x() {:;}; echo vulnerable」となるので、解析時に「echo vulnerable」が実行されてしまいます。つまり、「echo vulnerable」を悪意のあるコマンドに置き換えることで、任意のコマンドを実行することができてしまいます。

修正パッチを適用することでチェックが厳しくなり、上記のような環境変数を定義しても警告が出力されるようになります。「echo vulnerable」は実行されません。

修正リリース
  • bash43-025
  • bash42-048
  • bash41-012
  • bash40-039
  • bash32-052
  • bash31-018
  • bash30-017
  • bash205b-008

CVE-2014-7169

CVE-2014-6271に対する修正パッチがリリースされたものの、下記のようなケースには対応していないとの指摘が上がりました。それがCVE-2014-7169です。

$ env X='() { (a)=>\' bash -c "echo date"; cat echo

# expected
./bash: X: line 0: syntax error near unexpected token `)'
./bash: X: line 0: `X () { ()=>\'
./bash: error importing function definition for `X'
date
cat: echo: No such file or directory

# actual
./bash: X: line 1: syntax error near unexpected token `='
./bash: X: line 1: `'
./bash: error importing function definition for `X'
Sat Sep 27 08:34:22 JST 2014

このケースでは、CVE-2014-7169の脆弱性により解析に失敗した時点で「>」に関する情報が残ってしまい、次の「echo date」を実行する際に、「>echo date」として解析、実行してしまいます。その結果、期待された動作は「date」と出力されechoという名前のファイルは作成されないことですが、実際にはdateコマンドの結果がechoという名前のファイルに出力されます。

もう少し詳細に説明すると、「\」に改行が続いた場合は継続行とみなされる必要があるため、前の文字が「>」であったことをeol_ungetc_lookahead変数に格納しておきます。しかし、コマンドはそこで終わっています。その結果、仕様通り解析エラーにはなりますが、不具合バージョンではeol_ungetc_lookahead変数の値をクリアすることなく「echo date」を解析、実行してしまいます。

いくつかのディストリビューションでは、このケースにも対応した修正パッチを配布しています。最新バージョンに対するGNUの修正 (bash43-026) は1箇所のみです。解析エラー時にコールされるparse.yのreset_parser関数で、eol_ungetc_lookahead変数の値をクリアするように変更されています。

修正リリース
  • bash43-026
  • bash42-049
  • bash41-013
  • bash40-040
  • bash32-053
  • bash31-019
  • bash30-018
  • bash205b-009

その他のケース

下記のようなケースは問題ないのでしょうか。

$ cat <<EOF >test.sh
#!/bin/bash
cat /dev/null
EOF

$ chmod a+x test.sh
$ env cat='() { echo rm -rf /; }' ./test.sh
rm -rf /

CVE-2014-6271やCVE-2014-7169の最大の問題点は下記の2点です。

  • 任意の環境変数定義で起こりうる。
  • 期待されていない部分が解析、実行される。

例えば、CGIではUser-Agentの値をHTTP_USER_AGENT環境変数にセットした状態でプログラムが起動されます。従って、User-Agentに'() {:;}; echo 'Content-Type=text/plain'; echo; /bin/cat /etc/passwd'をセットしてシェルで書かれたCGIスクリプトにWebアクセスすると何が起こりうるか、容易に想像していただけると思います。

上記のケースはこの2点のどちらも満たさないので、CVE-2014-6271やCVE-2014-7169と比較すると、さほど大きな問題ではありません。

その他、これまでセキュリティ脆弱性についてあまり考慮されてこなかったbashには、未修正のCVE-2014-7186, CVE-2014-7187をはじめ多数の脆弱性が潜んでいる可能性もあり、継続的に議論が進められています。