仙豆のレシピ

ちょっとしたことでも書いていく姿勢で

新たなDNSキャッシュポイズニング手法、第一フラグメント便乗攻撃について

以前IPv6 summitに参加したときに聞いたお話の中にあった、昨年(2013年)8月に発表された新たなDNSキャッシュポイズニング手法「第一フラグメント便乗攻撃」について興味がわいたのでいろいろ調べたり実際にできるのかを試したりしてみました。(もちろん自分の管理するクローズドな環境で)

第一フラグメント便乗攻撃をグーグルで検索しても個人のブログ等が出てこないのでこれが第一「第一フラグメント便乗攻撃」便乗エントリです(別にうまくない)

攻撃コード等ありますが、けっして自分の管理する環境以外に使用しないでください

第一フラグメント便乗攻撃とは

第一フラグメント便乗攻撃とは、DNS応答がフラグメントされた場合DNS応答の同定に使える要素(UDPポート番号、問い合わせID、問い合わせ名)が一つ目のフラグメントにしかないことを利用し、二つ目のフラグメントを偽装することによって毒入れをするというDNSキャッシュポイズニングの手法です。顔だけアイドルで体などを別の人のものと入れ替えた画像である「アイコラ」と似たようなイメージですね。説明は以下のサイトが詳しいです。

(参考)
IPフラグメントによるDNSへの脅威 - Yukarin'Note
https://yukar.in/note/ckF8ji
IP fragmentation attack on DNS
https://ripe67.ripe.net/presentations/240-ipfragattack.pdf
JPRS_SAPPORO2014.pdf
https://www.iajapan.org/ipv6/summit/SAPPORO2014/pdf/JPRS_SAPPORO2014.pdf

攻撃の勘所

この攻撃を成功させるためにはとにかくDNS応答をフラグメントさせなくてはなりません。そのための方法は①大きなDNS応答を返すサーバを狙う、②ICMPtooBigを事前に送っておく、などがありますが、今回は②の方法を選択しました。すなわち攻撃の概要は以下の図のようになります。
f:id:senz:20140323205035p:plain
https://ripe67.ripe.net/presentations/240-ipfragattack.pdfより引用)

攻撃を実際にやってみた

STAP細胞の件でも言われていたように理論を別な人が追試することが理系として大事かと思ったので実際にこの攻撃をやってみました。(というのは大義名分でただおもしろそうだからやってみただけ)しかし「IPフラグメント?なにそれ?」状態のレベルから始まったこの発想によって地獄を見るのでした…w

環境

f:id:senz:20140323213403p:plain
上図のような環境で、キャッシュサーバ(10.0.4.5)が攻撃対象です。またサーバは全てubuntu上のBIND9です。.kmbの権威サーバ(10.0.3.5)が利用する権威サーバで、ここに.sonya.kmbドメインのリクエストを送ると.sonya.kmbのネームサーバ(10こある)を返すため応答が大きくなるので、それを利用します。

攻撃の手順

  1. 偽装した第二フラグメント(毒入り)を攻撃対象のキャッシュサーバに送りつける
  2. .kmbの権威サーバに偽装したICMPtooBig(ICMP type3 code4)を送りMTUを552に指定
  3. キャッシュサーバに "とてもとても長い文字列.sonya.kmb" を問い合わせる
  4. キャッシュサーバはルートサーバから再帰的に問い合わせを行う
  5. 権威サーバからの応答の第一フラグメントがキャッシュサーバに届く
  6. 1で送った偽装した第二フラグメントと5の第一フラグメントで応答が再構成される(毒入れ成功)

手順0:情報収集

ルータでMTUを552にしぼり、権威サーバからのフラグメントされた応答がどの部分で切れている(分かれている)のかを調べました。この情報を元に次の手順に進みます。実際に攻撃するとなるとルータをいじったりすることは当然できませんので、先に手順2のICMPtooBigの偽装をすることになるでしょう。

手順1:偽装第二フラグメントを送る

キャッシュサーバに偽装した第二フラグメントを送るのですが、難関はただひとつ、IPヘッダのIdentificationフィールドです。しかしこのフィールドは16bitしかないので総当りするプログラムを組んで65535個の第二フラグメントを送ります。プログラムはpythonで組みました(うんコード)この偽装第二フラグメントの、再構成されたあとaddtional recordになる部分に任意の毒を仕込みます。

手順2:偽装ICMPtooBIGを送る

こちらもpythonで組んだプログラムで実行。MTUを552にしてもらいます。(うんコード)このときICMPtooBig自体のIPヘッダの送信元アドレスは攻撃者のものでいいですが、パケットのデータ部分に入れるIPヘッダの送信元アドレスは攻撃対象のものに偽装しなくてはならないところに注意が必要です。結果は以下のようになりました。

f:id:senz:20140323231115p:plain
①通常のDNS問い合わせ・応答
②偽装したICMPtooBig
③フラグメントされたDNS応答

手順3:長いDNS問い合わせをする

DNS応答には問い合わせ内容がそのまま入るので、長い文字列を問い合わせることでDNS応答のサイズを大きくします。また、DNS応答が大きくなるとUDPではなくTCPが使われてしまうので、digのオプションに+bufsize=4096を指定してEDNS0によって大きな応答もUDPを使うようにします。

dig killmebabykillmebabykillmebabykillmebabykillmebabykillmebaby.killmebabykillmebabykillmebabykillmebabykillmebabykillmebaby.killmebabykillmebabykillmebabykillmebabykillmebabykillmebaby.killmebabykillmebabykillmebabykillmebabyk.sonya.kmb @10.0.4.5 +bufsize=4096 


以上でキャッシュサーバには毒が入っているはず…。これをキャッシュサーバでキャプチャしていたパケットログとキャッシュで確認します。

結果

f:id:senz:20140323231954p:plain

無事、ns3.sonya.kmb(.sonya.kmbのネームサーバのひとつ)のIPアドレスが44.44.44.44になっています。よかったよかった…

と思いきや!
キャッシュサーバのキャッシュをいくら探しても汚染されたキャッシュを見つけることができません。ここで最も参考にしたページを思い出す…
f:id:senz:20140323233104p:plain
UDPチェックサム忘れてた!

というわけで…

手順4:fix UDP checksum

checksumの計算はIPヘッダでも必要なので関数にしてあるしすぐできるだろう…と思っていたのですが、これが超むずかしい。なぜかというと、DNSではサーバ側はポート53をだいたい使いますがクライアント側はそのときによってまちまち。すなわち毎回パケットの中身がわずかに変わるのでチェックサムの値も毎回変わってしまいます。


<2014/5/7追記>
チェックサムを合わせようといろいろやってみました→第1フラグメント便乗攻撃を(僕が)成功させられない理由

結論

IPヘッダのidentificationフィールドですでに総当りしていることを考えると、さらにポート番号すべてを総当りするとなると現実的でない数字になってしまいます。よってこの攻撃はほとんど現実的でないという結論に達しました。(少なくとも自分レベルの技術では)

また今回はDNSSECなどは考慮していないので、強いていうならば

  • DNSSECなどは使っていない
  • 問い合わせに使うポートを固定している
  • どこからの問い合わせにも応答してしまう(オープンリゾルバ)

のすべてに当てはまるキャッシュサーバはこの攻撃を受ける危険性があると思います。そのようなサーバを運営している管理者の方は設定の変更を。(ポート固定とかあるとは思えないが)

まとめ

第一フラグメント便乗攻撃(1st-fragment piggybacking attack)は現実的にそれほど驚異的な攻撃手法ではない。しかし条件が重なれば(ポートが固定されてたり、たまたま当たっちゃったり)毒を入れることは可能であるので、サーバのログに不自然なICMPtooBigが来ていないかなどは注意したほうがよい。

終わりに

今回の件でいろいろやっているうちにBINDの挙動などたくさんのことを学べました。特にパケットを4bitsずつ値を書き込んで作るなどは初体験だったのでとても楽しかったです。バイナリアンならぬ(パケット)ヘキサリアンを名乗りたいぐらいにずっとHexとたわむれていましたw今ならDNS応答のAdditional recordのパケット空で書けます(笑)まあどうせすぐ忘れるんでしょうけど。いい経験になりました。


意見や間違いの指摘などありましたら@senz1024までおねがいします