攻撃者の報告を自動化する:AbuseIPDB APIとcowrieの統合
前回の記事でcowrieのハニーポットに罠を仕掛け、攻撃者を観察し始めた。毎日攻撃IPをAbuseIPDBに手動で報告していたが、さすがに面倒になってきた。scpでダウンロードして、ブラウザでアップロードして、報告済みリストを更新して、CSVを削除する。6ステップを毎日繰り返すのは学びがない。自動化する時が来た。
Hetznerサーバーを借りた理由をあらためて考える
自動化の話の前に少し脇道に逸れる。
今日bashのコースをやっていてSIGINFOというシグナルがmacOSでしか動かないことに気づいた。Linuxには存在しないシグナルだ。BSDとLinuxはPOSIXという共通の仕様書を元に実装した別の処理系で、互いに独自拡張を持っている。SIGINFOはBSD固有、SIGPWRはLinux固有といった具合だ。
このことを考えていてHetznerを借りた理由を再確認した。
Macは便利だがGUIのためにリソースを大量に消費する。btopで見比べると一目瞭然だ。
| Hetzner(Linux) | Mac(M2) | |
|---|---|---|
| CPU使用率 | 1% | 6〜16% |
| メモリ使用 | 557 MiB / 3.73 GiB(15%) | 3.49 GiB / 8 GiB(44%) |
| プロセス数 | 122 | 621 |
MacのWindowServerだけでかなりのメモリを持っていく。mediaanaというプロセスはPhotosのAI機能のためにバックグラウンドで機械学習を回している。Macは「全部いい感じにしてあげる」OSなので仕方ない。
Hetznerのサーバーはsshd、fail2ban、systemdなど本当に必要なものしか動いていない。Mac側のリソースを食わずに開発環境を完全分離できる。月額数百円でEPYCサーバーが使えて通信量は20TB。個人で使い切るのはまず無理な量だ。しかもIPレピュテーションは使い続けるほど積み上がっていく。
check_new_attackers.shの問題点
報告スクリプトは以前から動いていたが、今日見直すと問題があった。
ログソースがauth.logだけだった。
# 以前の実装(問題あり)
auth_ips=$(sudo grep "Invalid user\|Connection closed" "$AUTH_LOG" | \
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | \
sort -u)
これでは本物のSSH(XXXX番)への攻撃しか拾えない。cowrieのハニーポット(XX番)で累計7000パケット超を受けているのに、そのデータが報告に全く使われていなかった。しかも回数も時刻も取っていないので、「何回攻撃したか」という情報がコメントに入らない状態だった。
cowrie.jsonを統合する
cowrieのログはenokiユーザーから直接読めない。
ls -la /home/cowrie/cowrie/var/log/cowrie/
# Permission denied
cowrieグループに本番ユーザーを追加する方法もあるが、攻撃者が本番ユーザーに侵入したときにハニーポットの存在がバレる。なのでsudoで読む方針にした。
cowrie.jsonから必要な情報が取れるか確認した。
sudo cat /home/cowrie/cowrie/var/log/cowrie/cowrie.json | \
jq -r 'select(.eventid == "cowrie.session.connect") | .src_ip' | \
sort | uniq -c | sort -rn | head -10
174 120.26.194.219
152 46.101.80.205
125 159.223.49.170
89 138.197.182.192
85 176.120.22.47
回数も時刻も取れた。これをauth.logと統合する。
スクリプトの改善
改善のポイントは3つだった。
1. ログソースを両方に拡張
auth.logとcowrie.jsonの両方からIPを抽出して統合する。cowrieからは回数と最初の攻撃時刻も取得する。
# cowrie.jsonからIP・回数・最初の攻撃時刻を抽出
cowrie_data=$(sudo jq -r \
'select(.eventid == "cowrie.session.connect") | [.timestamp, .src_ip] | @tsv' \
"$COWRIE_LOG")
# 全IPを統合してユニーク化
all_ips=$(
echo "$auth_ips"
echo "$cowrie_data" | awk '{print $2}'
)
all_ips=$(echo "$all_ips" | sort -u)
2. フィルタリングの強化
テスト中に127.0.0.1がcowrie.jsonに記録されているのを発見した。ローカルホストを報告するのは恥ずかしい。プライベートIPを除外するフィルターを追加した。
if echo "$ip" | grep -qE \
'^(127\.|10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|0\.|169\.254\.)'; then
continue
fi
さらにCensysやShodanといったセキュリティリサーチャーはorg名とASN番号で除外するようにした。これらは攻撃者ではなくスキャナーなので報告しない。
if echo "$org" | grep -qiE "Censys|Shodan|AS398324|AS398722|AS20473"; then
echo "除外(リサーチャー): $ip | $org"
continue
fi
3. 回数の単複対応
細かいが1 attemptsは英語として変なので直した。
attempts_word=$([ "$total" -eq 1 ] && echo "attempt" || echo "attempts")
これで1 attempt、174 attemptsと正しく出力される。
AbuseIPDB APIで完全自動化
AbuseIPDBのbulk-report APIは無料アカウントで1日1,000件まで使える。APIキーをアカウントページで発行して安全に保存した。
echo "ABUSEIPDB_API_KEY=xxxxxxxxxxxx" > ~/.abuseipdb
chmod 600 ~/.abuseipdb
スクリプトの最後にAPI報告を組み込んだ。
source "$HOME/.abuseipdb"
response=$(curl -s -X POST https://api.abuseipdb.com/api/v2/bulk-report \
-H "Key: $ABUSEIPDB_API_KEY" \
-H "Accept: application/json" \
-F "csv=@$OUTPUT")
success=$(echo "$response" | jq -r '.data.savedReports // 0')
if [ "$success" -gt 0 ]; then
~/projects/update_reported.sh
rm "$OUTPUT"
fi
報告が成功したら自動でreported_ips.txtを更新してCSVを削除する。失敗した場合はCSVを残してデバッグできるようにした。
結果
以前は6ステップあった手動作業がこれだけになった。
~/projects/check_new_attackers.sh
実行すると今日の攻撃者が一覧表示される。
未報告: 120.26.194.219 → CN | Hangzhou | AS37963 Hangzhou Alibaba Advertising Co.,Ltd. | 174回
未報告: 46.101.80.205 → GB | London | AS14061 DigitalOcean, LLC | 152回
未報告: 159.223.49.170 → SG | Singapore | AS14061 DigitalOcean, LLC | 125回
除外(リサーチャー): 162.142.125.208 | AS398324 Censys, Inc.
除外: 167.94.146.60
...
=== 報告成功: 21件 ===
回数・時刻・org情報が揃った状態でAbuseIPDBに報告される。Hetznerのネットワーク健全性に貢献しながら、自分のIPレピュテーションも守れる。学習コストをそのままコミュニティへの貢献に変換できている。
次はCMDが記録されるのを待つ。攻撃者がhoneyfsの偽ファイルを開いた瞬間に何をしようとしていたか全部わかる。