~ To be, or not to be, or to forget all. ~ null-i.net |
Linux/静的IPフィルタの作成 | |
アクセス失敗履歴をもとにIPアドレスフィルター(2017-03-05、追記 2019-06-07) 出入り禁止のIPフィルター、出禁(できん)リストを作るのが今回の趣旨です。*1
掲示板cgi(既に閉鎖済み) へのアクセスが、少し煩わしいというか気持ち悪いのです。 IPで止めていいのか?†本来なら、同一IPアドレスに複数の端末が紐づいているので そういう製品買おうよ?†さらに、これをマメに確認し更新し続けなければならないので、
フィルタする対象の確認†まずサーバの「入り口」を確認しておきます。 # netstat -a Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State (結果は抜粋しています、もっと多いはずです) tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN tcp6 0 0 [::]:http [::]:* LISTEN tcp6 0 0 [::]:ssh [::]:* LISTEN netstat でポートを探します、 上の例だと ssh(=22)とhttp(=80)の2つがこのサーバへの入り口です。 今回は、Webサイト、sshログイン、メールの3つをターゲットにします。 Webへの不正アクセスの集計と、IPフィルタの作成†特定のキーワードに対して、件数と日付を調べます。 # ./black_url.sh 0201 ----------------- 4xx xxx.254.236.32 : 6 4xx xx.82.64.21 : 2 4xx xxx.25.166.199 : 1 4xx xx.82.65.21 : 1 4xx xxx.96.249.42 : 1 spm xxx.189.235.253 : 1 0202 ----------------- 4xx xxx.56.47.199 : 1 4xx xxx.22.126.147 : 1 (中略) xxx.14.194.226 :06days xxx.96.249.42 :09days xxx.189.235.253 :12days セキュリティホール狙いの404 NotFound や # cat black_url.sh #!/bin/sh # アクセスログの場所を指定し、 # 今月の今日までのログを読み込む ACLOG=/var/log/httpd/access_log* mm=`export LANG=C && date +%b` # 日本語になっちゃったからLANG=Cにする # mm=`date +%m` # うちの場合は、月を文字(Feb)でなく数値(02)で表示している。 min=1 max=`date +%-d` # 月日指定する場合は以下をいじる #mm=Feb #min=`date +%d --date "2 day ago"` #max=$min while [ $min -le $max ]; do ii=`printf '%02d' $min` echo "$mm$ii -----------------" # 一行目の mm ii の部分はアクセスログの書式(日付表示)に合わせて整形する # -> "Feb 01" のような表示か、 "02/01" のような表示か、など # 二行目で、アクセス禁止のトリガにするキーワードを選ぶ # -> 以下の例は2017/02 で比較的多かったもの。もっと沢山あるかも。 egrep "$mm $ii" $ACLOG | \ egrep -i "/mysql/admin|/phpmyadmin|/setup.php/|/wp-admin/|/upbbs.cgi" |\ awk ' { # アクセスログに合わせて$数値は変える。以下の場合は、 # $2 番目がIPアドレスで、 # $4 番目がレスポンスコード の場合 if($0 ~ /upbbs.cgi/ && $4 ~ /[45][0-9][0-9]/){ # ここだけ別カウント。 # 今は存在しない掲示板。これは別カウントして確実に止める。 host_list_spam[$2]++ } else if($4 ~ /4[0-9][0-9]/){ host_list_400[$2]++ } else if($4 ~ /5[0-9][0-9]/){ host_list_500[$2]++ } } END{ # 配列 host_list の # 添え字i は IPアドレス、中身はアクセス数 になる for (i in host_list_400) { printf(" 4xx %16s : %d\n", i, host_list_400[i]); } for (i in host_list_500) { printf(" 5xx %16s : %d\n", i, host_list_500[i]); } for (i in host_list_spam) { printf(" spm %16s : %d\n", i, host_list_spam[i]); } }' | tee $0.tmp awk '{print $2;}' $0.tmp | sort -u > ng_list$mm$ii.txt \rm $0.tmp min=`expr $min + 1 ` done # 過去何回アクセスしてきているかを調べる awk '{ a[$1]++; } END{ for (i in a){ printf("%16s :%02ddays\n", i, a[i]); } }' ng_list$mm* |\ sort -k2 |\ tee ng_list_days.txt 作成された ng_list*.txt で、再犯率に応じてリストをピックアップしましょう。 当時は、 参考まで、 まず、以前入れたものと総入れ替えすることにしたので、 # for ff in `firewall-cmd --list-sources --zone=drop`; do echo $ff------ firewall-cmd --zone=drop --permanent --remove-source=$ff done # firewall-cmd --reload # firewall-cmd --list-sources --zone=drop 今回のリストをすべて入れます。 # for ff in `cat black_hosts_1702.txt`; do echo $ff------- firewall-cmd --zone=drop --permanent --add-source=$ff done # firewall-cmd --reload # firewall-cmd --list-sources --zone=drop 効果のほどは・・・ ログイン失敗履歴(btmp/lastb)の集計†以下、ログインIDと、IPアドレスごとに失敗数を数えています。 # cat black_login.sh #!/bin/sh # btmpの実行結果から # 今月の今日までのログを読み込む mm=`export LANG=C && date +%b` min=1 max=`date +%-d` # 月日指定する場合は以下をいじる。 #mm="Feb" #min=1 #max=28 while [ $min -le $max ]; do ii=`printf '%2d' $min` echo "$mm$ii -----------------" # ローテ済みのログを見る場合は、 -f ファイル名で指定する。 #lastb -i -f /var/log/btmp-* |\ lastb -i |\ egrep "$mm $ii" | \ awk ' { if($0 != "" && $1 !~ /btmp/){ # 空行とログの最後の記述は除外。 host_list[$3]++ name_list[$1]++ } } END{ # 配列 host_list, name_list の # 添え字i は IPアドレス/ログイン名、中身はアクセス数 になる for (i in host_list) { printf(" %16s : %d\n", i, host_list[i]); } for (i in name_list) { printf(" name %10s : %d\n", i, name_list[i]); } }' | tee btmp_list$mm$min.txt min=`expr $min + 1 ` done # 過去何回アクセスしてきているかを調べる echo "----- total count [login]" awk '{ if($1 ~ /name/){ a[$2] += $4; } } END{ for (i in a){ printf("%16s %4d\n", i, a[i]); } }' btmp_list$mm* |\ sort -k2 echo "----- total count [host]" # 統計の出力 awk '{ if($1 ~ /name/){ # nothing } else { a[$1]++; } } END{ for (i in a){ printf("%16s :%02ddays\n", i, a[i]); } }' btmp_list$mm* |\ sort -k2 |\ tee btmp_list_days.txt メール履歴(postfixなど)の集計†ログがフリーフォーマットなので、手探りで拾っています。 # cat black_mail.sh #!/bin/sh # アクセスログの場所を指定し、 # 今月の今日までのログを読み込む MLLOG=/var/log/maillog* mm=`export LANG=C && date +%b` # 日本語になっちゃったからLANG=Cにする min=1 max=`date +%-d` # 月日指定する場合は以下をいじる #mm=Feb #min=`date +%-d --date "2 day ago"` min=1 #max=28 while [ $min -le $max ]; do ii=`printf '%2d' $min` echo "$mm$ii -----------------" # 一行目の mm ii の部分はアクセスログの書式に合わせて整形する # 二行目で、アクセス禁止のトリガにするキーワードを選ぶ egrep "^$mm $ii" $MLLOG | \ egrep -i "fail|err" |\ awk ' { if($0 ~ /auth.+fail/ && match($0, /\[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\]/) ){ # 認証失敗のログから(auth.+fail) # matchでIPアドレスを抜き出す ([IPアドレス]) host_list_auth[substr($0, RSTART+1, RLENGTH-2)]++ } else if($0 ~ /fail/){ # IPアドレスが拾えない場合は行番号NRで文面を拾う。 msg_list_fail[NR]=$0 } else { # まだ想定書式がないので、とりあえず行番号NRで文面を拾う。 msg_list_err[NR]=$0 } } END{ for (i in host_list_auth) { printf(" badIP %16s : %d\n", i, host_list_auth[i]); } for (i in msg_list_fail) { printf(" fail %s\n", msg_list_fail[i]); } for (i in msg_list_err) { printf(" err %s\n", msg_list_err[i]); } }' | tee $0.tmp grep badIP $0.tmp | awk '{print $2;}' | sort -u > bad_mail$mm$ii.txt \rm $0.tmp min=`expr $min + 1 ` done # 過去何回アクセスしてきているかを調べる awk '{ a[$1]++; } END{ for (i in a){ printf("%16s :%02ddays\n", i, a[i]); } }' bad_mail$mm* |\ sort -k2 |\ tee bad_mail_days.txt やってみた感想†以上のような履歴をもとにしたIPアドレスフィルターですが、 攻撃自体は公開しているサーバなら必ず来るものなので、
もう、そうなると何を信じればいいのやら。 IoTだの、ネットワーク化が更に進めば、 |
|