CVE-2012-0207 Linux カーネルの IGMP 処理における脆弱性について

この脆弱性は2012年1月6日頃に Debian へのバグレポートを発端として発見されました。当初は無線 LAN 関連のバグとして疑われていましたが、調査の結果 IGMP の処理に起因するバグであると判明しました。このバグを利用してリモートからカーネルパニックを誘発する攻撃が可能であるため、脆弱性としての扱いになりました。マルチキャストの処理に起因する脆弱性ですが、ユニキャストによる攻撃も可能であり、ローカルネットワーク以外からも攻撃が成立します。

該当する Debian のバグレポートは以下です。
CVE-2012-0207: divide error and panic when receiving mixed IGMP queries

また、以下のサイトで詳しく説明されています。
http://womble.decadent.org.uk/blog/igmp-denial-of-service-in-linux-cve-2012-0207.html

この脆弱性は最近の修正にて作り込まれたもので、2.6.36 以降のバージョンが対象となっています。Linux の各ディストリビューションで使用しているカーネルは、部分的に新しい機能をバックポートして取り込んでいる場合が多く、このバージョンに満たなくとも影響を受ける可能性があります。

例として、以下の表は国内において広く使われている CentOS の検証結果です。該当の機能がバックポートされるまでのリリースバージョンは影響を受けていません。

リリースバージョンカーネルバージョン影響
CentOS 5.52.6.18-194.el5
CentOS 5.62.6.18-238.el5
CentOS 5.72.6.18-274.el5
CentOS 6.02.6.32-71.el6
CentOS 6.12.6.32-131.0.15.el6
CentOS 6.22.6.32-220.el6

影響を受けるバージョンであったとしても無条件で対象となる訳ではなく、以下の両方を満たしている場合のみ影響を受けます。該当する例も合わせて記載します。

  • 攻撃を受けるインターフェイスにてマルチキャストを有効にしている
  • ip route add 224.0.0.0/4 dev eth0
  • ローカルネットワーク(224.0.0.1)以外のマルチキャストグループに参加している
  • NTPでマルチキャストクライアントを使用 (multicastclient 224.0.1.1)

攻撃を受けるとカーネルパニックを起こし、以下の様なバックトレースが出力されます。

divide error: 0000 [#1] CPU: 0 EIP: 0060:[<c06033a2>] Not tainted VLI EFLAGS: 00010246 (2.6.18-274.el5 #1) EIP is at igmp_start_timer+0x1c/0x36 eax: 07edd344 ebx: 00000000 ecx: 43496975 edx: 00000000 esi: cf3f58e0 edi: cf3f58c0 ebp: fffd873f esp: c074fe7c ds: 007b es: 007b ss: 0068 Process swapper (pid: 0, ti=c074f000 task=c068f3c0 task.ti=c070a000) Stack: cf3f58c0 cfd202c0 cf3f58e0 cf3f5900 c060546f ce38d080 00000000 00000000 c13ca63c c0436d18 00000000 c13ca620 ce38d080 c06c3d98 00000000 cf312000 c05e07c5 ce38d080 00000000 ce38d080 c13ca620 c05e0630 ce38d080 c0857588 Call Trace: [<c060546f>] igmp_rcv+0x45a/0x560 [<c0436d18>] __wake_up_bit+0x29/0x2e [<c05e07c5>] ip_local_deliver+0x15b/0x206 [<c05e0630>] ip_rcv+0x47f/0x4b9 [<c05c5102>] netif_receive_skb+0x3dd/0x401 ...
Code language: plaintext (plaintext)

問題となっているカーネルソースコードの該当箇所は以下です。ローカルネットワーク(224.0.0.1)を除外している処理もこの部分にあります。

=== net/ipv4/igmp.c static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb, int len) { ... } else if (IGMP_V2_SEEN(in_dev)) { /* this is a v3 query with v2 queriers present; * Interpretation of the max_delay code is problematic here. * A real v2 host would use ih_code directly, while v3 has a * different encoding. We use the v3 encoding as more likely * to be intended in a v3 query. */ // この箇所で0がセットされる max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE); } else { /* v3 */ ... rcu_read_lock(); // パケットの入ってきたインターフェイスに紐付くマルチキャストグループをチェック for_each_pmc_rcu(in_dev, im) { int changed; // マルチキャストグループのアドレスに一致しないパケットは除外 // 0.0.0.0の場合もここのチェックを通過 if (group && group != im->multiaddr) continue; // ローカルネットワーク(224.0.0.1)のグループは常に除外される // #define IGMP_ALL_HOSTS htonl(0xE0000001L) if (im->multiaddr == IGMP_ALL_HOSTS) continue; spin_lock_bh(&im->lock); if (im->tm_running) im->gsquery = im->gsquery && mark; else im->gsquery = mark; changed = !im->gsquery || igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs); spin_unlock_bh(&im->lock); if (changed) // この先でigmp_start_timerが呼ばれる igmp_mod_timer(im, max_delay); } rcu_read_unlock(); } ... static void igmp_start_timer(struct ip_mc_list *im, int max_delay) { // この部分で0除算が発生しカーネルパニック int tv = net_random() % max_delay; im->tm_running = 1; if (!mod_timer(&im->timer, jiffies+tv+2)) atomic_inc(&im->refcnt); }
Code language: C++ (cpp)

ローカルネットワーク以外のマルチキャストを使用しているという前提条件により、影響を受けるシステムは限られます。該当する場合は数パケットでシステムの停止が引き起こされてしまうので、脆弱性の無いバージョンへの変更や回避策の適用をお勧めします。

サーバにおいて適用可能な回避策として iptables によるパケットフィルタリングが挙げられます。u32 モジュールが使用可能な CentOS6 系などでは、攻撃に使用される該当のパターンのみを拒否可能です。以下はそのパターンを拒否する設定例です。

> iptables -A INPUT -p igmp -m u32 --u32 "0>>22&0x3C@0>>16=0x1100" -j DROP
Code language: Shell Session (shell)

u32 モジュールが使用不可能な場合は、IP アドレス等を条件にフィルタリングするしかありませんが IP スプーフィングされた状態でも攻撃が成立してしまうため、単体ではあまり有効な対策にはなりません。この場合はルータやファイアーウォール等によるフィルタリングと併せて対策を取る必要があります。

シェアする