これは、上記のクリスダウンによる優れた回答のチュートリアルスタイルのリキャストとして作成しました。
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" ; }'
これは非常に珍しいことです!通常はfn
、fn() {...}
構文を使用して上記のように関数を定義します。ただし、bashは環境を介してエクスポートするため、上記の通常の定義に直接「ショートカット」することができます。(おそらくあなたの直感に反して)これは、現在のシェルで利用可能な新しい関数にはならないことに注意してくださいfn
。ただし、**サブ**シェルを作成すると、それができます。
関数のエクスポートをキャンセルしfn
、新しいレギュラーfn
(上記を参照)をそのままにしてみましょう。
$ export -nf fn
これで、関数fn
はエクスポートされなくなりましたが、通常の変数fn
はエクスポートされ、その中に含ま() { echo "direct" ; }
れています。
これで、サブシェルがそれで始まる通常の変数を見る()
と、残りを関数定義として解釈します。しかし、これは新しいシェルが始まるときだけです。上で見たように、で始まる通常のシェル変数を定義するだけで()
は、関数のように振る舞うことはありません。サブシェルを起動する必要があります。
そして今、「shellshock」バグ:
先ほど見たように、新しいシェルが()
それから始まる通常の変数の定義を取り込むと、それを関数として解釈します。ただし、関数を定義する閉じ中括弧の後にさらに指定がある場合、そこにあるものはすべて実行されます。
これらはもう一度要件です。
- 新しいbashが生成されます
- 環境変数が取り込まれます
- この環境変数は「()」で始まり、中括弧内に関数本体が含まれ、その後にコマンドが含まれます
この場合、脆弱な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はの省略形ですtrue
。 true
そして、:
両方とも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 vulnerable
shellshockが存在する場合はおそらく実行します。
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"