コンパイラに仕込まれた細工とシステムのセキュリティの話

コンパイラのソースには書いていないのにバイナリだけで代々伝わっていく情報というのがあって、それはコンピュータのセキュリティに大きく関わっている。ここではそれについて書いてみよう。

僕は8ccというCコンパイラをスクラッチから書いたことがあるのだけど、8ccには文字列を読む部分で、"\"の後に"n"がきたら"\n"という文字(改行文字)を読んだことにするという箇所がある。これはよく考えてみれば自己言及的になっていて、ソースコードの中に"\n"のASCIIコードが一体本当は何なのかという情報が含まれていない。しかしコンパイラをコンパイルするコンパイラからその情報が受け継がれるので、できたバイナリは改行文字をきちんと出力できる。つまり8ccの改行文字は何度セルフコンパイルしても最初に使ったGCC起源ということになる。

コンパイラは、改行文字の文字コードというレベルではなく、もっと大きな情報をバイナリだけで受け継いでいくこともできる。

この話はUnixのオリジナル開発者のKen Thompsonがチューリング賞を受賞したときに暴露して世の中をちょっと驚かせたらしい。Ken Thompsonが初期のUnixのコンパイラに埋め込んだ情報は、loginプログラムをコンパイルしているときに、あるパスワードを勝手に受け付けるようにする、というコードだった。これによってKen Thompsonは誰のUnixアカウントにもログインすることができた。さらに彼は、コンパイラをコンパイルしているときにはloginに細工をするコードとコンパイラに細工するコードを埋め込むようにしたので、コンパイラのソースコードには存在しないバックドアが、何度セルフコンパイルしても受け継がれるようになっていた。

この手のバックドアは発見がかなり困難だ。単純なコンパイラバックドアなら逆アセンブラを使って注意深くバイナリを調べてみれば発見することができる。しかしそれに対する対抗策として、逆アセンブラをコンパイルしているのを検出して、悪意のあるコードだけを最初から表示しないようにするといった妨害が考えられる。他にもOSをコンパイルするときに細工を加えて、正常なコンパイラをロードするときにバイナリの一部分だけを書き換えて悪意のあるものにするといったこともできる。

システマチックな対抗策としては複数のコンパイラから出発するという方法がある。最初のコンパイラが違うバイナリであっても、ソースからビルドしたコンパイラを使ってそれ自身をもう一度コンパイルすれば、通常は最終的な実行ファイルはまったく同じになるはずだ。同じにならないとしたらどちらかのコンパイラにバックドアが仕込まれていた可能性がある。とはいえ両方のコンパイラを汚染しておくことも不可能ではないので、結局のところ、脅威を軽減することはできても、信頼できないシステムから出発して100%信頼できるシステムを構築するのは理論的にはできない。

これがどれくらい現実的な脅威なのか、というと、lldリンカのようなOS標準のビルドツールを作っている僕にもよくわからない。ある名前の関数を別のコードで置き換えてしまう、といったバックドアを仕込むのは簡単だろうけど、デバッガや逆アセンブラを使う人がいるだろうから、すぐにバレてしまいそうな気もする。それ以前にOSのコードを書く人ではなくビルドをする人になるのもちょっと大変そうだ。とはいえ大々的にやるのではなくターゲットを絞って攻撃すれば、なかなか発覚しなさそうな気もする。

僕の知る限りこの手の攻撃が真剣に行われたことは近年例がないはずなので、試されていない、というのがおそらく正直なところだろう。外部からの攻撃と違って、この手のバックドアを仕掛けるためには自分の築いてきた信頼を裏切る必要があるので、試す人はまずいない。とはいえ誰かが実行する可能性は常にあるし、それに対する対抗策を誰かが構築していることを願うばかりだ。

この記事が気に入ったらサポートをしてみませんか?