転載・引用について

ユーザ用ツール

サイト用ツール


os:freebsd:blacklistd

差分

このページの2つのバージョン間の差分を表示します。


os:freebsd:blacklistd [2024/07/02 04:35] (現在) – 作成 - 外部編集 127.0.0.1
行 1: 行 1:
 +====== blacklistd ======
 +
 +2024/07/02
 +
 +筆者が管理してるメールサーバーのユーザーがパスワード抜かれてPhishing Mailを転送しそうになってた。88通。
 +
 +  ; from : 抜かれた人のメールアドレス
 +  ; to   : docomoのmail address
 +  ; Body : 国税庁を騙ったフィッシングメール
 +
 +顛末
 +  - パスワード抜かれた人から、「送ってもいない携帯キャリア宛メール」が配送されなかったエラーが大量に来る、と報告される
 +  - 外出してて、友達と晩飯食ってる最中だったので、logの解析すらできず、取り急ぎ、smtpサーバー止める。結果的に4時間smtpサーバー止めた
 +  - 帰宅して解析開始。この段階でspamの配信元になりそうになってたことに気づき焦ったが、全てError Mailになっていたことに気づき、ちょっと安心
 +    * 結局、このエラーメールを送信するちょっと(大体3時間程度)前にパスワードが破られたことが判明
 +  - logから、SMTP AUTHで使われる「メールアドレスとパスワード」が抜かれて、SPAMを転送としようとしていることが判明。パスワードを強制変更
 +  - SMTP AUTHの認証を確認してSMTPサーバー復旧
 +
 +今回は「転送失敗」メールが88件、転送成功メールは0件で、他人に被害出さなくて済んだから良かったけどちょっと怖い思いをした。なお、転送に失敗していたのは、受信側の「携帯キャリア」のMail ServerがSMTPUTF8拡張をサポートしていなかったからだった。正直運良く転送されなかっただけで、一歩間違えたら大惨事(88通は決して少なくない)だった。
 +
 +というわけで、せめてもの被害軽減策としてblacklistdを導入して、SMTPAUTHとIMAPへの接続を監視し、パスワードチェック攻撃を少しでもreduceしようとしてみた。パスワード破られたら、DKIMもDMARKもSPFも関係なく全部送れてしまうわけで...
 +
 +なお、新しいパスワードは、強制的に16桁にしたから、しばらくは大丈夫。大丈夫だと思う。大丈であって欲しい。
 +(元のパスワード、6桁だったらしい。よく10年以上抜かれなかったなぁ...)
 +
 +(Event Driven...)
 +
 +===== 問題と事前考察 =====
 +
 +根本的には、
 +
 +  * Internetからの接続試行を防ぐことはできない
 +    * 全ての通信をVPNなどの技術を使って一度localに繋がせてからSMTPを利用させることはできるが、結局攻撃場所をVPN受け口に変更する程度の意味しかない
 +      * それでも素のSMTPAUTHでの認証試行を受けるよりは安全度は上がるが、利用者に対する負担が大きいだけでなく、VPN用の鍵管理などで作業量や管理料が増えてしまう
 +    * 外部からの利用者もいる状況の中で、1回のミスも許さない仕様にしても1回は施行される上に、新たにアカウント発行したものに対して及び、ユーザーがメールクライアントを変更した場合の試験すらミスが許されなくなるので、現実的ではない
 +  * なんらかの形で「攻撃元IP Address」のリスト(もしくはAddress Block)が明確になれば、他のサービスに転用することも可能になる
 +  * Blacklistdは「ある特定期間」に「blacklistdに対応したApplicationへの認証失敗」(実際にはそれに限らず、TCP接続試行などへの対応もできるだろうが、ここでは割愛)をApplicationからblacklistdに送付することによって、「ある条件を満たした攻撃元IP Address」を「指定された実行プログラムに引き渡す」と言う動作をする
 +    * 今回は、この「指定された実行プログラム」をシステム標準で提供されている''/usr/libexec/blacklistd-helper''を用いて、pfに登録することで、攻撃を緩和する
 +
 +という考察をした。もちろん、blacklistdで対応できる範囲はそれほど大きくないが、それでも何もしないよりは大きく改善することが期待できる。
 +
 +===== blacklistdの導入 =====
 +
 +FreeBSDだと、portsを使ってPostfixをCompileして導入すればblacklistdに対応できる。Option設定画面でblacklistdをOnにしておくこと。
 +
 +あとは、SMTP関連の設定だが、これはあまりにも重い話なので割愛。
 +
 +blacklistdへの対応済みpostfixをblacklistdが動いていない状況で稼働させると、認証失敗時に''/var/log/debug.log''
 +<code>
 +Jun  9 15:11:24 mta postfix/smtpd[95847]: bl_init: connect failed for `/var/run/blacklistd.sock' (No such file or directory)
 +</code>
 +のようにErrorが記載される。(logファイルが ''/var/log/debug.log''になるかどうかはsyslogdの設定に依存する)
 +
 +この状態で、blacklistd及びpfの設定を行う。
 +
 +==== pfの設定 ====
 +
 +本記事においては、
 +  * mta serverはMail関連のポートとsshdのポートのみLISTENしており、他のポートは開いていないので、基本のfilter ruleはall passとする。
 +  * blacklistdから送られてくるアドレスリストは、攻撃者のものであると仮定し、全てdrop(returnではない)する
 +というポリシーでpf(Firewall)を設定する。
 +
 +当然、自分の環境に合わせてしっかり確認してください。違いはせいぜい、pfのfilter ruleくらいしかないでしょう。
 +
 +と言うわけで、Configは以下の通り。
 +
 +<code - pf.conf>
 +##############################################################################
 +# pf.conf for mta
 +##############################################################################
 +
 +##### Macros
 +# Interfaces
 +IFextn="xn1"
 +
 +##### Tables
 +
 +table <rfc1918> const { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7 }
 +table <rfc6890> const { 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:0db8::/32 }
 +table <rfc6598> const { 100.64.0.0/10 }         # ISP CGN
 +# need to create /etc/pf.rej (Empty file permitted)
 +table <reject> persist file "/etc/pf.rej"
 +
 +##### pf Options
 +set block-policy drop
 +
 +##### Normarization
 +scrub in on $IFextn random-id fragment reassemble
 +scrub    on $IFextn random-id
 +
 +##### Filtering ruleset
 +set skip on lo0
 +
 +pass all
 +anchor "blacklistd/*" in on $IFextn
 +</code>
 +
 +ポイントは、最後にある''anchor "blacklistd/*" in on $IFextn''である。
 +この行が意味するのは、
 +  * Interface $IFextn (xn1)において、
 +  * Incomming(外から入ってくる通信)に対して、
 +  * blacklistdが作成するAnchorルールを適用する
 +ということになる。
 +
 +==== pfの起動 ====
 +ここまで設定したら、/etc/rc.confに
 +<code>
 +# pf
 +pf_enable="YES"
 +pflog_enable="YES"
 +</code>
 +を記載し、''service pf start && service pflog start''を実行する。
 +
 +==== blacklistdの設定 ====
 +
 +blacklistdの設定は、rc.confで設定する''blacklistd_flags''に設定された引数と''/etc/blacklistd.conf''によって行う。
 +このほかに、
 +
 +  * /usr/libexec/blacklistd-helper
 +    * pf/npf/ipf/ipfwにEntryを追加するためのscript
 +  * /var/db/blacklistd.db
 +    * blacklistdによってblacklistに入れられたIP AddressのDB
 +      * blacklistctlでdumpでき、blacklistd起動時の引数 -r で参照される
 +
 +というファイルが利用される。
 +
 +以下、blacklistd.confのサンプル
 +<code>
 +# $FreeBSD$
 +#
 +# Blacklist rule
 +# adr/mask:port type    proto   owner           name    nfail   disable
 +[local]
 +ssh             stream  *                                 24h
 +smtp            stream  *                                 24h
 +smtps           stream  *                                 24h
 +submission      stream  *                                 24h
 +imaps           stream  *                                 24h
 +*                                                     60
 +
 +# WhiteLists
 +# adr/mask:port type    proto   owner   name    nfail   disable
 +[remote]
 +# Never block localhost
 +127.0.0.1/32    *                                       *
 +[::1]/128                                             *
 +</code>
 +
 +==== blacklistdの起動 ====
 +ここまで設定が終わったら、Blacklistdを起動する。
 +
 +  * まず、/etc/rc.confに以下の設定を追加する
 +    * <code>
 +blacklistd_enable="YES"
 +blacklistd_flags="-r"
 +</code>
 +      * -rは、最後にblacklistdを修了した時のBlacklistデータを読み込む
 +      * -t [数値]は、登録されたIP Address情報をDBと比較して必要な更新を行うInterval time
 +        * このInterval timeはあくまでもDBとメモリー情報を同期するものであって、fail 回数を初期化するものではないことに注意
 +      * -fを起動時引数に与えると、DBを初期化してから起動する
 +  * 次に、''service blacklistd start''を実行する
 +    * この時、もしnameが間違っている場合、pf側でErrorが出る
 +      * これは、「そんなRuleはない」というエラーなので、blacklistd.confを修正して、''service blacklistd restart''すれば良い
 +
 +==== blacklistdに関連するpfctlのコマンド ====
 +  * ''pfctl -sr''
 +    * とにかく、pfのRulesetを確認する。ここにAnchor設定があることを確認する
 +      * 今回の例では、以下が表示されるはず。
 +      * <code>
 +anchor "blacklistd/*" in on xn1 all
 +</code>
 +  * ''pfctl -a "*" -sr''
 +    * Rulesetに記載された全てのAnchorを含むRulesetを確認
 +      * Anchor行が展開される
 +      * <code>
 +anchor "blacklistd/*" in on xn1 all {
 +  anchor "25" all {
 +    block drop in quick proto tcp from <port25> to any port = smtp
 +  }
 +  anchor "587" all {
 +    block drop in quick proto tcp from <port587> to any port = submission
 +  }
 +}
 +</code>
 +  * ''pfctl -a "blacklistd/25" -sr'' と''pfctl -a "blacklistd/587" -sr''
 +    * Anchor行に"25"と"587"があるので、それぞれのRuleを確認
 +      * <code>
 +# pfctl -a "blacklistd/25" -sr
 +block drop in quick proto tcp from <port25> to any port = smtp
 +# pfctl -a "blacklistd/587" -sr
 +block drop in quick proto tcp from <port587> to any port = submission
 +</code>
 +  * それぞれのTableを確認
 +    * Configを見れば自明だが、一応コマンドで取得
 +    * scriptにするときに必要
 +      * <code>
 +# pfctl -a "blacklistd/25" -sT
 +port25
 +# pfctl -a "blacklistd/587" -sT
 +port587
 +</code>
 +  * ''pfctl -a "blacklistd/587" -t port25 -Ts''と''pfctl -a "blacklistd/587" -t port587 -Ts''
 +    * それぞれのTableの内容を確認
 +      * <code>
 +# pfctl -a "blacklistd/587" -t port25 -Ts
 +   92.118.39.119
 +   178.215.236.46
 +   178.215.236.175
 +   178.215.236.209
 +   188.166.90.155
 +   193.32.162.75
 +   193.32.162.80
 +# pfctl -a "blacklistd/587" -t port587 -Ts
 +   156.224.20.135
 +</code>
 +  * ''blacklistctl dump -a''
 +    * Blacklistに載っている全てのエントリを表示
 +      * <code>
 +# blacklistctl dump -a
 +        address/ma:port id      nfail   last access
 +185.208.158.235/32:25           2/    2024/07/02 02:07:28
 + 178.215.236.46/32:25   OK      3/3     2024/07/01 22:45:59
 +  193.32.162.80/32:25   OK      3/3     2024/07/02 00:10:59
 +178.215.236.175/32:25   OK      3/3     2024/07/01 22:37:48
 +  93.123.39.205/32:25           1/    2024/07/02 03:04:55
 +  193.32.162.75/32:25   OK      3/3     2024/07/01 22:30:08
 +178.215.236.209/32:25   OK      3/3     2024/07/01 23:04:36
 + 188.166.90.155/32:25   OK      3/3     2024/07/02 01:22:29
 + 156.224.20.135/32:587  OK      3/3     2024/07/02 02:30:43
 +  92.118.39.119/32:25   OK      3/3     2024/07/01 23:15:31
 +</code>
 +  * ''blacklistctl dump -b''
 +    * Blacklistに載っているのエントリのうち、Blockするものだけを表示
 +      * <code>
 +# blacklistctl dump -b
 +        address/ma:port id      nfail   last access
 + 178.215.236.46/32:25   OK      3/3     2024/07/01 22:45:59
 +  193.32.162.80/32:25   OK      3/3     2024/07/02 00:10:59
 +178.215.236.175/32:25   OK      3/3     2024/07/01 22:37:48
 +  193.32.162.75/32:25   OK      3/3     2024/07/01 22:30:08
 +178.215.236.209/32:25   OK      3/3     2024/07/01 23:04:36
 + 188.166.90.155/32:25   OK      3/3     2024/07/02 01:22:29
 + 156.224.20.135/32:587  OK      3/3     2024/07/02 02:30:43
 +  92.118.39.119/32:25   OK      3/3     2024/07/01 23:15:31
 +</code>
 +  * ''blacklistctl dump -r''
 +    * Blacklistに載っている「まだblockするほどではない」リストを表示
 +      * <code>
 +# blacklistctl dump -r
 +        address/ma:port id      nfail   remaining time
 +185.208.158.235/32:25           2/    22h43m15s
 +  93.123.39.205/32:25           1/    23h40m42s
 +</code>
 +
 +==== おまけ ====
 +=== sshdでもblacklistd対応 ===
 +
 +  * ''/etc/blacklistd.conf''にsshのエントリーを作成(defaultで記述されているはず)
 +  * ''/etc/rc.d''に''sshd_flags="-o UseBlackList=yes"''を追加
 +    * もちろん、''/etc/ssh/sshd_config''に反映させても良い
 +  * 必要に応じて ''service blacklistd restart'' と ''service sshd restart'' を実行
 +これでsshdもblacklistd対応になる。
 +
 +<WRAP round Info>
 +=== Signal ===
 +
 +  * Debug Levelを変化させる方法
 +    * blacklistdに対して SIG_USR1を送りつけると、debugレベルが上がる
 +    * blacklistdに対して SIG_USR2を送りつけると、debugレベルが下がる
 +  * 設定を再読み込みさせる方法
 +    * blacklistdに対して、SIG_HUPを送りつける
 +  * 終了させる方法
 +    * blacklistdに対して、SIG_KILL / SIG_INT / SIGTERMを送りつける
 +</WRAP>
 +
 +===== 落穂拾い =====
 +
 +<WRAP round info>
 +==== name ====
 +nameフィールドは、使用するパケットフィルタールールの名前である。
 +
 +名前が "-" で始まる場合、デフォルトのルール名がその名前の前に付加される。
 +
 +名前に "/" が含まれる場合、名前の残りの部分はルールで指定されたアドレスに適用されるマスクとして解釈され、1つのルール違反で設定されたプレフィックス全体のサブネットがブロックされる
 +
 +nameに''/29''と書くと、そのRuleでMatchしたSrcAddressに対して''/29''のNetmaskを付加した上で、その範囲のアドレスがBlockされるという挙動になる。
 +</WRAP>
 +
 +<WRAP round info>
 +==== disable / duration ====
 +
 +このフィールドには ''最後のアクセスからブロッキングルールが有効であるべき時間を指定する。デフォルトは "" で、これは「永久に」を意味する。disableのデフォルト単位は秒だが、異なる単位を示す接尾辞を指定することができる。例えば、"m" は分、"h" は時間、"d" は日を表す。''
 +
 +source codeを読む限り、このフィールドに、例えば''1d12h30m59s''のように指定できるように見える。こう指定した場合、一度drop listに入ったら、1日12時間30分59秒の間Blacklistに登録される(すなわち、pfのdropテーブルに載り、filterout される)ように見える。
 +
 +また、Source codeとdebug logを見る限り、Disable / Durationフィールドの値は以下のような動作に利用されるように見える。
 +
 +  - ApplicationがblacklistdにAddressを送る→Blacklist候補としてアドレスとポート番号を登録し、LastAccessを現在の時刻として登録する
 +  - Blacklistdは -tで指定された値(もしくは15秒、もしくはdebugの値によっては5秒)ごとに、データをDBに同期する
 +    * 現在の時刻からLastAccessを引いて、その値が0以下になったらBlacklistから当該アドレスを削除する
 +    * この時、もし当該アドレスがFirewallのFilterout tableに登録されているなら、Filterout tableから当該アドレスを削除する
 +  - もし、durationが来る前にApplicationがblacklistdにAddressを送ったら、LastAccessを現在の時刻に置き換え、nfailを増やす
 +  * もし、nfailがConfigurationで指定されている値以上になったら、-Cで指定されたアプリケーション(もしくはdefaultのblacklistd-helper)を呼び出し、Firewallにfilter設定を追加する)
 +
 +したがって、disable/Durationの値は慎重に検討する必要がある。これを短くとるか長く取るかで、利便性と件出力のバランスを取る必要がある。
 +</WRAP>
 +
 +<WRAP round tip>
 +==== 自分の接続元IPアドレスがblacklistに登録された ====
 +
 +現在 blocklistctl には登録されているアドレスを削除する機能が無い。(というよりdumpしかできない)
 +したがって、''pfctl -a * -t port25 -T delete [Address]''を実行して、一時的にTableから削除する必要がある。
 +
 +なお拒否期限までの時間が残ってる間は blacklistd を再起動する度にpfに登録される
 +</WRAP>
 +
 +<WRAP round info>
 +==== どうやって過去のAddressデータを保存しているのか? ====
 +
 +blacklistdは、''/var/db/blacklistd.db''にデータを記録している。このdbに記載されたデータをblacklistd起動時に読み出してpfに登録している。
 +
 +このblacklistd.dbはBerklay DBファイルで、''blacklistctl dump -a''でDataをdump できる。
 +</WRAP>
 +
 +<WRAP round Alert>
 +==== blacklistdのデータを複数台で共有 ====
 +
 +Blacklistdは、sourcecodeを見る限り、DBファイルに対してLockなどの操作を行っていないように見える。
 +したがって、これを見る限りでは、このDBのデータファイルを複数台のマシンで共有すると、Dataを破損する可能性があると考えられる。
 +</WRAP>
 +
 +===== 末尾に追加でおまけ =====
 +本文中におまけはあるんだけど、こっちにやられた時の顛末とかのおまけを記載します。
 +
 +==== 解析した内容 ====
 +全てはmaillogからの解析。
 +  * 攻撃は、常に、定常的に、いろいろなIP Addressから来ている。
 +  * ここ3ヶ月くらいのmaillog見る限り、毎日150回くらいのSMTP AUTH認証失敗logがある。
 +  * SRC IP Addressは割とバラバラ。みた感じ1画面に収まらなかったから、少なくとも50以上は攻撃元がある。
 +  * 1発SMTP AUTHに成功したら、outlook.comに試験メールを送信し、メール転送に成功することを確認。
 +  * 3時間後、10秒間で10通メールを転送しようとして弾かれる
 +  * その30分後、2分間で21通メールを転送しようとして弾かれる
 +  * さらにその20分後、2分間で21通メール転送しようとして弾かれる
 +  * 繰り返し
 +という挙動だった
 +
 +==== 雑感 ====
 +今回は、送付先のメールサーバーがSMTPUTF8受け付けなかったからエラーメールになって帰ってきて、だから気づけたけど、はっきり言って、これは運がよかっただけ。
 +転送失敗してなかったら気づかなかったまであるわけで。
 +
 +問題は、
 +  * 攻撃者は、長めの間隔で攻撃してくるから、見つけにくいし止めにくい
 +  * spam送信も30分程度に20通程度送るだけだから、弾かれにくい
 +  * ある程度(3時間程度か?)の間隔でmaillog解析して挙動確認しないとやられても気づけない
 +  * パスワードでの認証は何らかのきっかけで突破される
 +  * SMTP AUTHレベルで突破されたら、DKIMやらDMARCやらでは止められない
 +というあたりにある。
 +
 +結局、DKIMやDMARC、SPFは、いわゆるOpenRelayに近いメールへの対策としてはそれなりに効果はあるんだろうけど、パスワード抜かれたら終わりというのは変わらない。まぁ、パスワード認証を利用する限りそうなるのは自明なんだけど...。SMTP捨てられればいいんだけど、なかなかそういうわけにも行かないし。
 +
 +パスワード認証の代わりに個人証明書を使うといのはないわけじゃない。設定や利用に関しては、まぁ、面倒なだけでやれなくはないし。
 +ただ、この手を採用した場合の問題は、「個人証明書の発行」にある。
 +今時オレオレ証明書ってのは面倒が過ぎる(client側の設定も鯖側の設定も面倒)だし、自分がsubsidiary CAになるのは大変が過ぎるし...
 +でもまぁ、いうて、弱いパスワードが何よりも悪いのは事実だな。こいつをまず何とかするか。
 +
 +=== ちょっとだけ愚痴 ===
 +今回blacklistdを導入するにあたって、pfで対応するサンプルが非常に少なかった。なので、結局source codeまで見ないとわからないところがたくさんあった。
 +
 +FreeBSDにおけるFirewall実装を選ぶなら、スピードの点でipfwが一番良い。特にCiscoとかの昔ながらRouterでFirewall設定したことがある人はipfwの方がわかりやすいとおもう。ただ、筆者は今時のPacket Filter Firewallでは設定はある程度抽象化されていて欲しい。その意味で、OpenBSD由来のpfは非常に気に入っている。
 +
 +ただ、FreeBSDに移植されているpfは、OpenBSDのpfよりも常に古いのがやはり辛い。NAT64はOpenBSDならpfでできるんだけど、FreeBSDだとipfwでやるしか今のところないし。
 +
 +うーん、やっぱり色々難しいなぁ...
 +
 +
  
os/freebsd/blacklistd.1719862227.txt.gz · 最終更新: (外部編集)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki