Brute Force Attack対策+α(Firewalld)
20170807

パスワードやファイル名に対してのブルートフォースアタック、総当り攻撃、辞書攻撃及びhttpサーバへのディレクトリトラサーバル対策

SSH、FTP、MAIL、HTTP、およびDNSサーバのログを定期的に監視し、攻撃元IPをFirewalldの「Drop」ゾーンに登録していくスクリプトを自動実行させます。
攻撃を検知してから何らかの処置を取る事から、簡易的ではありますがIPSのような動作となります。
※攻撃元は動的なIPアドレスを利用されることが多いため、Firewalldの一時的なルールに追加するスクリプトとしております。
 そのため、システム再起動時には初期化されますが、登録期間が短ければ(未ログローテーション)再登録されます。
 2017/08/07 恒久的に登録する仕組みも追加

※iptablesをご利用の方は、Brute Force Attack対策(iptables)をご参照ください。
確認環境
 CentOS7.x
 CentOS6.x
 CentOS5.x
 Cron
 sshd
 vsftpd
 Postfix+Dovecot
 Apache
 BIND

頻繁にtmpファイルを作成する事から、「tmpfs(RAMディスク)」の設定も検討して下さい。
20160415 メールの強化と処理を大きく変更した。
20170807 根本的に処理を変更
◆処理の流れ(デフォルトの場合)
 1.SSH、FTP、MAIL、HTTP、およびDNSサーバのログをCronにて1分毎に監視する。

 2.累計10回の不正アクセスの痕跡を確認した場合はIPレベルでのアクセス拒否を実施する。
 →Firewalldの「Drop」ゾーンに登録
 →不正なアクセスのログが無ければ処理の終了
 →ホワイトリストファイルにIPアドレスを1行ずつ記述する事で監視除外IPの指定が可能
 →HTTP監視においては、存在しないディレクトリ及びファイルの参照を試みる行為を監視します。
 →また、HTTP監視においては誤検知を減らす工夫が必要となる為、除外ディレクトリの指定が可能

 3.不正アクセスの痕跡を確認し、対処した事場合は詳細をログに保存

 4.上記内容をroot宛にメールで送信

◆監視ログメッセージ
 SSH
  "Invalid user"
  "Failed password"

 FTP
  "FAIL LOGIN"

 MAIL
  "authentication failure"
  "auth failed"
  "Recipient address rejected: User unknown in local recipient table"
  "SSL_accept error"
  "Connection concurrency limit exceeded"
  "NOQUEUE: reject:"
  "policyd-spf"

 HTTP
  "File does not exist"
  "script" & "not found or unable to stat"
  "../"
  "//"
  "//" ←HTTP通信時
  "404" ←HTTPS通信時

 DNS
  "denied"
◆まずはシェルスクリプトの作成
 [root@Server ~]# vi /root/brute_force_attack.sh

#!/bin/sh
export LANG=ja_JP.UTF-8

# Ver 20170807

# 攻撃判断回数(デフォルト10回以上)
NG="10"

# <テスト機能>回数の多い攻撃を恒久的に遮断する
# 「yes」で有効(デフォルト有効)
PNG="yes"
# 攻撃判断回数(デフォルト100回以上)
PNGL="100"

# そもそも遮断するIPは恒久的に設定する
# 「yes」で有効。(デフォルト無効)
PER="no"

# ログ監視の有効化設定 「no」で無効。(デフォルトは全て有効)
TELNET_CHECK="yes"
SSH_CHECK="yes"
FTP_CHECK="yes"
MAIL_CHECK="yes"
HTTP_CHECK="yes"
DNS_CHECK="yes"

# 監視ログファイル
# TelnetとSSHに利用
SSH_LOG="/var/log/secure"
# FTPに利用
FTP_LOG="/var/log/vsftpd.log"
# Mailに利用
MAIL_LOG="/var/log/maillog"
# HTTPに利用
HTTP_LOG="/var/log/httpd/error_log"
HTTP_LOG2="/var/log/httpd/access_log"
HTTPS_LOG="/var/log/httpd/ssl_access_log"
# DNSに利用
DNS_LOG="/var/log/named.log"

# HTTP監視除外ディレクトリ及びファイル設定(デフォルトでは「/home」ディレクトリ以下に存在するファイル全て)
DIR="/home"

# 攻撃を検知した際にメールを送信するか否か。「no」で無効。(デフォルト有効)
MAILMESSAGES="yes"
# メール送信先アドレス。(デフォルトroot宛)
MAILADDRESS="root"

# 動作ログのファイル名
BR_LOG="/var/log/brute_force_attack.log"

# Firewalldログのファイル名(エラー防止)
ERR_LOG="/var/log/firewalld"
ERR_LOG2="/var/log/messages"

# ホワイトリストファイルの指定
WHITE_LIST="/root/sh/allow_ip"

# 一時ファイル作成ディレクトリの指定
TMP_DIR="/tmp"

# 以降設定不要

# 一時ファイル設定
TMP="$TMP_DIR/brute_force_attack.tmp"
TMP2="$TMP_DIR/brute_force_attack2.tmp"
TMP3="$TMP_DIR/brute_force_attack3.tmp"

# 恒久フラグ
FLAG=0

function_CORE()
{
if [ $1 = "TELNET" ] ; then
grep "login:" $SSH_LOG | grep "FAILED LOGIN" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
MSGLOG="$SSH_LOG"
elif [ $1 = "SSH" ] ; then
grep "sshd" $SSH_LOG | grep "Invalid user" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "sshd" $SSH_LOG | grep "Failed password" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
MSGLOG="$SSH_LOG"
elif [ $1 = "FTP" ] ; then
grep "FAIL LOGIN" $FTP_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
MSGLOG="$FTP_LOG"
elif [ $1 = "MAIL" ] ; then
grep "authentication failure" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "auth failed" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "Recipient address rejected: User unknown in local recipient table" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "SSL_accept error" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "Connection concurrency limit exceeded" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "NOQUEUE: reject:" $MAIL_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
# 下記、SPF導入済みのログ
grep "policyd-spf" $MAIL_LOG | grep "Fail" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
MSGLOG="$MAIL_LOG"
elif [ $1 = "HTTP" ] ; then
DIR_LIST=`ls $DIR`
grep "error" $HTTP_LOG | grep "File does not exist" | grep -v "$DIR_LIST" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
grep "error" $HTTP_LOG | grep "script" | grep "not found or unable to stat" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "\.\./" $HTTP_LOG2 | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep "//" $HTTP_LOG2 | grep -v http:// | grep -v https:// | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
grep " 404 " $HTTPS_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP
MSGLOG="$HTTP_LOG"
MSGLOG2="$HTTP_LOG2"
elif [ $1 = "DNS" ] ; then
grep "denied" $DNS_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" > $TMP
MSGLOG="$DNS_LOG"
else
:
fi

# ホワイトリスト処理、重複アドレス処理、エラー防止処理、重複防止処理
echo "" > $TMP2
if [ -s $WHITE_LIST ]; then
cat $WHITE_LIST >> $TMP2
fi
if [ -s $ERR_LOG ]; then
grep "ERROR" $ERR_LOG | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP2
fi
if [ -s $ERR_LOG2 ]; then
grep "firewalld" $ERR_LOG2 | grep "ERROR" | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP2
fi
firewall-cmd --list-all-zones | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP2
firewall-cmd --list-all-zones --permanent | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+" >> $TMP2

IPLIST=`cat $TMP2`
for IP in $IPLIST
do
usleep 50000
grep -v "$IP" $TMP > $TMP3
cp -pr $TMP3 $TMP
done
echo "" > $TMP2
echo "" > $TMP3
usleep 50000

LIST=`cat $TMP`
for IP in $LIST
do
usleep 50000
COUNT=`grep -w "$IP" $TMP | wc -l`
if [ $COUNT -ge $NG ] ; then
if grep -w $IP $TMP2 >/dev/null 2>&1; then
:
elif grep -w $IP $BR_LOG >/dev/null 2>&1; then
if [ $PER = "yes" -o $COUNT -ge $PNGL -a $PNG = "yes" ] ; then
firewall-cmd --permanent --zone=drop --add-source=$IP >/dev/null 2>&1
FLAG="1"
REG="恒久的に登録"
else
firewall-cmd --zone=drop --add-source=$IP >/dev/null 2>&1
REG="一時的に登録"
fi
LOG_DATE=`date '+%Y/%m/%d %k:%M:%S'`
echo "["$LOG_DATE"] ""[$1ログ監視] IPをFirewallのDrop-Zoneへ再登録("$REG"):"$COUNT "回 ["$IP"]" >> $BR_LOG
echo $IP >> $TMP2
else
if [ $PER = "yes" -o $COUNT -ge $PNGL -a $PNG = "yes" ] ; then
firewall-cmd --permanent --zone=drop --add-source=$IP >/dev/null 2>&1
FLAG="1"
REG="恒久的に登録"
else
firewall-cmd --zone=drop --add-source=$IP >/dev/null 2>&1
REG="一時的に登録"
fi
LOG_DATE=`date '+%Y/%m/%d %k:%M:%S'`
echo "["$LOG_DATE"] ""[$1ログ監視] IPをFirewallのDrop-Zoneへ"$REG":"$COUNT "回 ["$IP"]" >> $BR_LOG
echo $IP >> $TMP2
if [ $MAILMESSAGES = "yes" ] ; then
if [ $1 = "HTTP" ] ; then
cat "$MSGLOG" > $TMP3
cat "$MSGLOG2" >> $TMP3
MSG=`grep -w "$IP" $TMP3`
else
MSG=`grep -w "$IP" $MSGLOG`
fi
echo -e "[$1]ログ監視にて、"$COUNT"回の不正なアクセスログを確認。\n対象ホストのIPアドレスをFirewallのDrop-Zoneへ"$REG"しました。\n\nIPアドレス [$IP]\n\n$MSG"| mail -s "$0" $MAILADDRESS
fi
fi
fi
done
}

# 実行
if [ $TELNET_CHECK = "yes" ] ; then
function_CORE TELNET
usleep 50000
fi
if [ $SSH_CHECK = "yes" ] ; then
function_CORE SSH
usleep 50000
fi
if [ $FTP_CHECK = "yes" ] ; then
function_CORE FTP
usleep 50000
fi
if [ $MAIL_CHECK = "yes" ] ; then
function_CORE MAIL
usleep 50000
fi
if [ $HTTP_CHECK = "yes" ] ; then
function_CORE HTTP
usleep 50000
fi
if [ $DNS_CHECK = "yes" ] ; then
function_CORE DNS
usleep 50000
fi

# 一時ファイル削除
rm -f $TMP $TMP2 $TMP3
# 恒久フラグ管理
if [ $FLAG -eq 1 ] ; then
firewall-cmd --reload
fi

◆一応実行権限を付与
 [root@Server ~]# chmod +x /root/brute_force_attack.sh
◆cronにて自動実行するよう設定しましょう。
 [root@Server ~]# echo "*/1 * * * * root bash /root/brute_force_attack.sh >/dev/null 2>&1" >> /etc/crontab

この場合、1分毎に監視して対処します。
変更する場合はcrontabにて設定を確認して下さい。

終わり。