web-dev-qa-db-ja.com

BashのShellshock脆弱性の背後にある動作は文書化されていますか、それとも意図的なものですか?

最近の脆弱性 CVE-2014-6271Bash が環境変数を解釈する方法 開示されました 。エクスプロイトは、いくつかの環境変数宣言を関数定義として解析するBashに依存していますが、その後、定義に従ってコードを実行し続けます。

$ x='() { echo i do nothing; }; echo vulnerable' bash -c ':'
vulnerable

しかし、私はそれを理解していません。 Bashのマニュアルで、環境変数を関数として解釈する方法について私が見つけたものは何もありません(ただし、継承関数は例外です)。実際、適切な名前付き関数の定義は単に値として扱われます。

$ x='y() { :; }' bash -c 'echo $x'
y() { :; }

しかし、破損したものは何も出力しません。

$ x='() { :; }' bash -c 'echo $x'

$ # Nothing but newline

壊れた関数には名前が付いていないため、呼び出すことはできません。この脆弱性は純粋な実装のバグですか、それとも私が見ることができない意図された機能がありますか?

更新

Barmarのコメントに従って、関数の名前はパラメーター名であると仮定しました。

$ n='() { echo wat; }' bash -c 'n'
wat

以前に試したと誓うことができたが、私は十分に努力しなかったと思います。現在は繰り返し可能です。もう少しテストがあります:

$ env n='() { echo wat; }; echo vuln' bash -c 'n'
vuln
wat
$ env n='() { echo wat; }; echo $1' bash -c 'n 2' 3 -- 4

wat

…そのため、明らかにエクスプロイトの実行時に引数は設定されていません。

とにかく、私の質問に対する基本的な答えは、はい、これがBashが継承された関数を実装する方法です

56
kojiro

これは実装のバグのようです。

明らかに、エクスポートされた関数がbashで機能する方法は、特別にフォーマットされた環境変数を使用することです。関数をエクスポートする場合:

f() { ... }

次のような環境変数を定義します。

f='() { ... }'

新しいシェルは、値が()で始まる環境変数を見つけると、変数名の前に付加して、結果の文字列を実行します。バグは、これには関数定義の後の後の実行も含まれることです。

説明されている修正は明らかに結果を解析して、それが有効な関数定義であるかどうかを確認することです。そうでない場合は、無効な関数定義の試行に関する警告を出力します。

この記事 バグの原因に関する私の説明を確認します。また、修正プログラムがそれを解決する方法についてもう少し詳しく説明します。値をより慎重に解析するだけでなく、エクスポートされた関数を渡すために使用される変数は、特別な命名規則に従います。この命名規則は、CGIスクリプト用に作成された環境変数に使用される命名規則とは異なるため、HTTPクライアントがこのドアに足を踏み入れることはできません。

48
Barmar

以下:

_x='() { echo I do nothing; }; echo vulnerable' bash -c 'typeset -f'
_

プリント

_vulnerable
x () 
{ 
    echo I do nothing
}
declare -fx x
_

bashよりも、_x=..._を解析し、それを関数として発見してエクスポートし、_declare -fx x_を見て、宣言後にコマンドを実行できるようにしたようです。

_echo vulnerable_

_x='() { x; }; echo vulnerable' bash -c 'typeset -f'
_

プリント:

_vulnerable
x () 
{ 
    echo I do nothing
}
_

x

_x='() { x; }; echo Vulnerable' bash -c 'x'
_

プリント

_Vulnerable
Segmentation fault: 11
_

segfaults-無限の再帰呼び出し

すでに定義されている関数を上書きしません

_$ x() { echo Something; }
$ declare -fx x
$ x='() { x; }; echo Vulnerable' bash -c 'typeset -f'
_

プリント:

_x () 
{ 
    echo Something
}
declare -fx x
_

例えばxは以前に(正しく)定義された関数のままです。

Bash 4.3.25(1)-releaseの脆弱性はクローズされているため、

_x='() { echo I do nothing; }; echo Vulnerable' bash -c ':'
_

プリント

_bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
_

しかし-奇妙なこと(少なくとも私にとっては)

_x='() { x; };' bash -c 'typeset -f'
_

まだプリント

_x () 
{ 
    x
}
declare -fx x
_

そしてその

_x='() { x; };' bash -c 'x'
_

セグメンテーション違反もあるので、奇妙な関数定義を受け入れます...

16
jm666

Bashのコード自体を見てみる価値はあると思います。 パッチ は、問題について少し洞察を与えます。特に、

_*** ../bash-4.3-patched/variables.c 2014-05-15 08:26:50.000000000 -0400
--- variables.c 2014-09-14 14:23:35.000000000 -0400
***************
*** 359,369 ****
      strcpy (temp_string + char_index + 1, string);

!     if (posixly_correct == 0 || legal_identifier (name))
!       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))
--- 364,372 ----
      strcpy (temp_string + char_index + 1, string);

!     /* Don't import function names that are invalid identifiers from the
!        environment, though we still allow them to be defined as Shell
!        variables. */
!     if (legal_identifier (name))
!       parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);

      if (temp_var = find_function (name))
_

Bashが関数をエクスポートすると、環境変数として表示されます。次に例を示します。

_$ foo() { echo 'hello world'; }
$ export -f foo
$ cat /proc/self/environ | tr '\0' '\n' | grep -A1 foo
foo=() {  echo 'hello world'
}
_

新しいBashプロセスは、その環境でこのように定義された関数を見つけると、parse_and_execute()を使用して変数内のコードを評価します。通常の悪意のないコードの場合、それを実行すると、Bashで関数が定義され、次に進みます。ただし、汎用の実行関数に渡されるため、Bashは関数定義の後に、その変数で定義されている追加コードを正しく解析して実行します。

新しいコードでは、_SEVAL_ONECMD_と呼ばれるフラグが追加され、Bashに最初のコマンド(つまり、関数定義)のみを評価するように指示し、_SEVAL_FUNCDEF_はfunctio0n定義のみを許可するように指示しています。

13
FatalError

ドキュメントに関する質問については、ここでenvコマンドのコマンドラインドキュメントをご覧ください。構文を調べると、envがドキュメントどおりに機能していることがわかります。

  • オプションで、4つの可能なオプションがあります。
  • -iの同義語としてのオプションのハイフン(後方互換性のため、私は想定しています)
  • 0個以上のNAME = VALUEペア。これらは、関数定義を含むことができる変数の割り当てです。
  • 割り当ての間または割り当ての後にセミコロン(;)は必要ないことに注意してください。
  • 最後の引数は、単一のコマンドの後に引数を続けることができます。使用されているログインに許可が与えられていれば実行されます。セキュリティは、ログインユーザーのアクセス許可を制限し、実行可能ファイルの所有者以外のユーザーがプログラムの読み取りと実行のみが可能で、プログラムを変更できないように、ユーザーがアクセス可能な実行可能ファイルのアクセス許可を設定することによって制御されます。
[ spot@LX03:~ ] env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Set each NAME to VALUE in the environment and run COMMAND.

  -i, --ignore-environment   start with an empty environment
  -u, --unset=NAME           remove variable from the environment
      --help     display this help and exit
      --version  output version information and exit

A mere - implies -i.  If no COMMAND, print the resulting environment.

Report env bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
Report env translation bugs to <http://translationproject.org/team/>
0
DocSalvager