バッファ・オーバーフローに起因するセキュリティ・ホールは後を絶たない。バッファ・オーバーフローの脅威は,攻撃者に任意のコードを実行されてしまうことだ。セキュリティ・ホールがあるプログラムへのパッチ適用などが第一の対策ではあるが,ユーザー側でバッファ・オーバーフローの発生を抑制して,その影響を最小限にとどめる方法もある。OSカーネルのオプションやライブラリ,コンパイラの変更である。今回のコラムではそれらを紹介する。

バッファ・オーバーフローはなぜ危険か

 ここ最近,Apache/mod-sslのセキュリティ・ホールを利用して伝播する「slapper」ワームが出回り始めている。このワームに関しては9月17日にJPCERT/CCからも警告が発せられている(関連記事)。このワームは,OpenSSLに存在するバッファ・オーバーフローのセキュリティ・ホールを悪用する。

 バッファ・オーバーフローに起因するセキュリティ・ホールは枚挙にいとまがない。例えば,最近では以下のセキュリティ・ホールが見つかっている。

 バッファ・オーバーフローを悪用した攻撃が危険なのは,攻撃者が意図する命令をぜい弱性があるマシン上で実行されてしまうからである。

 バッファとは,プログラムが処理を行う際に使用する,一時的にデータをためるためのメモリー上の領域のことである。通常は問題がないものの,プログラムで指定したサイズ以上のデータが入力された場合などには,バッファ領域が“あふれて”しまう。これがバッファ・オーバーフローである。あふれたデータはバッファ領域以外に上書きされる。そのため,プログラムを暴走させられたり,任意のコードを実行されたりすることになる(詳細は日経Linuxの「Linux Q & A」などを参照のこと)。

 バッファに書き込む前に,プログラムがデータのサイズをきちんとチェックしていればバッファ・オーバーフローは防げる。しかし実際には,適切なチェックを行っていないソフトウエアが多数あるために,次から次へとバッファ・オーバーフローに起因するセキュリティ・ホールが見つかるのである。

バッファ・オーバーフローの影響を最小限に

 では,次々見つかるバッファ・オーバーフローに,システム管理者はどのように対処すればよいのだろうか。本コラムで繰り返し述べているように,ベンダーからのパッチを入手し,適用するのが第一である。

 しかし,すぐにパッチが提供されない場合があるし,提供されてもシステム環境によってはすぐに適用できない場合もあるだろう。そのような場合には,ぜい弱性があるプログラムに手を加えるのではなく,プログラムが実行されるプラットフォーム側でバッファ・オーバーフローの発生を抑制し,影響を最小限にする方法が望ましい。実際にそのような方法が存在するので,そのいくつかを紹介する。

カーネル・オプションを変更する

 いうまでもなく,プログラムはOS上で動作する。そこで,OSレベルでバッファ・オーバーフローを抑制することはできないだろうか。実は,Solaris 2.6以降のSPARC版では,カーネルのオプションを変更することで,バッファ・オーバーフローを利用する攻撃をある程度防ぐことができる。

 具体的には,設定ファイル「/etc/system」に以下の2行を追加し,再起動するだけでよい。

---------------------------------------------------
set noexec_user_stack=1
set noexec_user_stack_log=1
---------------------------------------------------

 上記の1行目でスタック上の実行権限を外し,2行目ではsyslogへログを出力する設定にしている。1行目の設定で,少なくともバッファ内のスタック領域をあふれさせる攻撃によるコードの実行を防げる。

libsafeを利用する

 カーネル・オプションの変更で対処できるOSおよびハードウエアは限られている。対処できないOSを使っている場合にはどうすればよいか。考えられる方法の一つが,プログラムが必ず利用するライブラリで対処する方法である。それも,libcのような基本的なライブラリに,バッファ・オーバーフローを抑制する機能を加えて対処するのである。実際,そのようなライブラリが公開されている。「libsafe」だ。

 libsafeを使えば,C言語標準(ANSI-C)のライブラリに含まれる標準関数を呼び出す際に引き起こされるバッファ・オーバーフローを抑えられる。具体的には,libsafeはライブラリの呼び出し順序を変更し,バッファ・オーバーフローをチェックするコードを読み込ませた後に,本来呼び出されるライブラリの内容を呼び出す。

 libsafeの最新版はlibsafe-2.0-16である。ソース・ファイルからインストールしてもよいが,RPMも用意されているので,Linuxユーザーはそちらを利用したほうが便利だろう。

 libsafeを使用するには,各プログラムが既存の共有ライブラリよりもlibsafeを先に読み込むようにシステムの設定を変更する必要がある。ここでは環境変数 LD_PRELOADを使用して読み込み順序を変更する方法を紹介する。

 具体的には,/etc/rc.d/init.d/functions ファイルの先頭に,環境変数 LD_PRELOAD を次のように追加する。

---------------------------------------------------
export LD_PRELOAD=/lib/libsafe.so.2
---------------------------------------------------

 加えて,/etc/profile ファイルの先頭にも以下の1行を追加する。

---------------------------------------------------
export LD_PRELOAD=/lib/libsafe.so.2
---------------------------------------------------

 ただし,リンカーが必ず読みに行くディレクトリにlibsafe.so.2 を配置する必要がある。多くの場合は /lib である。なお,libsafe をデフォルトのままインストールすると, /lib 以下にコピーされる。

libsafeを実際に試す

 次に,libsafeがバッファ・オーバーフローを抑止するかどうか,実際に試してみた。テストには,次のようなプログラムを使用した(プログラム名はインストール前で“test”,後で“test2”とする)。見てお分かりのように,実行すれば必ずバッファ・オーバフローするプログラムである。

◆テストに使用したソース
---------------------------------------------------
#include<stdio.h>
main ()
{
  char str[4];
  strcpy (str, "long string");
  printf ("%s\n", str);
}
---------------------------------------------------

 libsafeインストール前と後の実行結果を示す。

◆libsafeインストール前の実行結果
---------------------------------------------------
$ ./test
Segmentation fault (core dumped)
---------------------------------------------------
◆libsafeインストール後の実行結果
---------------------------------------------------
$ ./test2
Libsafe version 2.0.16
Detected an attempt to write across stack boundary.
Terminating /home/sns/libsafe-test/test2.
    uid=500  euid=500  pid=812
Call stack:
    0x40016a91  /lib/libsafe.so.2.0.16
    0x40016b9a  /lib/libsafe.so.2.0.16
    0x8048407   /home/sns/libsafe-test/test2
    0x400369c6  /lib/libc-2.1.3.so
Overflow caused by strcpy()
Killed
---------------------------------------------------

 インストール前には,プログラムがオーバーフローした後,Segmentation faultが発生している。もし今回のテスト・プログラムの入力値(long string)にコードが仕込まれていた場合には,そのコードはテスト・プログラムの実行権限で実行される恐れがある。

 しかし,インストール後は,バッファ・オーバーフローが発生した時点でプログラムが終了している。つまり,プログラムが提供するサービスが停止することはあっても,バッファ・オーバーフローしたデータに含まれるコードが,そのまま実行されてしまうことはない。

プログラムにオーバーフロー検知機能を加える

 OSやライブラリ以外に,コンパイラを変更することでバッファ・オーバーフローを抑制する方法もある。この場合には,プラットフォーム側でバッファ・オーバーフローを抑えるのではなく,それぞれのプログラムにバッファ・オーバーフローを検知する機能を追加する。そのようなコンパイラの一例が「StackGuard」である。

 StackGuardはGNU C Compiler(gcc)の拡張版である。StackGuard付きでコンパイルされたプログラムは,実行時,リターン・アドレスの後に「カナリア」と呼ばれる確認用のコードを付加する。もし,関数が戻された際にこのコードが変更されていた場合にはバッファ・オーバーフローが発生したと判断し,プログラムを停止させるようになっている。なお,コンパイル時にオプションを付ければ,通常のgccとして使用できる。

 同様のコンパイラとして,「StackShield」がある。こちらもgccの拡張版である。StackGuardとの違いは,確認用コード「カナリア」の保護方法である。対象プラットフォームはIntel386アーキテクチャのLinuxである。

ただし万全ではない

 以上のような方法で,バッファ・オーバーフローの発生をある程度防止できる。しかし,いずれもバッファ内のスタック領域のオーバーフローには対応できるものの,ヒープ領域のオーバーフローについては対応できない(詳細については割愛する)。

 また,バッファ・オーバーフローを防ぐコードを挿入する方法では,プログラム実行時のパフォーマンスが低下する。さらに,スタック領域のオーバーフローに対しても,今回紹介した方法で100%防げるわけではない。パッチを適用する必要がなくなるわけでは決してない。あくまでも,パッチを適用するまでの急場しのぎの一つと考えていただきたい。

 加えて,今回紹介したソフトウエアには,過去にセキュリティ・ホールが見つかっている。ソフトウエアである以上,仕方がないことではあるが,セキュリティ・ホール情報には注意したい。もちろんこのことは,今回紹介したソフトウエアに限った話ではない。


阿部正道 (ABE Masamichi) masamichi.abe@lac.co.jp
株式会社ラック セキュアネットサービス事業本部


 IT Proセキュリティ・サイトが提供する「今週のSecurity Check [一般編]」は,その週に起きたUNIX関連およびセキュリティ全般のニュースや動向をまとめた週刊コラムです。セキュリティ・ベンダーである「株式会社ラック」のスタッフの方を執筆陣に迎え,専門家の立場から解説していただきます。