knqyf263's blog

自分のためのメモとして残しておくためのブログ。

CVE-2019-6467 (BINDのnxdomain-redirectに関する脆弱性)について

概要

今日の朝ぐらいにBINDでCVE-2019-6467の脆弱性が公開されました。最近BINDの脆弱性が出ても時間が取れず調査できていなかったので今回は超速で調査しました。世界で一番早く解析したんじゃないかなと思ってます(未確認)。

CVE-2019-6467はnxdomain-redirectに関する脆弱性です。

参考URL

詳細

前提

そもそもこの脆弱性はnxdomain-redirectを使っていない場合は影響を受けません。そのため、多くのBINDユーザには影響なしだと思われます。

それでもこの脆弱性の詳細が知りたい!!!という知的好奇心が抑えられない人だけこれ以降を読んで下さい。

NXDomain redirection

nxdomain-redirectはBINDの9.11から入った機能なのですが、調べてもとにかく情報が出ません。それぐらいマイナーな機能なんじゃないかと思っていますが、昔見つかったCVE-2016-9778もnxdomain-redirectに関連する脆弱性でその時にJPRSから出ていた資料がわかりやすいです。

BIND 9.xの脆弱性(DNSサービスの停止)について(CVE-2016-9778)

BIND 9.xには、名前が存在しなかった場合にNXDOMAINに替えて特定のIPアドレス(AまたはAAAA)に対する応答を返すように設定するための機能が二つ実装されています(1)。一つは、BIND 9.9で実装されたRedirect zone機能で(2)、もう一つはBIND 9.11で実装された、より柔軟な設定が可能なRedirect namespace機能です(3)(4)。

今回はこの2つのNXDOMAIN redirectionのうち、後者(nxdomain-redirect)の方に関するものです。ちなみにNXDOMAINはドメイン名が見つからなかった時に返すレスポンスタイプですね。

脆弱性概要

それを踏まえた上で参考URLに挙げたISCのページを見てみます。

In certain configurations, named could crash with an assertion failure if nxdomain-redirect was in use and a redirected query resulted in an NXDOMAIN from the cache. This flaw is disclosed in CVE-2019-6467. [GL #880]

これを読むと、nxdomain-redirectが有効でリダイレクトされた先もNXDOMAINになったときにクラッシュすると言っているように見えます。しかもそれがキャッシュから取り出されたときなのでネガティブキャッシュがあることが条件。それって設定次第では普通に起き得るのでは...?という気持ちになります。ただ In certain configurations と言っているので普通には起きないのかも、という若干の安心感があります。

どういうソースコードの差分があるか見てみます。

$ wget ftp://ftp.isc.org/isc/bind9/9.12.4/bind-9.12.4.tar.gz
$ wget ftp://ftp.isc.org/isc/bind9/9.12.4-P1/bind-9.12.4-P1.tar.gz
$ tar xvf bind-9.12.4.tar.gz
$ tar xvf bind-9.12.4-P1.tar.gz
$ diff -r bind-9.12.4-P1/ bind-9.12.4/
...
Only in bind-9.12.4-P1/bind-9.12.4-P1/bin/tests/system/redirect: ns5
Only in bind-9.12.4-P1/bind-9.12.4-P1/bin/tests/system/redirect: ns6
...

ほとんどはCHANGELOGとかの差分ですが、testsの中にredirectというディレクトリがあります。今回の脆弱性を受けて足されたテストということは、今回の脆弱性を再現するような設定になっている可能性が高いです。

$ cat bind-9.12.4-P1/bin/tests/system/redirect/ns5/named.conf.in
...
options {
        port @PORT@;
        listen-on port @PORT@ { 10.53.0.5; };
        pid-file "named.pid";
        nxdomain-redirect signed;
};

zone "." {
        type master;
        file "root.db.signed";
};

// An unsigned zone that ns6 has a delegation for.
zone "unsigned." {
        type master;
        file "unsigned.db";
};

中を見ると、確かにnxdomain-redirectの設定が足されています。こうやって設定するのかーと思うのと同時に、nxdomain-redirectってIPアドレスとか書くんじゃないの?書くとどういう動きになるの?という謎が生まれてきます。

nxdomain-redirect

BINDのマニュアルを見てみます。

Chapter 6. BIND 9 Configuration Reference

With a redirect namespace (option { nxdomain-redirect };) the data used to replace the NXDOMAIN is part of the normal namespace and is looked up by appending the specified suffix to the original query name. This roughly doubles the cache required to process NXDOMAIN responses as you have the original NXDOMAIN response and the replacement data or a NXDOMAIN indicating that there is no replacement.

と書いてあります。 is looked up by appending the specified suffix to the original query name あたりを読むに、元のクエリのsuffixにnxdomain-redirectが足されるのか!という雰囲気が伝わります。

では早速試してみます。

$ cat /etc/named.conf
options {
        nxdomain-redirect redirect;
};

zone "." IN {
        type hint;
        file "named.root";
};

zone "example.com." IN {
        type master;
        file "example.com.zone";
        allow-update { none; };
};

zone "redirect" IN {
        type master;
        file "example.com.redirect.zone";
};

上の設定は色々省略しているのでそのままでは動かないです。そしてzoneファイルを次のようにします。

$ cat example.com.redirect.zone
$ORIGIN redirect.
$TTL 3600       ; 1 hour
@ IN SOA ns1.example.com. postmaster.example.com. (
        2015012902  ; serial
        3600        ; refresh (1 hour)
        1200        ; retry (20 min.)
        1209600     ; expire (2 weeks)
        900         ; minimum (15 min.)
        )
@       IN  NS      ns1.example.com.
@       IN  NS      ns2.example.com.

ns1         IN  A       192.168.1.2
ns2         IN  A       192.168.1.3
nxdomain.example.com IN  A       192.168.1.4

こうしておけば、nxdomain.example.comを引いた時にBINDはNXDOMAINを返そうとするが、 nxdomain-redirectredirect が指定されているのでsuffixにくっつけて nxdomain.example.com.redirect を探しに行くはず。上で nxdomain.example.com.redirect の時に192.168.1.4を返すように設定してあるので nxdomain.example.com はNXDOMAINなのに 192.168.1.4 が返ってくる、という仕組みのはずです。

結論から言うとこれはうまく動きませんでした。nxdomain.example.comじゃなくてnxdomainなのか?!nxdomain.example.com.redirect. まで書かないとダメなのか?!などなど悪戦苦闘しまくりましたが、実は example.com が正解でした。つまり

example.com IN  A       192.168.1.4

こうしておくと、nxdomain.example.comを引いた時にexample.com.redirectにリダイレクトされるようです。冷静に考えたらnxdomainの部分は何でも良いわけで、そこまで指定するわけがありませんでした。*.example.comとかはありそうですが。

少し話がそれますが、CVE-2016-9778では以下のように書かれています。

BIND 9.xにはRedirect namespace機能に不具合があり、nxdomain-redirectオプションにおいて自身が権威を持つゾーンが指定されていた場合、本機能に該当する問い合わせを処理する際にnamedが異常終了を起こす障害が発生します(*5)。

ということは上の設定だと死ぬんだろうか...

話を戻します。完全に推測ですが、上の設定はexample.comのNXDOMAINのときは192.168.1.4という意味になっていて、他にも example2.com とか example3.com とかのzoneを管理している時に、redirectのゾーンで

example.com IN  A       192.168.1.4
example2.com IN  A       192.168.1.5
example3.com IN  A       192.168.1.6

のように書けるのが嬉しい、ということじゃないかと思います。とにかくnxdomain-redirectに関してドキュメントが見つからず試行錯誤の結果なので、正しい用法などあればJPRSの方などからの指摘をお待ちしております。

試行錯誤の結果を置いておきます。存在しないドメイン名であれば何を問い合わせても192.168.1.4が返ってきます。

github.com

脆弱性詳細

ということでようやく脆弱性詳細です。先程のdiffをもう一度見てみると、ソースコード上にもいくつか差分があります。

diff -r bind-9.12.4-P1lib/ns/query.c bind-9.12.4/lib/ns/query.c
5900a5891
>               qctx->is_zone = qctx->client->query.redirect.is_zone;
6006a5998
>               qctx->is_zone = qctx->client->query.redirect.is_zone;

中でも関係ありそうなのが上の箇所です。GitHub上で見ると以下です。

bind9/query.c at v9_12_4 · isc-projects/bind9 · GitHub

リダイレクトの場合は qctx->is_zoneqctx->client->query.redirect.is_zone を代入しています。脆弱性の概要にはassertion failureで落ちると書いてあったので、この qctx->is_zone をassertにかけているところを探します。

すると、INSISTに qctx->is_zone を渡している箇所が複数見つかります。INSISTは単にメッセージを出力してabortするassertだと思って良いと思います。

bind9/gen.c at f285dd9a0828ba472645e61e5e9608c852aa31b6 · isc-projects/bind9 · GitHub

中でもquery_ncache関数の中で呼ばれている INSIST(!qctx->is_zone); はとても怪しいです。何故かと言うと脆弱性概要で a redirected query resulted in an NXDOMAIN from the cache のように書かれており、キャッシュからNXDOMAINを取り出す、つまりネガティブキャッシュの箇所で起きる可能性が高いためです。

bind9/query.c at v9_12_4 · isc-projects/bind9 · GitHub

ということでソースコードも読んだので実際に動かします。redirect先がNXDOMAINになるようにするのは簡単です。redirect先が存在しなければ良い。先程の例でいうと、redirectのzoneを消せばよいです。

$ cat /etc/named.conf
options {
        nxdomain-redirect redirect;
};

zone "." IN {
        type hint;
        file "named.root";
};

zone "example.com." IN {
        type master;
        file "example.com.zone";
        allow-update { none; };
};

これで試せる状態になりました。

$ dig @127.0.0.1 nxdomain.example.com

このように適当に存在しないドメイン名を引いてみます。

...
25-Apr-2019 06:40:13.561 query.c:9309: INSIST(!qctx->is_zone) failed
25-Apr-2019 06:40:13.561 exiting (due to assertion failure)
25-Apr-2019 06:40:13.562 resolver priming query complete

oh...

BINDが死にました。ネガティブキャッシュと言っていたので一度キャッシュさせて二回目で死ぬのかな、と思っていたのですがクエリ一発で一瞬で死にます。確かに上のquery_ncacheを見る感じだと最初にINSISTしているので、NXDOMAINならすぐクラッシュするようです。

In certain configurations って言ってたじゃん!!単にNXDOMAINになるだけで再現するじゃないか!!

ということで意外な程にあっさりと再現できました。試したい人のために例によって再現環境を置いておきました。GIFとかもあるので動画見たい人はそちらをどうぞ。

github.com

対策

NXDOMAINにならなければクラッシュしないため、きちんと存在するzoneに対してredirectして上げれば影響ないと思います。 パッチ済みバージョンではis_zoneの代入が削除されているので、クラッシュしなくなっています。

まとめ

BINDのnxdomain-redirectに関する脆弱性(CVE-2019-6467)について調査しました。結果としては、あまりにも簡単に再現できてしまいました。ちゃんとテストしたのかな...と不安になるレベルです。ほとんど使われてないようなので問題なさそうですが。