memcached による Reflection DDoS の挙動を Docker でサクッと確認する

# はじめに

DDoS攻撃で実際に利用できる方法は記載しません。試す環境を用意する程度の内容を記載します。

# MemcachedによるDDoSについて

日本語の記事では、いつもながらpiyolog様に情報が集まっている\([memcached を悪用したDDoS攻撃についてまとめてみた](http://d.hatena.ne.jp/Kango/20180301/1519939259)\)のでそちらを確認するのが良いです。

ざっくり書くと、Memcachedという主にDB向けの分散キャッシュサーバがデフォルト設定でUDPによる通信を許可しており、Reflection DDoSで簡単に利用できる状態になっていた、というものです。

一般にReflection DDoSでは以下のようなサーバがターゲットにされます。

- UDPによる通信が行える
- UDPのレスポンスが大きい(増幅率、アンプ率が高い)
- 広く使われている(脆弱なサーバが無数に存在する)

Memcachedはそれらを満たしており、さらに増幅率が非常に高かったため、非常に大きな攻撃となりました。

2017年はMirai型のbotnetによる攻撃が過去最大のDDoSとなりましたが、2018年になってその記録を更新したものがこのMemcachedによるReflection DDoSです。

# 試す

## Memcachedサーバを用意する

Memcachedはver 1.5.6にて[デフォルトではUDPポートを開かないように更新](https://github.com/memcached/memcached/wiki/ReleaseNotes156)されました。
そのため、この記事では ver 1.5.5 と ver 1.5.6 にそれぞれUDPによる通信を試み、その動作の違いを確認してみようと思います。

異なるバージョンのMemcachedをそれぞれ用意するのは面倒なので、Docker と Docker composeにてテスト環境を用意します。

```docker-compose.yml
version: '3'

services:
  1-5-5:
    image: memcached:1.5.5-alpine
    ports:
      - 11211:11211/tcp
      - 11211:11211/udp
  1-5-6:
    image: memcached:1.5.6-alpine
    ports:
      - 11212:11211/tcp
      - 11212:11211/udp

```

ただUDPによるアンプを試すだけであればTCPでポートを開く必要はありませんが、今回はまずtelnetでの通信を試みようと思っているので、11211のをTCPとUDPの両方で待ち受けることにします。

ホスト側で見たとき、ver 1.5.5 は `11211` を、ver 1.5.6 では `11212` を待ち受けています。

## telnetでアクセスしてみる

まずはtelnetでアクセスしてみます。ver 1.5.5のコンテナへアクセスしてみます。

```terminal
$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
```

難なくアクセスできますね。
ここで `stats` コマンドを打ってみましょう。

```terminal
stas
STAT pid 1
STAT uptime 122
STAT time 1521188454
STAT version 1.5.5
STAT libevent 2.1.8-stable
STAT pointer_size 64
:
```

大量に情報が帰ってきました。たかだか `stats` という文字列を送り込むだけで、大量のレスポンスが帰ってくるわけです。

また、telnetはTCPアクセスなので、ver 1.5.6でもデフォルトでオープンです。アンプには使えずともこれはこれで良いのか微妙ですが、その微妙さ含めてアクセスしてみましょう。

```terminal
$ telnet localhost 11212
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

stats
STAT pid 1
STAT uptime 412
STAT time 1521188744
STAT version 1.5.6
STAT libevent 2.1.8-stable
STAT pointer_size 64
:
```

`set` や `get` で値を出し入れできちゃうので、もしも運用していたならTCPも気をつけましょう(firewallなんかで外から来るTCPパケットを落としましょう)

## UDPでアンプしてみる

Reflection DDoSで実際に攻撃する場合はIPアドレスを詐称するのですが、そんな方法を書くわけにはいかないので、[Cloudflareのセキュリティブログ](https://blog.cloudflare.com/memcrashed-major-amplification-attacks-from-port-11211/)を参考に詐称なしでUDPアクセスしてみます。

```terminal
$ echo -en "\x00\x00\x00\x00\x00\x01\x00\x00stats\r\n" | nc -q1 -u 127.0.0.1 11211
STAT pid 1
STAT uptime 1092
STAT time 1521189424
STAT version 1.5.5
STAT libevent 2.1.8-stable
STAT pointer_size 64
:
```

しっかり返事が来ました。UDPによる通信でアンプされていますね。この動作を試したいだけであれば、 `docker-compose.yml` のTCP行を削除してから行うほうがより「らしい」かもしれないですね。

さて、上記は vwe 1.5.5 へのアクセスだったので、ポートが塞がれた ver 1.5.6 へアクセスしてみましょう。

```terminal
$ echo -en "\x00\x00\x00\x00\x00\x01\x00\x00stats\r\n" | nc -q1 -u 127.0.0.1 11212
$
```

はい、なにも返事がありませんね。確かに ver 1.5.6 ではUDPの通信がデフォルトでオフになっています。

## 試してみて

この実験で初めてMemcachedを触っただけなのでちゃんとした知識がないのですが、 `docker-compose logs` してみると通信の有無はログに一切でていませんでした。デフォルト運用だとアンプに使われているか否かをログから判断できないので、結構大変そうですね。

## パケットキャプチャしてみる

![memcached-udp.PNG](https://qiita-image-store.s3.amazonaws.com/0/31124/44441e0b-b525-bae4-b398-7fa3a918a31e.png)

`stas` の例では、たかだか57byteの送信で1988byteのレスポンスが帰ってきました。だいたい35倍ですね。
なお、少し工夫するだけで倍率はいくらでも大きくなるそうです。

# 終わりに

ざっくりとMemcachedへのアクセスを試してみました。確かに ver 1.5.6 ではデフォルトでUDP通信が無効になっているので、まずはバージョンアップすべきでしょう。

ですが、telnetは相変わらず疎通しますし、ログに通信が出てこないのも気になります。そのあたりはバージョンを上げた後も気をつけるところかな、と思います。