iptables の ipt_recent で ssh の brute force attack 対策

著者:佐藤裕介 (nuts@cclub.cc.tut.ac.jp)
バージョン:2005/11/18 01:00:00 +0900 (JST)
このドキュメントは、クリエイティブ・コモンズ・ライセンスの下でライセンスされています。 クリエイティブ・コモンズ・ライセンス

概要

このドキュメントでは、 ipt_recent モジュールの機能を利用して ssh の brute force attack に対抗する方法を示します。具体的には、あるホストが、 一定時間内に一定回数 ssh でのログインに失敗したら、そのホストからのアク セスを一定時間遮断する、という方法をとります [1]

[1]

もっとも、見ているのはトランスポート層だけなので、本当に SSH で ログインしようとしているのか、は判断していません。

参考URL: http://megadriver.yi.org/~fumi/?date=20060519

必要な知識

このドキュメントでは、次のことは分かっているものとして話を進めます。

用語

試行・ログイン試行・攻撃
どれも、ログインをしようとすること( ssh -l fobar example.com 等 を実行すること)を指します。 foobar@example.com's password: とか が表示される状態まで行けたら「試行が成功した」ということにします。こ のドキュメントで説明している対策では、それ以前の段階で弾かれるように なります( ssh -l fobar example.com を実行すると ssh: connect to host example.com port 22: Connection refused 等と表示される)。

brute force attack とは

最近、私の管理しているホストに向けて行われた攻撃のログ (/var/log/secure on Vine Linux 2.6r4) です。このように、ユーザ名とパスワードの組み合わ せを自動で生成するなり何なりして、それを片っ端から試す攻撃方法を brute force attack (力ずく攻撃・総当り攻撃)と言います [2]

Oct 26 04:07:06 debonair sshd[18883]: Illegal user b from 210.98.138.250
Oct 26 04:07:08 debonair sshd[18883]: Failed password for illegal user b
 from 210.98.138.250 port 59480 ssh2
Oct 26 04:07:09 debonair sshd[18885]: Illegal user c from 210.98.138.250
Oct 26 04:07:11 debonair sshd[18885]: Failed password for illegal user c
 from 210.98.138.250 port 59733 ssh2
Oct 26 04:07:12 debonair sshd[18887]: Illegal user d from 210.98.138.250
Oct 26 04:07:14 debonair sshd[18887]: Failed password for illegal user d
 from 210.98.138.250 port 59983 ssh2
Oct 26 04:07:15 debonair sshd[18889]: Illegal user e from 210.98.138.250
Oct 26 04:07:17 debonair sshd[18889]: Failed password for illegal user e
 from 210.98.138.250 port 60228 ssh2
[2]

この例は a~z, aa~zz, aaa~zzz ……というユーザ名とパスワード の組み合わせを順に試しています。

なお、本ドキュメントの対策は brute force attack 以外の類似の方 法(辞書式攻撃など)にも有効です。

ipt_recent とは

ipt_recent とは、 iptables で ''-m recent'' というオプションを使えるよ うにするモジュールです。このオプションを指定すると、次のような機能を使 うことができます。

今回は、 ipt_recent モジュールの機能 [3] を使って「n 回以上攻撃を試行 したらアクセス禁止」という処理を実装し、 brute force attack に対抗しま す。

[3]

IP アドレスを記録するリストはいくつでも作れるので「このプロトコ ルならこのリスト」等の処理が可能です。

また、リストにはパケットの source IP アドレスに加えてタイムスタ ンプも記録されるので、「直近 n 秒の間に記録があったら」や「n 回 以上記録があったら」といった条件も指定できます。

参考のために、 Fabrice MARIE 氏の Netfilter Extensions HOWTO より 3.16 recent patch の訳を以下に挙げておきます。


3.16 recent patch

Stephen Frost <sfrost@snowman.net> によるパッチです。これを使うと、 IP アドレスのリストを動的に作成することができます。また、そのリスト とのマッチをとることもできます(マッチの方法はいくつかあります)。

例えば、ファイアウォールの port 139 にアクセスしようとしてきた輩を 'badguy' というリストにして、以降そこからのパケットを何も考えずに DROP する、とかいうことができます。

# iptables -A FORWARD -m recent --name badguy --rcheck --seconds 60 -j DROP
# iptables -A FORWARD -p tcp -i eth0 --dport 139 -m recent --name badguy --set -j DROP

# iptables --list
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
DROP       all  --  anywhere             anywhere           recent: CHECK seconds: 60
DROP       tcp  --  anywhere             anywhere           tcp dpt:netbios-ssn recent: SET

recent マッチで使えるオプションは :

--name name
-> 使うリストの名前を指定します。指定がない場合は 'DEFAULT' が使わ れます。
[!] --set
-> パケットの source アドレスをリストに追加します。アドレスが既にリ ストにある場合は、既存のエントリを更新します。常に成功します( `!' が指定されている場合は、常に失敗します)。
[!] --rcheck
-> パケットの source アドレスがリストにあるかどうか調べ、あれば true を返し、さもなくば false を返します。 `!' が指定されている場 合はその逆が返ります。
[!] --update
-> パケットの source アドレスがリストにあるかどうか調べます。あれば エントリを更新し true を返します。リストになければ false を返しま す。 `!' が指定されている場合はその逆が返ります。
[!] --remove
-> パケットの source アドレスがリストにあるかどうか調べます。あれば リストからアドレスを削除して true を返します。アドレスが見つからな ければ false を返します。 `!' が指定されている場合はその逆が返りま す。
[!] --seconds seconds
-> このオプションは `rcheck' または `update' と一緒に使います。 これを指定すると、マッチが絞り込まれ、アドレスがリストにあり、か つ、ここ seconds 秒のあいだにある場合にのみマッチするようになりま す。 `!' が指定されている場合は逆が返ります。
[!] --hitcount hits
-> このオプションは `rcheck' または `update' と一緒に使います。 これを指定すると、マッチが絞り込まれ、アドレスがリストにあり、か つ、 hits 回以上受信している場合にのみマッチするようになります。こ のオプションを `seconds' と一緒に使うと、一定時間内に一定回数受信 したら、というより絞り込んだマッチを作成することができます。 `!' が指定されている場合は逆が返ります。
--rttl
-> このオプションは `rcheck' または `update' と一緒に使います。 これを指定すると、マッチが絞り込まれ、アドレスがリストにあり、か つ、パケットの TTL が --set ルールにヒットしたパケットの TTL と一致 した場合にのみマッチするようになります。これは、 source アドレスを 偽って DoS を仕掛けようとする(このモジュールの機能を利用して、偽造 したパケットを送りつけることで他の人がホストにアクセスできないよう にする)輩がいる場合に役立ちます。

実際の利用例

1. n 秒間に可能な試行を m 回までにする

images/ipt_recent_strategy_1.png

簡単なものとして、 60 秒間に 5 回までしかログインを試行できないように する、という方法を試してみましょう。60 秒間に 5 回ログインを試行すると それ以降はしばらくログイン試行が失敗するようになり、最初のアクセスから 60 秒経過すると 6 回目のログイン試行ができるようになります。

$IPTABLES -A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --set
$IPTABLES -A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --rcheck --seconds 60 --hitcount 5 -j LOG --log-prefix 'SSH attack: '
$IPTABLES -A INPUT -p tcp --syn --dport 22 -m recent --name sshattack --rcheck --seconds 60 --hitcount 5 -j DROP

それぞれのルールの意味は次のようなものです。

  1. port 22 に対して接続を確立しようとしたパケットの source IP アドレス を記録します。
  2. 直近の 60 秒間に 5 回以上 1. の記録があった場合、ログを取ります。
  3. 直近の 60 秒間に 5 回以上 1. の記録があった場合、 DROP します。

ここでは例を簡単にするために INPUT チェインに直接ルールを追加していま す。フィルタリングルールのメンテナンスを容易にしたければ、新しくチェイ ンを作って(例えば $IPTABLES -N ssh )そこにルールを追加し、 ssh のパケットをそのチェインに通すようにした方が見通しがいいでしょう。

この例は次のメーリングリストへの投稿を参考にしました。 http://lists.suse.com/archive/suse-security/2005-Feb/0024.html

2. n 秒間に m 回試行したら、以降 l 分間は試行不可能にする

images/ipt_recent_strategy_2.png

もう少し複雑なものでは、 60 秒間に 5 回以上ログインを試行したら、以降 10 分間はアクセス禁止、という処理も可能です。

$IPTABLES -N SSH-evil
$IPTABLES -A SSH-evil -m recent --name badSSH --set -j LOG --log-level DEBUG --log-prefix "evil SSH user: "
$IPTABLES -A SSH-evil -j REJECT

$IPTABLES -A SSH -p tcp ! --syn -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -N SSH
$IPTABLES -A SSH -p tcp --syn -m recent --name badSSH --rcheck --seconds 600 -j REJECT
$IPTABLES -A SSH -p tcp --syn -m recent --name sshconn --rcheck --seconds 60 --hitcount 5 -j SSH-evil
$IPTABLES -A SSH -p tcp --syn -m recent --name sshconn --set
$IPTABLES -A SSH -p tcp --syn -j ACCEPT

SSH-evil チェインと SSH チェインがありますが、先に SSH チェインに目を 通して下さい。 SSH チェインには、 -p tcp --dport ssh にマッチしたパ ケットが入ってくるようにします [4]

IP アドレスのリストとして sshconn と badSSH の 2 つを使っています。役割 は次の通りです。

sshconn
tcp/22 に飛んできた SYN パケットが記録されます。つまり、 ssh でのログ インを試行した時に記録が残ります。
badSSH
ブラックリストです。brute force attack だと判断された場合に、攻撃元 ホストの IP アドレスが記録されます。

それぞれのルールの意味は次のようなものです。

SSH チェイン
  1. まず、 SYN フラグの立っていないパケットは ACCEPT します。

  2. brute force attack 攻撃元ホストからのパケットを REJECT します。

    具体的には、「これは brute force attack だ」という判定が、ここ 10 分以内になされているホストからのパケットだった場合、 REJECT しま す。

  3. brute force attack が行われているかどうかの判定です。

    具体的には、パケットの source IP アドレスが、 sshconn リストに、 ここ 60 秒間で 5 回以上記録されていたら「これは brute force attack だ」と判断します。 SSH-evil チェインに飛びます。

  4. 2.3. のルールにマッチしなかった場合、 sshconn リストにパ ケットの source IP アドレスを記録します。この記録で 3. の判定が決 まります。

  5. 4. の記録が済んだら ACCEPT します。

SSH-evil チェイン
  1. SSH チェインの 3. から入ってきます。ここに来たパケットは、次の条 件を満たすものです。

    • -p tcp --dport ssh --syn にマッチした
    • 同じホストから、ここ 60 秒間に 5 回以上飛んできている

    brute force attack が行われていると判断し、このパケットの source IP アドレスを BadSSH リストに登録して、ログをとります。

  2. パケットを REJECT します。

[4]例えば $IPTABLES -A INPUT -p tcp --dport ssh -j SSH

この例は次のメーリングリストへの投稿を参考にしました。 http://lists.suse.com/archive/suse-security/2005-Feb/0026.html

弱点

images/login_jamming.png

この方法にも弱点があります。ログイン試行を受け入れるかどうかの判断が、 ホストベース(IP アドレスベース)でしかできないことです。

ここで、 Bob さんは Alice さんがホスト R へログインするのを妨害できま す。 Bob さんがホスト R へのログイン試行をひたすら繰り返していると、ホ スト R の SSH-evil チェインには常にホスト L のアドレスが記録され続ける ことになり、 Alice さんはホスト R にログインできなくなってしまいます。

この問題に対処しようとすると ssh の通信内容を知る必要が出てくるため、 iptables だけでは対処のしようがありません。もっとも、この問題に対策を しようとすると(1ユーザごとに n 回まで試行可能、などの設定ができるよう にすると)、対策にスキができてしまいます。つまり「ユーザ名を変えて同じ パスワードを試し続ける」というタイプの攻撃( brute force attack とは の例を参照)に全く無防備になってしまいます。 あきらめた方がいい、というのが私の結論です。

なお、 source IP アドレスを偽装したパケットを送りつけることでも同様の 攻撃が行えますが、この場合は上記 Netfilter Extensions HOWTO の通り --rttl オプションを使用すればある程度の対策が行えるようです(当方では 動作未確認)。

その他の対策方法

以下では、このドキュメントで触れなかった brute force attack 対策の方法 について挙げておきます。

sshd の listen するポートを 22 以外に変える

通常、攻撃者は 22/tcp を listen しているプロセスがいるかどうかで ssh を使っているかどうか確認する(と思う)ので、 sshd が listen するポート を、例えば 8022/tcp とかに変えてしまえば攻撃の頻度は一気に下がります (と思います)。

sshd が listen するポートの設定は、''/etc/ssh/sshd_config'' ファイルで 行えます。

発展

以下、このドキュメントの対象に入らなかった話題やツールの紹介です。

[5]排除時間を長くしても、自分が締め出される心配がなくなります。否 定的な意見も多いポートノッキングですが、こういう使いかたなら大 丈夫じゃないかと思います。

更新履歴

2005-11-18
  • 最初のバージョン。
2007-03-25
  • 「弱点」「その他の対策方法」の節を追加。
  • その他、リンク元のページに書いてあったいろいろを取り込んで加筆。
  • 実際の使用例がどう働くか、を示す図を追加した。

このドキュメントに対するコメントです。

このドキュメントについてコメントする / コメントを全部見る