現在悪名高い、例のbashのバグCVE-2014-6271 は、後に「ShellShock」として知られるようになった。このバグはコードのリモート実行を許可してしまうもので、直接的または間接的にbashスクリプトを実行しているサーバに対し、巧妙に作成されたデータをネットワーク越しに送信することで起こる。最初のバグは修正されたが、後続の、解析ルーチンに関するゼロデイの懸念は2つ目の脆弱性CVE-2014-7169をもたらした。こちらの脆弱性は公開されてから週末にかけて修正された。しかし、この脆弱性はなぜ起こったのだろうか。また、この手のバグはこれが最後となるのだろうか。FreeBSDやNetBSDは、関数を自動的にインポートする機能をデフォルトで無効にした。将来の脆弱性を防ぐためだ。
問題が発生する理由は、Bashシェルにとある機能( バグではなく? )があり、それによって環境変数で関数を定義できるからである。よく呼び出されるコードを関数として定義しておけば、それ以降の場所で再利用できるようになるという仕組み自体は、どのシェルスクリプト言語でも提供されている。bash(および他のほとんどのシェル)の関数は、次のように定義することができる:
function hello { echo "Hello" } hello # 関数呼び出し
しかし、bashには環境変数を使用して関数を定義する手段もあり、これはbashに特有のものだ。環境変数の値が (){
で始まる場合、暗黙的にインポートされた関数定義として扱われ、シェルを起動するだけで有効になる。
$ export HELLO="() { echo 'Hello'; }" $ HELLO -bash: HELLO: command not found $ bash $ HELLO Hello
シェルを起動するだけで有効になることから、脆弱性の実証例はほとんどがこのようなワンライナーだった。
env HELLO="() { echo 'Hello'; }" bash -c HELLO
そしてこれは、上の例と同じ処理を実行する。( env
という命令が意味することは、環境に依存しない形で「この環境変数が設定された状態で次のプログラムを実行する」だ。実際に、 HELLO="() { echo 'Hello'; }" bash -c HELLO
と全く同じように動作する。)この(-cで指定された)ワンライナーのbashコマンドは正しく実行されるべきものなので、関数は起動時に一度だけ解析され、新しいシェルの中に存在するようにしておく必要がある。
コード解析の中にあった最初の欠陥は、関数定義の後ろのコードを実行していたことだ。そのため、
env CVE_2014_6271="() { echo 'Hello';}; echo 'Goodbye'" bash -c CVE_2014_6271
というコードは、修正済みのシステムでは「Hello」が出力されるだけだが、脆弱なシステム上では「Goodbye」も同様に出力される。問題は、関数の自動インポートを行うパーサが関数定義の終端を踏み越えて、続くコードを実行し続けたことだ -- そして新しいbashシェルが起動するたびにこれが起きるため、任意のコードを実行させることができてしまう。
この問題が大きなものになるのには2つの理由がある。第一に、bashは広くインストールされたソフトウェアで、あらゆる場所で実行されていること。Rapsberry Piや携帯電話から、データセンターやメインフレーム内のサーバーに至るまでだ。関数の自動インポート機能は少なくともbash 3.0以来存在していたので、およそ20年ほど前から、ほとんどのシステムにこのバグは存在している。第二に、Apacheサーバを動かしているときに (mod_php
や mod_python
ではなく) mod_cgi
でスクリプトを実行していた場合、データが環境変数を介して渡される。これはインターネットの最初期にさかのぼる技術である。また、変更を処理するためにいろんな所でbashスクリプトを使っているクライアントは他にも存在する -- LinuxのDHCPクライアントがその例だ。そのため、不正なデータを含むDHCPパケットも同様に悪用可能である。
これが意味するのは、bashがほとんどのLinux(およびOSX)システムでのデフォルトのシェルであることと相まって、CGIスクリプトを実行しているサーバーに対して毒された環境変数を渡すことで、とても簡単に悪用できてしまうということである。たとえば、HTTPヘッダーUser-Agent
は、環境変数HTTP_USER_AGENT
として渡されるのが普通である。したがって、このようなシンプルなコードを書くだけで攻撃できてしまう。
curl -A "() {:;}; echo 'Game Over'}" http://example.com/some-cgi/script.cgi
ユーザーエージェントを渡さないサーバだったとしても、しばしば他の手段が存在する。たとえばCookieや、またはリクエストメソッド自体などである。
このバグはCGIスクリプトおよびApacheだけに影響するわけではないことに気を付けてほしい--同様の方法で変数が毒され、他のプログラムに対して値がそのまま渡される(例えばssh
であれば、TERM
やDISPLAY
のような変数)場合、そしてそれらのプロセスがbashスクリプトを実行している(または、system()
を呼び出して暗黙的に実行している)場合は、同じ脆弱性が悪用される可能性がある。HTTPとは異なり、起動できるようにするにはログインが要求されるので、要求を匿名で行うことはできないが、(制限されたシェルであっても)自由にログオンできるようになっているコードホスティングプロバイダでは、GitHubは企業向け製品にアップデートを提供し 、Bitbucketはサーバーを更新してすべての侵入可能性を修正した。
上記のバグについてはCVE-2014-6271で修正されたことで直近の問題は解決され、ほとんどのベンダーは修正ビルドを解禁日以前に利用可能にした。インターネットに直接接続されたサーバーがこれを直さないでおく理由はない。ホストしているマシンを(Apacheサーバを実行するユーザーとして)完全に制御することができてしまうからだ。
しかし、この領域に注目が集まったことで、新しいバグが発見された。bashのシェルリダイレクトと関数の自動インポートを組み合わせることで、CVE-2014-7169が発生した。リモートマシン上のファイルの読み書きが可能になってしまうものだ。前と同じ技術を使ってはいるが、今回はシェルのリダイレクト文字「<」や「>」も使用する。
env CVE_2014_7169='() { (a)=>\' bash -c "echo date"; cat echo
今回はパーサは=記号で止まる(bashでは式(a)=
には意味がない)が、重大なのは、パイプラインにリダイレクト文字 「>」 が残ってしまうことだ。エスケープ文字「\」が合わせて使われることで、リダイレクト文字との間にいくつかの(無視できる)空白が生じる。これによって、後続の命令がリダイレクションに変換されるようになる。
>echo date
これはbashとして有効であり、下記のような、より一般的な形式と意味的に等価である。
date >echo
他のリダイレクト演算子も有効になっていることに気を付けること。たとえば、「<」でファイルを消費することが可能である。
このバグも同様に修正されたが、bashの関数自動インポート機能のエッジケースを修正するのがもぐら叩きゲームとなっているのではないかと疑問視されている。あるバグが潰されても、他の場所に別のバグが出てくる。このバグが発生する前にも、bashの自動インポート機能を使用すれば、完全修飾されていないシステム標準の実行可能ファイルを上書きすることができることに対する懸念があった:
$ env ls="() { echo 'Game over'; }" bash -c ls Game over $ env ls="() { echo 'Game over'; }" bash -c /bin/ls Applications Desktop Documents Downloads ...
(最近のパッチよりも以前には、env /bin/ls="() { echo 'Game over'; }" bash -c /bin/ls
で完全修飾パスを上書きすることが可能であったが、これは完全にパッチを適用したバージョンで今日までに修正された。)
しかし、多くの実行ファイルは、パスをエンコードされた形では持っていない。これはすなわち、適切な名前の変数を使えば、できてはいけないことができてしまうことを意味する。
$ touch /tmp/a /tmp/b $ env test='() { echo vulnerable >&2; }' /usr/bin/bzdiff /tmp/a /tmp/b vulnerable bzip2: Can't open input file a.bz2: No such file or directory.
もちろん、任意の環境変数を設定できるのであれば、攻撃者は数多くの物事を制御できる -- 環境変数 IFS
を変えたり(これは攻撃手法として過去に使用されている)、あるいはPATH
であっても新たに起動するシェルスクリプトの動作に影響するし、こういった手法はいずれにせよ何らかの問題を引き起こす可能性がある。少なくとも、上に書いたSSHとCGIの両方への攻撃では、そのまま渡される変数は、限定的な集合(TERM
や DISPLAY
など)であるか、またはプレフィックスが適用されている(HTTP_USER_AGENT
や HTTP_REFERRER
)のどちらかだ。したがって、そのような名前を持つコマンドが存在しない限り、攻撃の範囲ははるかに限られている。
それでも、bashの関数自動インポートに関して、これが最後のセキュリティ脆弱性ではないだろうと想像し、将来的にバグが発見されることを懸念している人もいる。NetBSDはデフォルトでbashの関数自動インポートを無効にしている。FreeBSDも同様に、新たに追加された--import-functions
オプション付きで呼び出されたときは、オプトイン関数として自動インポートを提供することを選択した。これは技術的には後方互換性のない破壊的な変更だが、この件についてはパラノイアにも合理性があり、そのようなオプションがアップストリームで利用可能になるまで長い時間はかからないかもしれない。OSXユーザーのためのパッチもある。
InfoQは進展を監視し、変更があれば報告する。
9月29日 追記:一連のパッチの最新版で上記の欠陥が修正されている。この問題を解決するリリースがAppleを除くすべてのベンダーから提供された。