前回の説明に続き、今回はISC DHCPのソースコードをサンプルにして、スタックバッファオーバーフローが生じる仕組みと修正方法について説明します。
今回は、過去に発見され修正されたバッファオーバーフローの実例を見ていきます。前回はバッファオーバーフローのうち、主に「スタックバッファオーバーフロー」について説明したので、ここで取り上げる実例もスタックバッファオーバーフローが発生するものを選びました。
今回はISC DHCPというソフトウェアの脆弱性を取り上げます。まず、その動作を理解する上で必要となるDHCPの知識を簡単に説明します。
DHCPは、ネットワークに接続するホストのIPアドレスをはじめとするネットワーク設定を自動的に行うための仕組みです。ネットワーク設定には、IPアドレス、サブネットマスク、デフォルトルータ、ネームサーバなどの情報が必要になりますが、これらの情報は、あらかじめネットワーク上に設置したサーバ(DHCPサーバ)で管理します。ネットワークに接続するホスト(DHCPクライアント)が、情報提供を求めるリクエストメッセージをネットワーク上にブロードキャストすると、DHCPサーバは必要な情報を含むメッセージを送り返し、これを拾ったDHCPクライアントは、メッセージに含まれている情報を使ってネットワーク設定を行います。
DHCPサーバとDHCPクライアントの間でやりとりされるメッセージのフォーマットは、以下の図2、図3のようになっています(RFC2131、RFC2132を参照)。
図2のoptionsの部分には、IPアドレス以外のさまざまな情報が、図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)に発見されたスタックバッファオーバーフローについて見てみましょう。
ここで取り上げる脆弱性には「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のオプション部分のフォーマットに従って、以下のように表現されています。
オプション部分について記載している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メッセージに記述されていた値(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]; };
メンバフィールドiabufの宣言を見ると、16バイト用意してあるようです。これはおそらくIPv6対応を考慮したものでしょう。
ここで、もしネットマスク情報のサイズ(図4のLenフィールドの値)が4より大きいとどうなるでしょうか? memcpy関数ではネットマスク情報のサイズをそのまま使ってコピーしています。DHCPメッセージのフォーマットとしてはLenフィールドは1バイト幅なので256まで表現できますが、コピー先であるiabufのサイズは16バイトしかありません。もし16を超える値が渡されると、バッファオーバーフローが発生します。
ローカル変数がコードに現れる順番でスタックに積まれるとして、スタックに積まれる各ローカル変数のサイズを調べると、バッファオーバーフローによってどのようなデータが上書きされるか分かります。問題のコードでは、
がスタックに積まれているはずです。その先には、関数呼び出し時に退避されたレジスタの値と関数実行後の戻りアドレスがあります(前回の図1を参照)。
戻りアドレスが上書きされると、script_write_params関数の実行が終了した後に呼び出し元に「戻る」代わりに、上書きされた値をメモリアドレスとする場所に「戻って」いきます。
つまり、攻撃者の観点では、あらかじめ実行させたいコードをメモリ上に置き、そのメモリアドレスがちょうど図5の戻りアドレスの部分に当てはまるようにバッファオーバーフローを発生させれば、指定したコードを実行できるわけです。これが、バッファオーバーフローによって任意のコードが実行可能になる仕組みです。
もう一点重要なことは、dhclient(を実行しているプロセス)がroot権限で動作しているということです。攻撃者がバッファーオーバーフローを悪用してdhclientプロセスに実行させるコードも、当然root権限で実行されることになります。UNIXシステムにおいてroot権限を持っているということは、あらゆる操作が可能であるということであり、そのホストは乗っ取られてしまうと考えてよいでしょう。
なお、DHCPサーバになりすましてDHCPクライアントにパケットを送り、今回紹介した脆弱性、CVE-2009-0692を悪用してバックドアを仕込み、root権限でログインする……という一連の攻撃を紹介する動画がYouTubeにアップロードされています。興味ある方はタイトル“Exploiting dhclient flaw CVE-2009-0692”で検索してみてください。
Copyright © ITmedia, Inc. All Rights Reserved.