DNSキャッシュポイズニングの基本と重要な対策

   このエントリをはてなブックマークに登録    2014/4/25-1

2014年4月15日に公開されたJPRSの緊急注意喚起に続き、中京大学の鈴木常彦教授によるDNSキャッシュポイズニングに関する技術情報が公開されました。

今回公開された技術情報に書かれている内容には、DNSの本質につながるさまざまな要素が関係しており一回で書ききれるものではなく、また、書いている側(私)も、それぞれの要素技術について勉強しながら理解しつつ進めていかないと混乱してしまうということが良くわかったため、これから数回に分けて徐々に書いて行くことにしました。

ということで、今回はまず、そもそもDNSキャッシュポイズニングとは何かということと、JPRSの注意喚起に書かれているUDPソースポート番号のランダム化(ソースポートランダマイゼーション)の概要、そしてなぜそれが重要なのかという点について解説します。

DNSキャッシュポイズニングとは

インターネットで通信を行うとき、各機器同士は通信相手の識別子としてIPアドレスを使います。 しかし、IPアドレスは32ビット(IPv6では128ビット)の番号であり、IPアドレスをそのまま使うのでは人間がわかりにくいので、ドメイン名と呼ばれる「名前」を利用することが一般的です。 そして、ドメイン名に対応するIPアドレスを得る行為を「名前解決」と言います。

インターネット誕生当初は、機器の数も多くなかったので名前(ホスト名)が羅列されたHOSTS.TXTというファイルをみんながダウンロードして使っていたのですが、インターネットの普及とともに、そこに接続される機器が増えたこともあり、名前に階層構造を導入した「ドメイン名」と、それを実現するために開発されたDNS(Domain Name System)という仕組みが使われるようになりました。 DNSでは、ドメイン名は各ゾーンごとに分けられ、分散管理されます。

DNSには、ドメイン名とIPアドレスの対応情報を保持する権威DNSサーバ(別名、コンテンツサーバ)と、ユーザからのリクエストに応じて名前解決をする「キャッシュDNSサーバ」という2種類のサーバがあります。

一般的なユーザにとって、「DNSサーバ」は「キャッシュDNSサーバ」であると言えます。 実際、WindowsやMac OS Xなどのコンピュータや、スマホなどのモバイル機器など、様々なOSの設定パネルで、単に「DNSサーバ」と書いてある場合、その多くはキャッシュDNSサーバのことを示しています。


キャッシュDNSサーバは、名前解決のパフォーマンスを向上させ、同時に権威DNSサーバに対する負荷を軽減するため、名前解決の結果を一時的なキャッシュ(cache)として保持します。 キャッシュDNSサーバは、そのドメイン名が既にキャッシュされている場合にはその内容をそのままユーザに返し、キャッシュされていないか、あるいは有効期限が満了していた場合には、権威DNSサーバに改めて問い合わせ、その結果をユーザに返したうえで、将来に備えてその結果をキャッシュする、という作業を担当します。

このように、キャッシュDNSサーバは権威DNSサーバから得た結果をキャッシュし、名前解決を効率化します。 しかし、もし、キャッシュDNSサーバに対して、外部から何らかの方法で偽の応答をキャッシュさせることができたらどうなるでしょうか。 偽のキャッシュを持ってしまったキャッシュDNSサーバは、ユーザからの名前解決要求に対して、注入されてしまった偽の結果を返すようになってしまいます。 そして、偽の結果がキャッシュされている間にその名前を参照したすべてのユーザのアクセスが、すべて偽サイトに誘導されてしまうことになります。

こうした、キャッシュDNSサーバーに偽の応答をキャッシュさせることでユーザのアクセスを偽サイトに誘導する攻撃手法を「DNSキャッシュポイズニング」といいます。 DNSキャッシュポイズニングにはさまざまな手法が存在しますが、「偽の応答(毒)を何らかの方法でキャッシュDNSサーバに注入する」という基本コンセプトは共通しています。

DNSキャッシュポイズニングの手順

典型的なDNSキャッシュポイズニングの例を示します。

以下では、偽の応答を本物よりも先に注入するという手法が使われていますが、偽の応答をキャッシュさせる方法は他にも存在します。

  • 攻撃対象となるキャッシュDNSサーバに対して問い合わせを行う
  • 攻撃対象となるキャッシュDNSサーバが権威DNSサーバに対して問い合わせを行う
  • 権威DNSサーバの正当な応答よりも先に偽の応答をキャッシュDNSサーバに送りつける
  • キャッシュDNSサーバが、偽の応答を本物と信じ込んで受け入れたら毒入れ成功

この形のDNSキャッシュポイズニングを成功させるためには、偽のDNS応答の送信元IPアドレスを本物と一致させておく(偽装する)必要があります(*1)。

インターネットで使われているIPのうちWebなどで使われているTCPでは通信開始時に相手との接続を確認するため、送信元IPアドレスを偽装してデータを送りつけることは困難です。 しかし、DNSでは通信にかかる負荷や遅延を抑えるため、事前に接続を確認しないUDPが使われています。UDPでは、送信元IPアドレスの偽装をTCPよりも容易に行うことができます(*2)。

(*1) 問い合わせIDなど、他にも一致させる必要がある要素が存在します。
(*2) TCPを使うこともありますが、その前にUDPで問い合わせをすることになっています。RFC 1123 6.1.3.2参照。

この方式では、攻撃者は、権威DNSサーバからの応答であると偽装したデータをキャッシュDNSサーバへと送りつけます。 そのデータが本物よりも先に到着し、かつ、本物と判定された場合、キャッシュDNSサーバに対して「毒」となる偽の情報をキャッシュとして注入できるわけです。

この方式でDNSキャッシュポイズニングを行う場合、攻撃者がキャッシュDNSサーバに対して名前解決の要求を出したうえで、外部から偽の応答を適切なタイミングで送り込む必要があります。

適切に管理されているキャッシュDNSサーバでは通常、組織外からのDNS問い合わせを拒否するように設定されています(そうでない場合、つまり「オープンリゾルバ」になっている場合、キャッシュポイズニングに対しても危険な状態であると言えます。キャッシュポイズニングに対し特別な対策を行ったうえで意図的にオープンリゾルバサービスを提供している事業者も存在します。)。

しかし、オープンリゾルバでない場合もさまざまな方法で、外部から名前解決をするように仕向けることができるため、注意が必要です。

たとえば、Webページなどに特定のドメイン名を含む画像ファイルを貼付けてそのページを見るように誘導したり、名前解決が必要となる動作を行うスクリプトを仕込むことによって、そのネットワーク内にいるユーザがキャッシュDNSサーバへと問い合わせを行うように仕向けるといった方法が考えられます。

電子メールの送受信を悪用する方法も考えられます。 差出人(FromやSender)に特定のメールアドレスを設定したメールを送りつけ、メールサーバーソフトウェアにその名前に対する名前解決を促す、とった攻撃も可能です。

DNSのキャッシュとTTL

DNSキャッシュポイズニングは、その名の通り、キャッシュに偽の結果を注入するというものです。 そのため、攻撃側の視点から見ると、「どうすればキャッシュDNSサーバのキャッシュに偽の結果を注入できるのか?」が重要になります。

DNSキャッシュポイズニングを考える上で、非常に重要な要素としてTTL(Time To Live)があります。 TTLとは、そのキャッシュの有効期限を秒数で表したものです。

DNSでは、個別の情報は「リソースレコード」と呼ばれ、リソースレコードごとに、それぞれ個別のTTLを持ちます。 各リソースレコードに関する情報を取得した側は、そこに記載されたTTLまで、その情報をキャッシュすることができます。

たとえば、キャッシュDNSサーバは、権威DNSサーバから得られたリソースレコードのTTLに従って、そのリソースレコードのキャッシュを保持し続けます。 そのキャッシュを保持し続けている間は、キャッシュDNSサーバはそのキャッシュデータに対する権威DNSサーバへの問い合わせを行いません。 キャッシュDNSサーバが権威DNSサーバへの問い合わせを行わないということは、その問い合わせに対応する応答を偽装した応答(毒)の注入を行えないことを意味しています。

たとえば、www.example.comという名前に対するAレコード(IPv4アドレス)が以前に検索され、キャッシュDNSサーバにキャッシュされていたとします。 そのAレコードのTTLが24時間(86400)であれば、キャッシュDNSサーバが権威DNSサーバから正規の応答を受け取ってから24時間は、そのAレコードを検索する応答を偽造する形でのDNSキャッシュポイズニングは行えなくなります。 このように、権威DNSサーバが返した正規の応答がキャッシュされている間は同じドメイン名/リソースレコードに対する名前検索はそもそも実施されないため、その問い合わせに対する応答を偽装した形でのキャッシュポイズニングは、TTLが満了するまでできないことになります。

TTLによる保護を無力化した「カミンスキー型攻撃手法」

このように、DNSキャッシュポイズニングを防ぐためにはTTL設定が重要であり、長いTTL値を設定することでDNSキャッシュポイズニングのリスクを減らすことができると考えられていました。

しかし、2008年7月にダン・カミンスキー(Dan Kaminsky)氏によりTTLによる保護を無効化した連続攻撃が可能になることが示され、世界中の関係者の間に衝撃が走りました。

その発表が行われたのは、Black Hatという有名なセキュリティカンファレンスです。 この攻撃手法は後に、「カミンスキーアタック」や「カミンスキー型攻撃手法」と呼ばれるようになりました(本稿では「カミンスキー型攻撃手法」という表現を使います)。

カミンスキー型攻撃手法のポイントは「存在しない名前への問い合わせを攻撃に用いることでTTLによる保護を無力化し、連続攻撃を可能にした」ことにあります。

典型的なカミンスキー型攻撃手法の例を示します。

  • 攻撃したい名前の存在しないサブドメインに対する問い合わせを、標的となるキャッシュDNSサーバーに対して実行します。サブドメインの部分は毎回変更するため、ここでは($random)で表します。カミンスキー氏の発表資料では、www.example.comに対する攻撃の場合、($random)www.example.comという名前を攻撃対象としています(ただし、この資料に記載された通りの方法では毒は入りません)。
  • 存在しない名前であるため、キャッシュDNSサーバーはこの名前に対する名前解決を実施します。この場合、通常はexample.comの権威DNSサーバーに対し、名前検索が実施されることになります。
  • 攻撃者は、example.comの権威DNSサーバーからの応答であると偽装した偽の応答の注入を図ります。
  • 偽の応答の付随情報として、www.example.comの情報を乗っ取るための「毒」を仕込みます。(ここが大きなポイントです。)
  • この場合、example.comの正当な権威DNSサーバは「その名前は存在しない(NXDOMAIN)」という応答を返します。もし、この応答が偽の応答よりもキャッシュDNSサーバに先に到達した場合、攻撃は失敗であり、その攻撃に使った($random)ではしばらくの間、攻撃できなくなります(DNSでは、名前が存在しなかったという検索結果もキャッシュされます)。
  • しかし、攻撃者は($random)の部分を変更することにより、事実上無限に攻撃を継続できます。

このように、カミンスキー型攻撃手法では攻撃に用いる名前をランダムに変化させることにより、TTLによる保護を無力化できることになります。

「一度攻撃した後、しばらくの間再攻撃できない」という意味で、TTLによる保護は火縄銃に例えることができます。カミンスキーは火縄銃の時代に、機関銃の作り方、つまり、TTLによる保護を無効にした「効率の良い武器」を提示したとも言えます。

カミンスキー型攻撃手法では、上記のように、問い合わせる名前に対する毒を直接入れるわけではなく、問い合わせに付随する情報として毒を入れることになります。

つまり、カミンスキー型攻撃手法による攻撃では「どのような毒をどう盛るのか」ということが重要なポイントとなります。この部分は今回の話における重要なポイントの一つですが、今回はカミンスキー型攻撃手法の本質(存在しない名前を用いることでTTLによる保護を無効化する)の紹介にとどめ、今後、別記事として徐々に紐解いていくことにします。

UDPポート番号のランダム化

さて、DNSキャッシュポイズニングが大きな話題となった今、現段階において考えられる緩和策としてどういったものがあるのかを知ることが大事な方々が多いと思います。 社内で調査結果等を求められる、などがあるかも知れません。 特に、キャッシュDNSサーバーを業務で運用している人にとっては、「じゃ、私はどうすればいいの?」「どのくらい危険なの?」ということを知ることは、死活問題であるとも言えるでしょう。

DNSキャッシュポイズニング対策を考える場合にまず重要となるのは、偽の応答を注入されにくくするための対策です。偽の応答を注入されなければ、キャッシュポイズニングはそもそも成立しません。

JPRSが出している緊急注意喚起に書かれているUDPポート番号のランダム化も、キャッシュDNSサーバに毒が入りにくくする方法のひとつです。

JPRSの発表によれば、JP DNSサーバにアクセスがあったIPアドレスのうち、約10%が未だに固定ポート番号で運用されているとあります。 これらのキャッシュDNSサーバは、数秒から数分でDNSキャッシュポイズニングが可能であり、非常に危険な状態です。 まずはそれらを減らすことが重要であるということで「緊急注意喚起」を公開した、ということのようです。

DNSキャッシュポイズニングは、キャッシュDNSサーバが偽パケットを正規の回答であると誤認してしまうことが原因です。 この誤認が成立するためには、以下の要素が重要となります。

  • キャッシュDNSサーバが偽パケットを受け取るタイミング。キャッシュDNSサーバが権威DNSサーバへの問い合わせを行い、権威DNSサーバから正規の回答を受け取る前に偽パケットを注入しなければならない。
  • キャッシュDNSサーバからの問い合わせに対応した回答であること。たとえば、キャッシュDNSサーバがwww.example.comに対するAレコードの問い合わせを行っているときに、別の名前であるmail.example.comや別の型(たとえばIPv6アドレスを示すAAAAレコード)の回答を偽造しても駄目。
  • キャッシュDNSサーバからの問い合わせメッセージと同じTXID(トランザクション識別子)であること。TXIDフィールドは16ビットなので、ランダム化されていれば、偽パケットがたまたま同じ値になる確率は65536分の1となる(TXIDは16ビットしかないため、TXIDのみが異なる65536個の応答を同時に送るという、総当たり攻撃が可能になります)
  • キャッシュDNSサーバから送信されたUDPパケットと同じポート番号であること。権威DNSサーバのUDPポート番号は53番になるので常に固定される。キャッシュDNSサーバの送信元UDPポートがランダム化されていれば、偽パケットが注入される確率が65535分の1になる。

これらの要素からわかるように、TXIDとUDPポート番号が両方ともランダム化されていれば、偽パケットが正規のものであると誤認される確率は約43億分の1となります。 現時点では、非常に限られた時間内に、この確率を突破するのは困難です(確率の問題ですので、長時間の攻撃により突破することは理論的には可能です。ただし、そうした攻撃を仕掛けた場合、攻撃が露見する可能性が高くなります。)。

しかし、2008年にカミンスキー型攻撃手法が発表されたとき、UDPポート番号が固定された状態での運用が非常に多く行われていました。 そのため、2008年にUDPポート番号をランダム化するという緊急対策が各所で急がれました。

固定UDPポート番号を利用するキャッシュDNSサーバの発見方法

UDPポート番号のランダム化は、本来であれば、カミンスキー型攻撃手法が発表された2008年時点において行われている必要があった対策です。

現時点において、それを行っていない10%のキャッシュDNSサーバを運用する担当者は、普段からあまりそうしたセキュリティ上の対策に気を配っていない可能性があります。 恐らく、ユーザがそういったキャッシュDNSサーバを発見したうえで、管理者に設定変更を依頼する必要がありそうです(自宅ホームゲートウェイがそういった設定になっている場合も有り得ます)。

UDPポート番号が固定状態もしくは限定されたUDPポート番号のみの脆弱なキャッシュDNSサーバを自分が使っているかどうかは、DNS-OARCのDNS Radomness Testを試すことで確認が可能です。

Webを使ってテストを行いたくない方は、digコマンドでも確認が可能です。 DNS-OARCのporttest.dns-oarc.netのTXTレコードを確認すると、その中に結果が記述されます(参考)。



dig +short porttest.dns-oarc.net TXT


さらに、digに対して@の引数を使うことで、指定したキャッシュDNSサーバをテストすることも可能です(ただし、指定したキャッシュDNSサーバーが、そのIPアドレスからの再帰検索要求を受け付けている場合のみ。)。



dig @ns.example.com +short porttest.dns-oarc.net TXT


これらのテストを行い、POORまたはGOODという結果が出た場合、そのキャッシュDNSサーバは毒入れ攻撃に対して脆弱であると言えます(現状を考えた場合、GOODという結果が出ても十分であるとは言えません)。

そういった結果が出た場合、焦って下さい。そして、固定になってしまっているUDPポート番号をランダム化するように自分で設定するか、それとも設定が可能な人に何らかの対策を依頼しましょう。 凄く危険な状態でインターネットを使っています。

最後に

UDPポート番号ランダム化以外にも、様々な緩和策があります。 それらに関しても、そのうち書きたいと思います。

先日のDNSキャッシュポイズニングの話は様々な話の複合体であり、それらが全て同時に公開されているので全体としては非常に複雑になっています。 その後、どうやって記事にするのかをもう一度考え直したのですが、複数記事に分けて、徐々に書いて行くしかないという結論に達しました。

徐々に時間をかけて、書いていきたいと思います(原稿〆切があるので、ちょっと間は空きそうですが)。

   このエントリをはてなブックマークに登録