最近の脆弱性 CVE-2014-6271 、 Bash が環境変数を解釈する方法 開示されました 。エクスプロイトは、いくつかの環境変数宣言を関数定義として解析する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が継承された関数を実装する方法です。
これは実装のバグのようです。
明らかに、エクスポートされた関数がbash
で機能する方法は、特別にフォーマットされた環境変数を使用することです。関数をエクスポートする場合:
f() { ... }
次のような環境変数を定義します。
f='() { ... }'
新しいシェルは、値が()
で始まる環境変数を見つけると、変数名の前に付加して、結果の文字列を実行します。バグは、これには関数定義の後の後の実行も含まれることです。
説明されている修正は明らかに結果を解析して、それが有効な関数定義であるかどうかを確認することです。そうでない場合は、無効な関数定義の試行に関する警告を出力します。
この記事 バグの原因に関する私の説明を確認します。また、修正プログラムがそれを解決する方法についてもう少し詳しく説明します。値をより慎重に解析するだけでなく、エクスポートされた関数を渡すために使用される変数は、特別な命名規則に従います。この命名規則は、CGIスクリプト用に作成された環境変数に使用される命名規則とは異なるため、HTTPクライアントがこのドアに足を踏み入れることはできません。
以下:
_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'
_
セグメンテーション違反もあるので、奇妙な関数定義を受け入れます...
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定義のみを許可するように指示しています。
ドキュメントに関する質問については、ここでenv
コマンドのコマンドラインドキュメントをご覧ください。構文を調べると、env
がドキュメントどおりに機能していることがわかります。
-i
の同義語としてのオプションのハイフン(後方互換性のため、私は想定しています)[ 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/>