env x = '(){:;};とは何ですか command 'bash doとなぜ安全でないのですか?


237

bashには明らかに脆弱性(CVE-2014-6271)があります:Bashの特別に細工された環境変数コードインジェクション攻撃

私は何が起こっているのかを理解しようとしていますが、私はそれを理解しているとは完全に確信していません。echo単一引用符で囲まれたまま実行するにはどうすればよいですか?

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

編集1:パッチを適用したシステムは次のようになります。

$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test

編集2:関連する脆弱性/パッチがあります:CVE-2014-7169はわずかに異なるテストを使用します:

$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"

パッチ未適用の出力

vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test

部分的に(初期バージョン)パッチされた出力

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
bash: error importing function definition for `BASH_FUNC_x()'
test

CVE-2014-7169までのパッチされた出力

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `BASH_FUNC_x'
test

編集3:物語は次のように続きます:


実行されるのはエコーではありません。そのxの関数定義。xで定義された関数が巧妙な下手な作業を行う場合、関数xが実数である場合、bashが戻り値をチェックする方法はありません。テストコードで関数が空になっていることに注意してください。チェックされていない戻り値は、スクリプトインジェクションにつながる可能性があります。スクリプトインジェクションは特権の昇格につながり、特権の昇格はルートアクセスにつながります。このパッチは、関数としてのxの作成を無効にします
eyoung100 14

26
eyoung100、エコー実行されていません。単語vulnerableが出力に表示されるため、実行されていることがわかります。主な問題は、bashが関数定義のにコードを解析して実行することです。別の例については/bin/idseclists.org / oss-sec / 2014 / q3 / 650の一部を参照してください。
ミケル14

4
簡単なサイドコメント。Red Hatは、リリースされたパッチは部分的なパッチにすぎず、システムを依然として危険にさらしているとアドバイスしています。
ピーター14

2
@ eyoung100の違いは、関数内のコードは、環境変数が明示的に呼び出されたときにのみ実行されることです。関数定義後のコードは、新しいBashプロセスが開始されるたびに実行されます。
デビッドファレル14

1
詳細については、stackoverflow.com
questions / 26022248 /…を

回答:


204

bashは、エクスポートされた関数定義を環境変数として保存します。エクスポートされた関数は次のようになります。

$ foo() { bar; }
$ export -f foo
$ env | grep -A1 foo
foo=() {  bar
}

つまり、環境変数fooにはリテラルの内容があります。

() {  bar
}

bashの新しいインスタンスが起動すると、これらの特別に細工された環境変数を探し、それらを関数定義として解釈します。あなたも自分で書いて、それがまだ機能することを見ることができます:

$ export foo='() { echo "Inside function"; }'
$ bash -c 'foo'
Inside function

残念ながら、文字列(環境変数)からの関数定義の解析は、意図したよりも広い効果をもたらす可能性があります。パッチを当てていないバージョンでは、関数定義の終了後に発生する任意のコマンドも解釈します。これは、環境内で許容される関数のような文字列の決定における制約が不十分なためです。例えば:

$ export foo='() { echo "Inside function" ; }; echo "Executed echo"'
$ bash -c 'foo'
Executed echo
Inside function

関数定義外のエコーは、bashの起動中に予期せず実行されていることに注意してください。関数定義は、評価とエクスプロイトを実現するための単なるステップであり、関数定義自体と使用される環境変数は任意です。シェルは環境変数を見てfoo、を参照します。これは、関数定義がどのように見えるかについて知っている制約を満たしているように見え、行を評価します。

これは安全ではないと考えられます。変数は通常、変数に含まれる任意のコードの呼び出しを直接引き起こすこと自体が許可または予期されていないためです。おそらく、プログラムは信頼できないユーザー入力から環境変数を設定します。これらの環境変数が、コードで宣言されたそのような理由でその環境変数を使用する明示的な意図なしにユーザーが任意のコマンドを実行できるように操作できることは非常に予想外です。

実行可能な攻撃の例を次に示します。脆弱なシェルを実行するWebサーバーを、その存続期間の一部として実行します。このWebサーバーは環境変数をbashスクリプトに渡します。たとえば、CGIを使用している場合、HTTP要求に関する情報は多くの場合、Webサーバーからの環境変数として含まれます。たとえばHTTP_USER_AGENT、ユーザーエージェントのコンテンツに設定される場合があります。これは、ユーザーエージェントを '(){:; }; echo foo 'は、そのシェルスクリプトの実行時echo fooに実行されます。繰り返しecho fooますが、悪意の有無にかかわらず、何でもかまいません。


3
これは、Zshのような他のBashのようなシェルに影響しますか?
アメリオバスケスレイナ14

3
@ user815423426いいえ、zshにはこの機能はありません。Kshにはそれがありますが、実装方法が異なります。関数は、環境を介さずにシェルが分岐した場合にのみ、非常に狭い状況でのみ送信できると思います。
ジル14

20
@ user815423426 rcは、環境内の関数を渡すもう1つのシェルですが、名前に「fn_」というプレフィックスが付いた変数があり、呼び出されたときにのみ解釈されます。
ステファンシャゼル14

18
@StéphaneChazelas-バグを報告してくれてありがとう。
鹿ハンター14

13
@gnclmorais実行するexport bar='() { echo "bar" ; }'; zsh -c barと、表示されるのbarではなくzsh:1: command not found: bar?呼び出しているシェルと、テストのセットアップに使用しているシェルを混同していないことを確認していますか?
ジル14

85

これは、何が起こっているかをさらに実証するのに役立ちます。

$ export dummy='() { echo "hi"; }; echo "pwned"'
$ bash
pwned
$

脆弱なシェルを実行している場合、新しいサブシェルを開始すると(ここでは単にbashステートメントを使用して)、任意のコード(echo "pwned")がその開始の一部としてすぐに実行されることがわかります。どうやら、シェルは環境変数(ダミー)に関数定義が含まれていることを認識し、その環境でその関数を定義するために定義を評価します(関数を実行していないことに注意してください: 'hi'を出力します)。

残念ながら、関数定義を評価するだけでなく、関数定義に続く悪意のある可能性のあるステートメントを含む環境変数の値のテキスト全体を評価します。初期関数定義がなければ、環境変数は評価されず、テキスト文字列として環境に追加されるだけです。Chris Downが指摘したように、これはエクスポートされたシェル関数のインポートを実装するための特定のメカニズムです。

新しいシェルで定義された関数(およびそこにエクスポート済みとしてマークされた関数)を確認し、実行することができます。さらに、ダミーはテキスト変数としてインポートされていません。

$ declare -f
dummy ()
{
    echo "hi"
}
declare -fx dummy
$ dummy
hi
$echo $dummy
$

この関数の作成も、それが実行されたとしてもそれが行うことは、エクスプロイトの一部ではありません。エクスプロイトが実行される手段のみです。重要なのは、攻撃者がエクスポートされた環境変数に入れられるテキスト文字列で、最小限の重要でない関数定義が先行する悪意のあるコードを提供できる場合、サブシェルの起動時に実行されるということです。これは一般的なイベントです多くのスクリプトで。さらに、スクリプトの権限で実行されます。


17
注意深く読んだ場合、受け入れられた答えは実際にこれを言っていますが、問題は(関数自体を実行するのではなく)定義の評価であることを理解する上で、この答えはさらに明確でより役立つことがわかりました。
natevw 14

1
export他の人が持っていたenvのに、なぜこの例には命令があるのですか?私はenv、別のbashシェルが起動したときに呼び出される環境変数を定義するために使用されていると考えていました。その後、これはどのように機能しますかexport
ハリス14

この瞬間まで、受け入れられた答えはありませんでした。私はおそらく数日待ってから受け入れます。この回答の欠点は、元のコマンドを破壊せず、質問の元のコマンドからこの回答のコマンドに到達する方法についても議論していないことです。それとは別に、それは良い説明です。
ジッピー14

@ralph- 環境定義の両方envexportサブシェルで使用できるようにエクスポートします。問題は、これらのエクスポートされた定義がサブシェルの環境にどのようにインポートされるか、具体的には関数定義をインポートするメカニズムにあります。
sdenham

1
@ralph- envいくつかのオプションと環境変数を設定してコマンドを実行します。元の質問の例でenvx、文字列に設定し、bash -c実行するコマンドで呼び出していることに注意してください。実行するとenv x='foo' vim、Vimが起動し、そこに含まれるシェル/環境を呼び出すことができ!echo $x、で印刷されfooますが、終了してecho $x、定義すると、vimの実行中にのみ存在するため、定義されませんenvコマンド経由。export代わりに、このコマンドは現在の環境に永続的な値を設定し、後で実行されるサブシェルがそれらの値を使用するようにします。
ゲイリーフィクスラー14

72

これは、上記のクリスダウンによる優れた回答のチュートリアルスタイルのリキャストとして作成しました。


bashでは、このようなシェル変数を持つことができます

$ t="hi there"
$ echo $t
hi there
$

デフォルトでは、これらの変数は子プロセスに継承されません。

$ bash
$ echo $t

$ exit

ただし、エクスポート用にマークすると、bashはサブプロセスの環境に入ることを意味するフラグを設定しenvpます(パラメーターはあまり見られませんmainが、Cプログラムには3つのパラメーターがあります:main(int argc, char *argv[], char *envp[])ポインターの最後の配列は配列です)シェル変数とその定義)。

t次のようにエクスポートしましょう。

$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit

上記tはサブシェルでは定義されていませんでしたが、エクスポート後に表示されます(export -n tエクスポートを停止する場合に使用します)。

しかし、bashの機能は別の動物です。次のように宣言します。

$ fn() { echo "test"; }

そして今、あなたはそれが別のシェルコマンドであるかのようにそれを呼び出すことによって、関数を呼び出すことができます:

$ fn
test
$

繰り返しますが、サブシェルを作成すると、関数はエクスポートされません。

$ bash
$ fn
fn: command not found
$ exit

以下を使用して関数をエクスポートできますexport -f

$ export -f fn
$ bash
$ fn
test
$ exit

トリッキーな部分は次のとおりです。fnエクスポートされた関数は、シェル変数のエクスポートtが上記のように環境変数に変換されます。これはfnローカル変数の場合は発生しませんが、エクスポート後はシェル変数として見ることができます。ただし、同じ名前の通常の(つまり、関数ではない)シェル変数を持つことできます。bashは、変数の内容に基づいて区別します。

$ echo $fn

$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$ 

これでenv、エクスポート用にマークされたすべてのシェル変数を表示するために使用でき、通常fnの関数と関数の両方がfn表示されます。

$ env
.
.
.
fn=regular
fn=() {  echo "test"
}
$

サブシェルは両方の定義を取り込みます:1つは通常の変数として、もう1つは関数として:

$ bash
$ echo $fn
regular
$ fn
test
$ exit

fn上記のように定義することも、通常の変数割り当てとして直接定義することもできます。

$ fn='() { echo "direct" ; }'

これは非常に珍しいことです!通常はfnfn() {...}構文を使用して上記のように関数を定義します。ただし、bashは環境を介してエクスポートするため、上記の通常の定義に直接「ショートカット」することができます。(おそらくあなたの直感に反して)これは、現在のシェルで利用可能な新しい関数にはならないことに注意してくださいfn。ただし、**サブ**シェルを作成すると、それができます。

関数のエクスポートをキャンセルしfn、新しいレギュラーfn(上記を参照)をそのままにしてみましょう。

$ export -nf fn

これで、関数fnはエクスポートされなくなりましたが、通常の変数fnはエクスポートされ、その中に含ま() { echo "direct" ; }れています。

これで、サブシェルがそれで始まる通常の変数を見る()と、残りを関数定義として解釈します。しかし、これは新しいシェルが始まるときだけです。上で見たように、で始まる通常のシェル変数を定義するだけで()は、関数のように振る舞うことはありません。サブシェルを起動する必要があります。

そして今、「shellshock」バグ:

先ほど見たように、新しいシェルが()それから始まる通常の変数の定義を取り込むと、それを関数として解釈します。ただし、関数を定義する閉じ中括弧の後にさらに指定がある場合、そこにあるものはすべて実行されます。

これらはもう一度要件です。

  1. 新しいbashが生成されます
  2. 環境変数が取り込まれます
  3. この環境変数は「()」で始まり、中括弧内に関数本体が含まれ、その後にコマンドが含まれます

この場合、脆弱なbashが後者のコマンドを実行します。

例:

$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$

通常のエクスポートされた変数exは、関数として解釈されるサブシェルに渡されましたが、サブシェルが生成exされると、後続のコマンドが実行されました(this is bad)。


なめらかな1行テストの説明

Shellshockの脆弱性をテストするための一般的なワンライナーは、@ jippieの質問で引用されたものです。

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

内訳は次のとおりです。最初の:in bashはの省略形ですtruetrueそして、:両方ともbashで(あなたが推測した)真に評価します:

$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$

次に、envコマンド(bashにも組み込まれています)は環境変数を出力します(上で見たように)が、そのコマンドに与えられたエクスポートされた変数(または変数)でbash -c単一のコマンドを実行し、そのコマンドから単一のコマンドを実行することもできますコマンドライン:

$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'

$ env t=exported bash -c 'echo $t'
exported
$

したがって、これらすべてをまとめて縫うと、bashをコマンドとして実行し、ダミーの操作(などbash -c echo this is a test)を与えてから始まる変数をエクスポートし()て、サブシェルがそれを関数として解釈できるようにします。shellshockが存在する場合、サブシェル内の後続コマンドもすぐに実行されます。渡す関数は私たちとは無関係です(しかし解析する必要があります!)ため、想像できる限り最短の有効な関数を使用します。

$ f() { :;}
$ f
$ 

fここの関数は:コマンドを実行するだけで、trueを返して終了します。次に、その「悪」コマンドに追加して、通常の変数をサブシェルにエクスポートすると、勝ちます。これもまたワンライナーです。

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

そのため、最後にx付加された単純で有効な関数を持つ通常の変数としてエクスポートされますecho vulnerable。これはbashに渡され、bash xは関数(これは気にしません)として解釈し、echo vulnerableshellshockが存在する場合はおそらく実行します。

this is a testメッセージを削除することで、ワンライナーを少し短くすることができます。

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

これは気になりませんthis is a testが、サイレント:コマンドを再度実行します。(そのままにしておく-c :と、サブシェルに座って手動で終了する必要があります。)おそらく最もユーザーフレンドリーなバージョンは次のようになります。

$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"

12
いい説明。この質問には多くの意見が寄せられており(おそらく誰もが他の人と同じようにbashに習熟しているわけではありません)、{ :;};実際に何を言っているのか誰もまだ話していないと思います。それは私の意見ではあなたの答えへの素晴らしい追加になるでしょう。あなたの例から質問の元のコマンドにどのように到達するかを説明できますか?
ジッピー14

20

任意の環境変数をプログラムにフィードできる場合、選択したライブラリをロードすることにより、プログラムにほぼ何でも実行させることができます。ほとんどの場合、これはそれらの環境変数を受け取るプログラムの脆弱性ではなく、部外者が任意の環境変数を入力できるメカニズムの脆弱性と見なされます。

ただし、CVE-2014-6271は異なります。

環境変数に信頼できないデータがあっても何も問題はありません。プログラムの動作を変更する可能性のあるこれらの環境変数のいずれにも置かれないようにする必要があります。特定の呼び出しに対して、もう少し抽象化して、環境変数名のホワイトリストを作成し、部外者が直接指定できるようにします。

CVE-2014-6271のコンテキストで提案されている例は、ログファイルの解析に使用されるスクリプトです。それらは、環境変数で信頼できないデータを渡す非常に正当な必要性を持っているかもしれません。もちろん、このような環境変数の名前は、悪影響を及ぼさないように選択されます。

しかし、ここにこの特定のbashの脆弱性の悪いところがあります。任意の変数名を介して悪用される可能性があります。という環境変数を作成する場合GET_REQUEST_TO_BE_PROCESSED_BY_MY_SCRIPT、独自のスクリプト以外のプログラムがその環境変数の内容を解釈することは期待できません。しかし、このbashのバグを悪用することにより、すべての環境変数が攻撃ベクトルになります。

これは環境変数の名前が秘密であると期待されることを意味しないことに注意してください。関連する環境変数の名前を知っていても、攻撃が容易になることはありません。

program1program2を呼び出す場合program3、環境変数program1program3介してデータを渡すことができます。各プログラムには、設定する環境変数の特定のリストと、実行する特定のリストがあります。に認識されない名前を選択した場合、に悪影響を与えることを心配せずにprogram2からprogram1にデータを渡すことができます。program3program2

エクスポートされる変数の正確な名前とprogram1解釈される変数の名前を知っている攻撃者program2は、名前のセット間に重複がない場合、この知識を利用して「program2」の動作を変更することはできません。

しかし、これがスクリプトprogram2だった場合bash、このバグbashが原因ですべての環境変数をコードとして解釈するため、故障しました。


1
「すべての単一の環境変数が攻撃ベクトルになります」-それは私が欠けていた部分です。ありがとう。
wrschneider 14

9

リンクした記事で説明されています...

bashシェルを呼び出す前に、特別に細工された値で環境変数を作成できます。これらの変数には、シェルが呼び出されるとすぐに実行されるコードを含めることができます。

つまり、呼び出されたbash -c "echo this is a test"は、呼び出されたときに単一引用符で囲まれたコードを実行します。

Bashには機能がありますが、実装は多少制限されていますが、これらのbash関数を環境変数に入れることができます。この欠陥は、これらの関数定義の最後に追加のコードが追加されたときにトリガーされます(環境変数内)。

投稿したコード例は、呼び出されたbashが割り当ての実行後にこの文字列の評価を停止しないという事実を利用しています。この場合の機能の割り当て。

あなたが投稿したコードスニペットについて実際に特別なことは、私が理解しているように、実行したいコードの前に関数定義を置くことにより、いくつかのセキュリティメカニズムを回避できることです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.