Develop and Download Open Source Software

OpenSource Downloads

7-Zip  (4,208)  
HandBrake Japanese Language Version  (3,353)  
CrystalDiskInfo  (1,743)  
CotEditor  (1,120)  
CrystalDiskMark  (866)  
Boookends  (788)  
SMPlayer  (642)  
えこでこツール  (599)  
Tera Term  (595)  
10  FFFTP  (579)  
11  Cabos  (530)  
12  BathyScaphe  (494)  
13  ffdshow  (481)  
14  MergeDoc  (464)  
15  ギコナビ  (438)  
More >>

最近ブックマークされた記事

'Exec Shield' ── Linuxの新たなセキュリティ機構

2003年05月06日 13:09 Ingo-Molnar(2003年5月2日(金))
このたび、Linux/x86向けにカーネル・ベースの新たな セキュリティ機構「exec-shield」のソース・コードを初めて公開 する運びとなった。対象となるカーネルのバージョンは2.4.21-rc1で、 GPL/OSLのライセンスのもとで配布される。 http://redhat.com/~mingo/exec-shield/からパッチをダウンロードしていただきたい。

さて、exec-shieldはスタック、バッファ、関数ポインタのオー バフロー防止機構を提供することにより、データ構造への上書きや コードの書き込みを利用した攻撃に対抗するものである。本パッチ を適用すると、いわゆる「シェルコードを利用した攻撃」も難しく なる。本パッチは透過的に動作するのでアプリケーシ ョンの再コンパイルは必要ない。

背景:

よく知られた事実だが、x86のページテーブル・エントリでは、 いわゆる実行可能ビットがサポートされていない。PROT_EXEC PROT_READが独立しておらず、読み取り 実行の制御を1つのフラグで兼用しているのだ。つまり、 アプリケーション・レベルで(メモリマップの際にPROT_EXEC フラグを立てないようにして)メモリ領域を実行可能でないと マークしても、x86ではPROT_READフラグが立っていると その領域を実行できてしまうのである。

さらに、x86のELF ABIがプロセス・スタックを実行可能とマーク するため、ページテーブルで実行可能ビットをサポートするCPUでも スタックを実行可能とマークせざるをえないだ。

この問題と取り組むために、これまでもさまざまなカーネル・パッチ が考案されてきた。Solar Designerの「non-exec stack patch」は そうしたパッチの代表格である。これらのパッチは概ねx86の セグメント・アドレッシング機能を利用して、コード・セグメントの 限界点(リミット)をスタック・フレーム直下の固定アドレスに設定 するようなしくみになっている。exec-shieldは、このコード・セグ メント・リミットで、スタックだけでなく仮想メモリもできるだけ 広くカバーしようとするものである。

実装:

exec-shield機構はカーネル側からトランスペアレントに働き、 アプリケーションの実行可能マップを常に監視して「最大実行可能 アドレス」を適切に維持する。このアドレスを「execリミット」 と呼ぶ。スケジューラはexecリミットを使用してコンテキスト・ スイッチングのたびにコード・セグメント・デスクリプタを更新 する。システムの各プロセス(あるいはスレッド)のexecリミット は必ずしも同じでないので、スケジューラはユーザ・コード・セグ メントを動的に設定して常に適切なコード・セグメント・リミット が使われるようにする。カーネルはユーザ・セグメント・デスクリ プタの値をキャッシュするため、コンテキスト・スイッチングの パスで生じるオーバーヘッドはごくわずかで、GDTに無条件に6バ イト書き込んでも、せいぜい2、3サイクル消費するにすぎない。 また、カーネルは、いわゆるASCII-armor領域(x86では0〜16MBの アドレス)にすべてのPROT_EXECマップを再マップする。 これらのアドレスが特徴的なのは、ASCIIベースのオーバフローを 利用してもそこにジャンプできないことだ。たとえば、次のような 長いURLが入力されるとオーバフローするバグを持つアプリケーションが あったとする。

http://somehost/buggy.app?realyloooooooooooooooooooong.123489719875

この場合、攻撃者が使うのはASCII文字(値1〜255)だけである。 そこで、すべての実行可能アドレスがASCII-armor領域にあれば、 URL攻撃を利用して実行可能コードへジャンプすることは不可能となる。 つまり、攻撃が成功することはない(URL文字列中に\0を含めること ができないからだ)。つい最近あった、sendmailの脆弱性を狙った remote root攻撃もASCIIベースのオーバフローを利用したものであった。

exec-shieldが有効な状態で、ASCII-armor領域に再リンクされた 「cat」バイナリを実行すると、次のメモリ・レイアウトが得られる。

  $ ./cat-lowaddr /proc/self/maps
  00101000-00116000 r-xp 00000000 03:01 319365     /lib/ld-2.3.2.so
  00116000-00117000 rw-p 00014000 03:01 319365     /lib/ld-2.3.2.so
  00117000-0024a000 r-xp 00000000 03:01 319439     /lib/libc-2.3.2.so
  0024a000-0024e000 rw-p 00132000 03:01 319439     /lib/libc-2.3.2.so
  0024e000-00250000 rw-p 00000000 00:00 0
  01000000-01004000 r-xp 00000000 16:01 2036120    /home/mingo/cat-lowaddr
  01004000-01005000 rw-p 00003000 16:01 2036120    /home/mingo/cat-lowaddr
  01005000-01006000 rw-p 00000000 00:00 0
  40000000-40001000 rw-p 00000000 00:00 0
  40001000-40201000 r--p 00000000 03:01 464809     locale-archive
  40201000-40207000 r--p 00915000 03:01 464809     locale-archive
  40207000-40234000 r--p 0091f000 03:01 464809     locale-archive
  40234000-40235000 r--p 00955000 03:01 464809     locale-archive
  bfffe000-c0000000 rw-p fffff000 00:00 0

このレイアウトで、最上位の実行可能アドレスは0x01003fff である。つまり、実行可能アドレスはすべてASCII-armor領域 に含まれる。

これは、スタックだけでなく、mmap()した領域の多くとmalloc()で確保 したヒープ領域も実行可能でなくなることを意味する(一部のデータ領域 は依然として実行可能だが、ほとんどの領域はそうでない)。

ASCII-armor領域の最初の1MBはNULLポインタの間接参照対策のために 手付かずで残されいるため、XFree86などが16ビット・エミュレーション のマッピングに使用できる。

exec-shieldが有効でないときのメモリ・レイアウトと比べてみよう。

  
  08048000-0804b000 r-xp 00000000 16:01 3367       /bin/cat
  0804b000-0804c000 rw-p 00003000 16:01 3367       /bin/cat
  0804c000-0804e000 rwxp 00000000 00:00 0
  40000000-40012000 r-xp 00000000 16:01 3759       /lib/ld-2.2.5.so
  40012000-40013000 rw-p 00011000 16:01 3759       /lib/ld-2.2.5.so
  40013000-40014000 rw-p 00000000 00:00 0
  40018000-40129000 r-xp 00000000 16:01 4058       /lib/libc-2.2.5.so
  40129000-4012f000 rw-p 00111000 16:01 4058       /lib/libc-2.2.5.so
  4012f000-40133000 rw-p 00000000 00:00 0
  bffff000-c0000000 rwxp 00000000 00:00 0

このレイアウトでは、実行可能な領域はASCII-armor領域内に 存在せず、しかもexecリミットは0xbfffffff(3GB)である。 つまり、ユーザ空間のすべてのマッピングが含まれることになる。

なお、カーネルは共有ライブラリをいちいちASCII-armor領域に 再配置するが、そのバイナリ・アドレスはリンク時に決定される。 ASCII-armor領域にアプリケーションを再リンクする手間を省くた めにArjan Van de Venが作成したパッチ (binutils-2.13.90.0.18-elf-small.patch)があり、 このパッチで追加されたldの新しいフラグ、 "ld -melf_i386_small"(あるいは "gcc -Wl,-melf_i386_small")によってASCII-armor 領域への再リンクが行われる(このパッチもexec-shieldと同じ URLから入手できる)。

オーバーヘッド:

本パッチは、効率を第一に考えた。PROT_MMAP システム・コールごとに監視のためのごくわずかのオーバーヘッド (2サイクル)が生じるほかは、コンテキスト・スイッチングごとに 2、3サイクル消費するだけである。

制限:

この機構は、あらゆるタイプの攻撃に対応するものではない。

たとえば、オーバーフローを利用してローカル変数が上書きされ た場合、その影響で制御の流れが変わって障害が生じることはあり 得る。しかし、スタック内のリターン・アドレスやヒープ内の関数 ポインタを狙った純粋なオーバーフロー攻撃はすべて阻止できると 思う。また、exec-shieldはシェルコードの実行もほぼ抑えるので、 これ以外の攻撃もかなり難しくなる。

だが、オーバーフローがexec-shield自体で発生した場合は (つまり、ASCII-armor領域内のいずれかの共有ライブラリ・オブ ジェクトのデータ・セクションで発生すると)まだ攻撃される 余地がある。

exec-shieldは攻撃を抑える防壁の1つであって、それだけで100% 完璧な保護を与えるものではない。セキュリティを確保するには、 何重にも対策を講じることが重要なのだ。

付け入る隙をできるだけ与えないように、exec-shieldコードは トランポリンに頼らないことにした。トランポリンを利用した execリミットの侵犯が起こる余地はまずない。gccのトランポリンを 前提とするアプリケーションはバイナリ単位のELFフラグを使用して スタック・コードを再度makeする必要がある(このELFフラグは Solar Designerのnon-exec stack patchで使われているものと同じで、 既存のnon-exec-stack処理系との互換性に配慮した)。

exec-shield機構は、x86のPROT_READによる実行許可 を前提とした変則的なアプリケーションをあぶりだしてくれる。 その1つの例が、XFree86モジュール・ローダだ。この問題は rawhide.redhat.comの最新のXFree86では解決されている。XFree86の バグフィックスをすぐインストールできない人のために、本パッチでは 次の回避オプションを用意した。

    echo 1 > /proc/sys/kernel/X-workaround

これで、アプリケーション(たとえば、X)を使用するiopl() ごとにexec-shieldが無効になる。他のアプリケーション(sendmailなど) ではexec-shieldは依然として有効である。この回避オプションはデフォルト でオフになっている。この問題を解決するには、Xをアップグレードするか、 「chkstk」ユーティリティを使ってXのスタックを強制的に実行可能にする ことを強くお勧めする。

使用方法:

exec-shield-2.4.21-rc1-B6カーネル・パッチを2.4.21-rc1カーネル に適用し、再コンパイル後、カーネルをインストールして再起動すれば 終わりである。

起動時のカーネル・コマンド・ライン・オプションとして新たに exec-shield=が設けられた。これはセキュリティ・レベルに応じて 次の4つの値を取る。

   exec-shield=0    - 常に無効
   exec-shield=1    - 明示的に有効にしたバイナリ以外はデフォルトで無効
   exec-shield=2    - 明示的に無効にしたバイナリ以外はデフォルトで有効
   exec-shield=3    - 常に有効

現在のパッチはexec-shield=2がデフォルトである。次のように して/procに値を書き込めば、実行時にセキュリティ・レベルを変更 することもできる。

   echo 0 > /proc/sys/kernel/exec-shield

重要:セキュリティに関係のアプリケーションがexec-shieldが無効な 間に開始された場合は、実行可能なスタックを持つことになるので、 exec-shieldを再び有効にするときそれらを再起動する必要がある。

Solar Designerのchstk.cコードの修正版もアップロードした。 これにはELFフラグ「enable non-exec stack」の変更に必要が次のオプション が設けられている。

  $ ./chstk
  使用方法: ./chstk OPTION FILE...
  バイナリのスタック領域の実行可能フラグを管理する

    -e    実行パーミッションをオンにする
    -E    非実行パーミッションをオンにする
    -d    実行パーミッションをオフにする
    -D    非実行パーミッションをオフにする
    -v    現在のフラグの状態を表示する

つまり、2つの明確に区別されたフラグが存在する。1つは実行可能 なスタックを強制するもので、もう1つは実行可能でないスタックを 強制するものだ。両方のフラグがオフの場合はシステムのデフォルト が使われる。

したがって、exec-shieldのセキュリティ・レベルを1にしておき、 バイナリごとにnon-execスタックを有効にするようなことが可能である。 具体的には、起動時にexec-shield=1を指定し、各バイナリに対して 個別に次のコマンドを実行する。

   ./chstk -E /usr/sbin/sendmail

(本番の環境をexec-shieldカーネルに移行する場合は、 この方法を取るのがよいだろう。)

ご意見、提案、評価をお聞かせ願いたい。

Ingo

関連トピック

最終更新:2007年07月01日 19:05