ELF Statifierを使って実行形式ファイルをシステム間で移動する

 共有ライブラリを動的にリンクすると、静的にリンクする場合よりもディスクを効率的に使用できる。それ以上に重要なのは、セキュリティのアップデートを簡単に実施できることだ。しかし、特定のバージョンの動的ライブラリを対象としてコンパイルされた実行形式ファイルは、そのバージョンの共有ライブラリが存在するコンピュータでしか実行できない。Fedora 9が稼働するコンピュータとopenSUSE 11が稼働するコンピュータでは、共有ライブラリのバージョンが微妙に違っていても不思議はない。このようなコンピュータ間で実行形式ファイルをコピーして実行すると、共有ライブラリのバージョンが異なるため正常に動作しないことがある。このような場合は、 ELF Statifier を使って静的リンク版の実行形式ファイルを作成すると、実行時に共有ライブラリを探す代わりに、組み込まれた共有ライブラリが使用される。静的リンク版の実行形式ファイルは、別のLinuxディストリビューションや同じLinuxディストリビューションの別のバージョンでもずっと高い確率で動作する。

 実行形式ファイルに共有ライブラリを静的にリンクすれば、当然その分ディスクの消費量は増える。しかし、最近はテラバイトを単位とする大容量のディスクさえあるので、ディスク容量の懸念はセキュリティの懸念ほど大きくない。実行形式ファイルが、ある共有ライブラリ ─ 仮にlibfooとでも呼んでおくが ─ と動的にリンクされている場合にlibfooのセキュリティ・アップデートが行われたとしよう。アプリケーションが動的にリンクされるとlibfooの共有コピーが更新されるため、古いlibfooにあったセキュリティの問題によってアプリケーションが脅かされることはなくなる。静的にリンクされた実行形式ファイルではこうはいかない。古いlibfooのプライベートなコピーが組み込まれたまま、それを使い続けることになる。新しいlibfooとセキュリティ・アップデートを取り入れるには、静的にリンクされた実行形式ファイルを再作成する必要がある。

 このような事情はあるにせよ、Fedoraマシンでコンパイルしたデーモンを、デーモン本体と依存ファイルを再コンパイルしないで、そのままopenSUSEマシンで実行したいこともあるだろう。とりあえず実行してみたい、再ビルドは必要なら後でもできる、そういった状況だ。もちろん、実行形式ファイルをコピーしてくるコンピュータと、このファイルを実行するコンピュータは、同じアーキテクチャでなければならない。

 ELF Statifierは、1-ClickパッケージがopenSUSE 10.3向けに用意されているが、Ubuntu HardyやFedoraには用意されていない。今回は、ELF Statifieのバージョン1.6.14をFedora 9マシン(x86)でソースからビルドした。ELF Statifieではautotoolsを使わないため、makeを呼び出してコンパイルする。次のコマンドでコンパイルとインストールを行う。

$ tar xzvf statifier-1.6.14.tar.gz
$ cd ./statifier-*
$ make
$ sudo make install

 ELF Statifieを実際に試すサンプルとして、lsバイナリの静的リンク版を以下のコマンドで作成してみよう。最初に動的リンク版の実行形式ファイルからコピーを作成し、何が動的にリンクされるのかを確認する。次にstatifierコマンドを実行するが、このときに1つ目の引数として動的リンク版の実行形式ファイルへのパスを指定し、2つ目の引数として静的リンク版を作成する場所へのパスを指定する。lddコマンドの実行結果を見ると、ls-staticで動的リンクライブラリが必要とされないことがわかる。その次のコマンドは、静的バージョンのlsではバイナリのサイズが大幅に増えていることを示している。

$ mkdir  test
$ cd ./test
$ cp -a /bin/ls ls-dynamic
$ ls -lh
-rwxr-xr-x 1 ben ben 112K 2008-08-01 04:05 ls-dynamic
$ ldd ls-dynamic
	linux-gate.so.1 =>  (0x00110000)
	librt.so.1 => /lib/librt.so.1 (0x00a3a000)
	libselinux.so.1 => /lib/libselinux.so.1 (0x00a06000)
	libacl.so.1 => /lib/libacl.so.1 (0x00d8a000)
	libc.so.6 => /lib/libc.so.6 (0x0084e000)
	libpthread.so.0 => /lib/libpthread.so.0 (0x009eb000)
	/lib/ld-linux.so.2 (0x0082e000)
	libdl.so.2 => /lib/libdl.so.2 (0x009e4000)
	libattr.so.1 => /lib/libattr.so.1 (0x0606d000)

$ statifier ls-dynamic  ls-static
$ ldd ls-static
	not a dynamic executable

$ ls -lh ls-static
-rwxr-x--- 1 ben ben 2.0M 2008-10-03 12:05 ls-static

$ ls-static /tmp
...
$ ls-static -lh
Segmentation fault

 ご覧のとおり、静的リンク版のlsは-lオプションを付けて実行するとクラッシュする。このセグメントエラーが起きる場合は、スタックのランダム化を無効にし、静的リンク版を再作成する必要がある。Linuxカーネルが提供するスタックとアドレス空間のランダム化機能は、スタックや実行形式ファイルのその他の重要な部分を格納する領域を実行のたびに変更するというものだ。バイナリを実行するたびにこういった領域がランダム化されることでlibc関数の場所が変わるため、return-to-libcなどの攻撃が阻止される。

 randomize_va_spaceパラメータを以下のコマンドで変更すると、セキュリティのレベルがいくぶん低下する。実行形式ファイルへの攻撃だけでなく、バッファ・オーバーフローを利用してシステムに侵入する攻撃手口に対しても脆弱になる。アドレス空間がランダム化されないと、これらの攻撃はいずれも容易になるのだ。randomize_va_spaceパラメータを以下のように0に設定してからls-staticバイナリを再作成すると、コマンドは正常に動作する。静的リンク版を実行するには、スタック・ランダム化の機能は常に無効にしておく必要がある。

# cd /proc/sys/kernel
# cat randomize_va_space
2
# echo -n 0 >| randomize_va_space
# cat randomize_va_space
0

 これ以外にも、statifierには若干の裏ワザがある。実行形式ファイルに対して環境変数を設定(または設定解除)することや、他のライブラリ(LD_PRELOADライブラリ)を実行形式ファイルに組み込むことができる。実行形式ファイルに対して他の環境変数を設定できることは、静的リンク版を作成したいバイナリが設定ファイルなどの他のリソースを見つける必要がある場合に便利だ。このようなバイナリにリソースが見つかる場所を環境変数を通じて通知できれば、そういった設定を静的リンク版の実行形式ファイルに直接組み込むことができる。

 プリロード型の共有ライブラリを静的リンク版のバイナリに組み込む機能(LD_PRELOADing)は、おそらくあまり使われない機能だろう。1つの用途としては、静的にリンクされた実行形式ファイルをデフォルトで”ゴミ箱フレンドリ”にする(おそらくdelsafeを使用して)ことが挙げられる。この場合、同じコンピュータにソフトウェアを追加でインストールする必要はない。

 バイナリのアドレス空間のランダム化によるセキュリティ対策は、ELF Statifierの実行にとって妨げとなる。しかし、実行形式ファイルを別のLinuxマシンでも実行したいだけなら、ELF Statifierを使うことで面倒な再コンパイルを避けられる。

Ben Martin 10年以上にわたってファイルシステムを研究。博士課程を修了し、現在、libferris、ファイルシステム、検索ソリューションを中心にコンサルティングをしている。

Linux.com 原文(2008年10月23日)