連載
» 2013年02月19日 20時20分 UPDATE

もいちど知りたい、セキュアコーディングの基本(3):Cでポピュラーな脆弱性とバッファオーバーフロー(後編) (1/2)

前回の説明に続き、今回はISC DHCPのソースコードをサンプルにして、スタックバッファオーバーフローが生じる仕組みと修正方法について説明します。

[戸田洋三(JPCERT/CC) ,@IT]

 今回は、過去に発見され修正されたバッファオーバーフローの実例を見ていきます。前回はバッファオーバーフローのうち、主に「スタックバッファオーバーフロー」について説明したので、ここで取り上げる実例もスタックバッファオーバーフローが発生するものを選びました。

ネットワークを支える「DHCP」という仕組み

 今回はISC DHCPというソフトウェアの脆弱性を取り上げます。まず、その動作を理解する上で必要となるDHCPの知識を簡単に説明します。

 DHCPは、ネットワークに接続するホストのIPアドレスをはじめとするネットワーク設定を自動的に行うための仕組みです。ネットワーク設定には、IPアドレス、サブネットマスク、デフォルトルータ、ネームサーバなどの情報が必要になりますが、これらの情報は、あらかじめネットワーク上に設置したサーバ(DHCPサーバ)で管理します。ネットワークに接続するホスト(DHCPクライアント)が、情報提供を求めるリクエストメッセージをネットワーク上にブロードキャストすると、DHCPサーバは必要な情報を含むメッセージを送り返し、これを拾ったDHCPクライアントは、メッセージに含まれている情報を使ってネットワーク設定を行います。

図1 DHCPによるIPアドレス割り当てのフロー 図1 DHCPによるIPアドレス割り当てのフロー(参考:http://en.wikipedia.org/wiki/File:DHCP_session_en-2.svg)

 DHCPサーバとDHCPクライアントの間でやりとりされるメッセージのフォーマットは、以下の図2、図3のようになっています(RFC2131、RFC2132を参照)。

図2 DHCP メッセージのフォーマット 図2 DHCP メッセージのフォーマット(かっこ内の数字はバイト数)

 図2のoptionsの部分には、IPアドレス以外のさまざまな情報が、図3のフォーマットで表現され収められます。

図3 オプション部分のフォーマット 図3 オプション部分のフォーマット

【関連記事】

ヘルスチェックしてる? 怠ってはならないDNSのケア

http://www.atmarkit.co.jp/fsecurity/rensai/corenet01/corenet01.html


 このDHCPサーバやDHCPクライアントの動作を実装したプログラムとして有名なのが「ISC DHCP」です。DHCPサーバとDHCPクライアントの両方が含まれており、多くのUNIXシステムがISC DHCPを使っています。

 それでは、ISC DHCPのDHCPクライアントプログラム(dhclient)に発見されたスタックバッファオーバーフローについて見てみましょう。

ISC DHCP dhclientのバッファオーバーフロー

 ここで取り上げる脆弱性には「CVE-2009-0692」という識別番号が付けられています。開発元であるISCが公開しているアドバイザリでは、次のように説明しています。

"While generating a subnet number from the server-supplied leased address and subnet-mask 'dhclient' copies the information into a field without verifying if the length of the information exceeds the length of the field."

(出典:https://www.isc.org/software/dhcp/advisories/cve-2009-0692


 これを訳すと、おおよそ以下の内容になります。

dhclientは、サーバから受け取ったIPアドレスとサブネットマスクからサブネット情報を生成するとともに、ローカル変数にコピーする。そのとき、コピーする情報のサイズがローカル変数のサイズを超えるかどうかの検証を行っていない。


 ここで問題になるのは、サブネットマスク情報をローカル変数にコピーする部分です。DHCPメッセージ中のサブネットマスク情報は、図3のオプション部分のフォーマットに従って、以下のように表現されています。

図4 subnet maskオプションのフォーマット(RFC2132、§3.3) 図4 subnet maskオプションのフォーマット(RFC2132、§3.3)

 オプション部分について記載しているRFC2132では、データサイズを表す「Lenフィールド」の値は「定数4」と明記されています。IPv4アドレスのバイト長ですね。

 では、サブネット情報の扱いで問題となった部分のコードを以下に示します。ここでは、受け取ったDHCPメッセージの中からサブネットマスク情報を取り出し(lookup_option()およびevaluate_option_cache())、そのデータをローカル変数netmaskのiabufフィールドに、memcpy関数を使ってコピーしています。

void script_write_params (client, prefix, lease)
	struct client_state *client;
	const char *prefix;
	struct client_lease *lease;
{
	int i;
	struct data_string data;
	struct option_cache *oc;
	struct envadd_state es;
	......
	memset (&data, 0, sizeof data);
	oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK);
	if (oc && evaluate_option_cache (&data, (struct packet *)0,
					 (struct lease *)0, client,
					 (struct option_state *)0,
					 lease -> options,
					 &global_scope, oc, MDL)) {
		if (data.len > 3) {
			struct iaddr netmask, subnet, broadcast;
			memcpy (netmask.iabuf, data.data, data.len);
			netmask.len = data.len;
			data_string_forget (&data, MDL);
			......
dhcp-4.1.0/client/dhclient.cより

 コピーするデータサイズはDHCPメッセージに記述されていた値(data.len)になっています。IPv4ではサブネットマスク情報のサイズは4バイトですから、通常ならば、data.lenの値は4となるはずです。

 では、コピー先である構造体、struct iaddrの定義を見てみましょう。

/* An internet address of up to 128 bits. */
struct iaddr {
	unsigned len;
	unsigned char iabuf [16];
};
dhcp-4.1.0/includes/inet.hより

 メンバフィールドiabufの宣言を見ると、16バイト用意してあるようです。これはおそらくIPv6対応を考慮したものでしょう。

 ここで、もしネットマスク情報のサイズ(図4のLenフィールドの値)が4より大きいとどうなるでしょうか? memcpy関数ではネットマスク情報のサイズをそのまま使ってコピーしています。DHCPメッセージのフォーマットとしてはLenフィールドは1バイト幅なので256まで表現できますが、コピー先であるiabufのサイズは16バイトしかありません。もし16を超える値が渡されると、バッファオーバーフローが発生します。

 ローカル変数がコードに現れる順番でスタックに積まれるとして、スタックに積まれる各ローカル変数のサイズを調べると、バッファオーバーフローによってどのようなデータが上書きされるか分かります。問題のコードでは、

  • int型のi
  • data_string構造体のdata
  • option_cache構造体へのポインタoc
  • envadd_state構造体のes

がスタックに積まれているはずです。その先には、関数呼び出し時に退避されたレジスタの値と関数実行後の戻りアドレスがあります(前回の図1を参照)。

図5 スタックの様子 図5 スタックの様子

 戻りアドレスが上書きされると、script_write_params関数の実行が終了した後に呼び出し元に「戻る」代わりに、上書きされた値をメモリアドレスとする場所に「戻って」いきます

 つまり、攻撃者の観点では、あらかじめ実行させたいコードをメモリ上に置き、そのメモリアドレスがちょうど図5の戻りアドレスの部分に当てはまるようにバッファオーバーフローを発生させれば、指定したコードを実行できるわけです。これが、バッファオーバーフローによって任意のコードが実行可能になる仕組みです。

 もう一点重要なことは、dhclient(を実行しているプロセス)がroot権限で動作しているということです。攻撃者がバッファーオーバーフローを悪用してdhclientプロセスに実行させるコードも、当然root権限で実行されることになります。UNIXシステムにおいてroot権限を持っているということは、あらゆる操作が可能であるということであり、そのホストは乗っ取られてしまうと考えてよいでしょう。

 なお、DHCPサーバになりすましてDHCPクライアントにパケットを送り、今回紹介した脆弱性、CVE-2009-0692を悪用してバックドアを仕込み、root権限でログインする……という一連の攻撃を紹介する動画がYouTubeにアップロードされています。興味ある方はタイトル“Exploiting dhclient flaw CVE-2009-0692”で検索してみてください。

       1|2 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。