目次
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
に
Jun 9 15:11:24 mta postfix/smtpd[95847]: bl_init: connect failed for `/var/run/blacklistd.sock' (No such file or directory)
のように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は以下の通り。
- 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
ポイントは、最後にあるanchor “blacklistd/*” in on $IFextn
である。
この行が意味するのは、
- Interface $IFextn (xn1)において、
- Incomming(外から入ってくる通信)に対して、
- blacklistdが作成するAnchorルールを適用する
ということになる。
pfの起動
ここまで設定したら、/etc/rc.confに
# pf pf_enable="YES" pflog_enable="YES"
を記載し、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のサンプル
# $FreeBSD$ # # Blacklist rule # adr/mask:port type proto owner name nfail disable [local] ssh stream * * * 3 24h smtp stream * * * 3 24h smtps stream * * * 3 24h submission stream * * * 3 24h imaps stream * * * 3 24h * * * * * 3 60 # WhiteLists # adr/mask:port type proto owner name nfail disable [remote] # Never block localhost 127.0.0.1/32 * * * * * * [::1]/128 * * * * * *
blacklistdの起動
ここまで設定が終わったら、Blacklistdを起動する。
- まず、/etc/rc.confに以下の設定を追加する
blacklistd_enable="YES" blacklistd_flags="-r"
- -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設定があることを確認する
- 今回の例では、以下が表示されるはず。
anchor "blacklistd/*" in on xn1 all
pfctl -a “*” -sr
- Rulesetに記載された全てのAnchorを含むRulesetを確認
- Anchor行が展開される
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 } }
pfctl -a “blacklistd/25” -sr
とpfctl -a “blacklistd/587” -sr
- Anchor行に“25”と“587”があるので、それぞれのRuleを確認
# 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
- それぞれのTableを確認
- Configを見れば自明だが、一応コマンドで取得
- scriptにするときに必要
# pfctl -a "blacklistd/25" -sT port25 # pfctl -a "blacklistd/587" -sT port587
pfctl -a “blacklistd/587” -t port25 -Ts
とpfctl -a “blacklistd/587” -t port587 -Ts
- それぞれのTableの内容を確認
# 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
blacklistctl dump -a
- Blacklistに載っている全てのエントリを表示
# blacklistctl dump -a address/ma:port id nfail last access 185.208.158.235/32:25 2/3 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/3 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
blacklistctl dump -b
- Blacklistに載っているのエントリのうち、Blockするものだけを表示
# 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
blacklistctl dump -r
- Blacklistに載っている「まだblockするほどではない」リストを表示
# blacklistctl dump -r address/ma:port id nfail remaining time 185.208.158.235/32:25 2/3 22h43m15s 93.123.39.205/32:25 1/3 23h40m42s
おまけ
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対応になる。
Signal
- Debug Levelを変化させる方法
- blacklistdに対して SIG_USR1を送りつけると、debugレベルが上がる
- blacklistdに対して SIG_USR2を送りつけると、debugレベルが下がる
- 設定を再読み込みさせる方法
- blacklistdに対して、SIG_HUPを送りつける
- 終了させる方法
- blacklistdに対して、SIG_KILL / SIG_INT / SIGTERMを送りつける
落穂拾い
name
nameフィールドは、使用するパケットフィルタールールの名前である。
名前が “-” で始まる場合、デフォルトのルール名がその名前の前に付加される。
名前に “/” が含まれる場合、名前の残りの部分はルールで指定されたアドレスに適用されるマスクとして解釈され、1つのルール違反で設定されたプレフィックス全体のサブネットがブロックされる
nameに/29
と書くと、そのRuleでMatchしたSrcAddressに対して/29
のNetmaskを付加した上で、その範囲のアドレスがBlockされるという挙動になる。
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の値は慎重に検討する必要がある。これを短くとるか長く取るかで、利便性と件出力のバランスを取る必要がある。
自分の接続元IPアドレスがblacklistに登録された
現在 blocklistctl には登録されているアドレスを削除する機能が無い。(というよりdumpしかできない)
したがって、pfctl -a * -t port25 -T delete [Address]
を実行して、一時的にTableから削除する必要がある。
なお拒否期限までの時間が残ってる間は blacklistd を再起動する度にpfに登録される
どうやって過去のAddressデータを保存しているのか?
blacklistdは、/var/db/blacklistd.db
にデータを記録している。このdbに記載されたデータをblacklistd起動時に読み出してpfに登録している。
このblacklistd.dbはBerklay DBファイルで、blacklistctl dump -a
でDataをdump できる。
blacklistdのデータを複数台で共有
Blacklistdは、sourcecodeを見る限り、DBファイルに対してLockなどの操作を行っていないように見える。 したがって、これを見る限りでは、このDBのデータファイルを複数台のマシンで共有すると、Dataを破損する可能性があると考えられる。
末尾に追加でおまけ
本文中におまけはあるんだけど、こっちにやられた時の顛末とかのおまけを記載します。
解析した内容
全ては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でやるしか今のところないし。
うーん、やっぱり色々難しいなぁ…