Snortのアラート検出時のログを見たい
SnortをIDSエンジンにしたBaseではいろんな角度からアラートを統計表示してくれるのでとても便利ですが、アラートが出た時点でなにが起きたのかを知りたくて、コマンドを叩いてログを検索することがたまにあります。しかし出先のスマホからはそれができない(できるけどやりたくないw)ので歯がゆい思いをすることがしばしば。いっそのことBaseからそれをやってしまおう(といってもリンクしてるだけですが)と思って作ったのがこのスクリプトです。SnortやBaseのインストールに関しては侵入検知ソフトSnortと周辺ツールをインストールするをご覧ください。
BaseのTimestampに表示されている時分秒に合致するログを/var/log/から根こそぎ拾ってきて左画像のように表示します。Baseのソースを熟読する時間的余裕が無いのでまったくのスタンドアロンとしました。ですのでBase本体のように体裁良く表示するわけではありません。
また、ログのパーミッションを甘くしたりとか、ツールを新たにインストールすることはせずに簡単に設置可能であることを目標としました。あくまでネットワークに繋がったPCの無い状況での簡易確認用途としてスマホから使うのが主な目的です。
大量のアクセスおよびログが吐かれるサーバでは検索条件を新たに加えなければ使い物にならないかもしれません。
一応ピックアップしたログの中でSource AddressとDest. Addressがハイライト表示して見やすくはなっています。ちなみにBase側の変更箇所はリンクの記述を追加する一ヶ所だけです。
主なファイル構成
Baseを設置したディレクトリ(/var/www/base)にfind_logディレクトリを作って以下のファイルを作成します。
- cache/ (キャッシュディレクトリ)
- del_exp.sh (期限切れキャッシュファイルを削除する)
- grep_log_dt.sh (指定された日時のログファイルを検索する)
- grep_result.txt (検索した結果を一時的に保存するファイル)
- index.php (ログ表示ページ)
- jquery-2.0.2.min.js (jquery、適当なやつをダウンロード)
- target_datetime (タイムスタンプを一時保存するファイル)
処理の流れ
BaseのTimestampのリンクをクリックする。 ↓ find_log/index.php キャッシュファイルがなかったらタイムスタンプをtarget_datetimeに書き込んで、grep_log_dt.shがキャッシュファイルを生成するのを待つ(指定秒間隔のリフレッシュ)。キャッシュファイルが既にあったらそれを表示する。 ↓ find_log/grep_log_dt.sh 定時実行してtarget_datetimeのタイムスタンプを読み込んで、ログファイルを検索、結果をキャッシュファイルに書き込む。target_datetimeに時刻が書き込まれていなかったら終了する。
Baseの改造
Baseディレクトリ直下のbase_qry_sqlcalls.phpの225行目あたり
qroPrintEntry($myrow[3]); ↓ qroPrintEntry("<a href='./find_log/index.php?timestamp=$myrow[3]&sip=$current_sip&dip=$current_dip'>".$myrow[3]."</a>");
cache/
キャッシュディレクトリにはBaseのタイムスタンプに紐づくキャッシュファイルを作成します。命名規則は、例えば"2013-12-31 23:59:59"のスペースの部分を'_'(アンダースコア)に置換した"2013-12-31_23:59:59"とする。
del_exp.sh
機能:キャッシュファイルの生成日から指定日数を過ぎたものを削除します。
crontabに
0 0 * * * /var/www/base/find_log/del_exp.sh
を記述して毎日実行。
#!/bin/bash
export LANG=en
CACHE_DIR="/var/www/base/find_log/cache/"
LIMIT=`date '+%s' -d '30 days ago'`
for FILE in `find ${CACHE_DIR} -type f | \
grep -e "[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}_[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}"`
do
if [ `date '+%s' -r ${FILE}` -lt ${LIMIT} ]; then
rm -f ${FILE}
fi
done
exit 0
grep_log_dt.sh
主な機能:各種のログの日付フォーマットでログファイルを検索して、合致する行をピックアップしてhtmlタグをつけてキャッシュファイルに書き出します。
検索対象日は、index.phpがtarget_datetimeファイルに書き出した日付をピックアップします。そのためにこのスクリプトを一定時間毎に走らせて、日付が書き出されたか常に監視します。とりあえず5秒間隔で動作するように、crontabを指定秒ごとに実行するで書いたような方法で記述しています。なにもしなければほとんど負荷はかからないのでさらに短い秒数でもいいかもしれません。ただcrontabにあの記述をづらずら書くのが気が引けるのと、スマホからのアクセスで大量に短時間に処理したいわけでもないので5秒間隔で充分かなと…。
#!/bin/bash export LANG=en # "YYYY-MM-DD hh:mm:ss"の19の文字列を取得する DT_FILE="/var/www/base/find_log/target_datetime" DT=`cut -c-19 ${DT_FILE}` SIP=`cut -d' ' -f3 ${DT_FILE}` DIP=`cut -d' ' -f4 ${DT_FILE}` :> ${DT_FILE} # ターゲットの日時の文字列がなかったら終了する if [ ${#DT} -ne 19 ]; then exit 0 fi # キャッシュファイルディレクトリ CACHE_DIR="/var/www/base/find_log/cache/" # キャッシュファイル名はスペースに_を入れて"YYYY-MM-DD_hh:mm:ss"とする CACHE_FILE=`echo ${DT} | sed -e "s/\s/_/"` # キャッシュディレクトリに既存のファイルがあるか検索する EXIST_FILE=`find "${CACHE_DIR}" -name "${CACHE_FILE}"` # 同じファイルがキャッシュにあったら処理を終了する if [ ${#EXIST_FILE} -ne 0 ]; then exit 0 fi # ログファイルディレクトリ LOGDIR="/var/log/" # grepした結果の一時保存ファイル RES_FILE="/var/www/base/find_log/grep_result.txt" # messages maillog 他 のフォーマット(冒頭に日付があって西暦がない) *********** # e.g. "Jun 2 06:12:13" FORMAT_1=`date -d "${DT}" "+%b %e %T"` # apache access_log ssl_access_log ssl_request_log のフォーマット************* # e.g. "08/Jun/2013:06:22:57" FORMAT_2=`date -d "${DT}" "+%d/%b/%Y:%T"` # apache error_log ssl_error_log のフォーマット******************************* # e.g. "Wed Jun 19 10:20:44 2013" FORMAT_3=`date -d "${DT}" "+%a %b %d %T %Y"` # ↓ 検索文字列の先頭に"^"をつけてFORMAT_3にかぶらないようにする grep -r "^${FORMAT_1}" "${LOGDIR}" >> ${RES_FILE} grep -r "${FORMAT_2}" "${LOGDIR}" >> ${RES_FILE} grep -r "${FORMAT_3}" "${LOGDIR}" >> ${RES_FILE} CACHE_FILE=`echo ${DT} | sed -e "s/\s/_/"` # ホスト名を取得する SNM=`nslookup "${SIP}" | sed -n "s/.*\(in-addr.arpa\tname = \)\(.*\)/\2/p"` DNM=`nslookup "${DIP}" | sed -n "s/.*\(in-addr.arpa\tname = \)\(.*\)/\2/p"` # IPアドレスとホスト名を書き込む TBL="<table class='iptbl'>" TBL=${TBL}"<tr>" TBL=${TBL}"<td class='title'>Source Address</td><td>${SIP}</td><td>:</td><td>${SNM}</td>" TBL=${TBL}"</tr>" TBL=${TBL}"<tr>" TBL=${TBL}"<td class='title'>Dest. Address</td><td>${DIP}</td><td>:</td><td>${DNM}</td>" TBL=${TBL}"</tr>" TBL=${TBL}"</table>" echo "${TBL}" > ${CACHE_DIR}${CACHE_FILE} # 検索結果が得られなかったらメッセージ"No contents"を書き込む RES=`cat ${RES_FILE}` if [ ${#RES} -eq 0 ]; then echo "No contents" >> ${RES_FILE} fi # タグ(ログファイル名にdt 内容にdd)を埋め込んでキャッシュファイルに書き込む while read line do #echo ${line} | sed -e "s/\(^[^:]\+:\)\(.*$\)/<dt>\1<\/dt><dd>\2<\/dd>/g" >> ${CACHE_DIR}${CACHE_FILE} echo ${line} | sed -e "s/${SIP}/<span class='sip'>${SIP}<\/span>/g" \ | sed -e "s/${DIP}/<span class='dip'>${DIP}<\/span>/g" \ | sed -e "s/\(^[^:]\+:\)\(.*$\)/<dt>\1<\/dt><dd>\2<\/dd>/g" >> ${CACHE_DIR}${CACHE_FILE} done < ${RES_FILE} :> ${RES_FILE} exit 0
index.php
主な機能:Baseから渡された日時の文字列に対応するキャッシュファイルがあったらそれを表示する。なかったらtarget_datetimeに日時を書き込んで、キャッシュファイルが生成されるのを待つ。
※Baseに戻るときはブラウザの戻るボタンを使用する。
<?php #// キャッシュディレクトリ $cache_dir = '/var/www/base/find_log/cache/'; #// grep対象の日時を書き込むファイル $target_dt = '/var/www/base/find_log/target_datetime'; #// このファイルのURL $base_url = 'https://EXAMPLE.EXA/base/find_log/index.php'; /************************************************* * キャッシュファイルの存在チェック *************************************************/ function existCacheFile ($timestamp = "", &$cache = "") { global $cache_dir; $cache = $cache_dir.strtr($timestamp, ' ', '_'); return file_exists($cache); } /************************************************* * Baseのタイムスタンプフォーマットかどうか *************************************************/ function isTimestamp ($ts = '', &$matches = array('')) { return preg_match('/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/', $ts, $matches); } /************************************************* * ipアドレスのフォーマットかどうか *************************************************/ function isIpformat ($ip = '', &$matches = array('')) { return preg_match('/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/', $ip, $matches); } if (isTimestamp(htmlentities($_GET['timestamp'], ENT_QUOTES), $ts)) { $timestamp = $ts[0]; if (!existCacheFile($timestamp)) { #// timestampがセットされていてキャッシュがなかったらtarget_datetimeファイルに書き込む isIpformat(htmlentities($_GET['sip'], ENT_QUOTES), $sip); isIpformat(htmlentities($_GET['dip'], ENT_QUOTES), $dip); if (!file_put_contents($target_dt, "$timestamp"." $sip[0]"." $dip[0]")) { echo "書き込みできませんでした"; } } } else { #// timestampがセットされていなかったらリフレッシュしてr_timestampに日時がセットされているはず if(!isTimestamp(htmlentities($_GET['r_timestamp'], ENT_QUOTES), $r_ts)) exit(); $timestamp = $r_ts[0]; } #// キャッシュファイルがまだ生成されていなかったら2秒後にリフレッシュする if (!existCacheFile($timestamp)) { header("refresh:2;url=$base_url?r_timestamp=$timestamp"); } ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script type="text/javascript" src="jquery-2.0.2.min.js"></script> <style type="text/css"> table.iptbl { border: solid 1px #CCC; border-collapse: collapse; } table.iptbl tr { border: solid 1px #CCC; } table.iptbl .title { background: #CCC; } table.iptbl td { padding: 3px; } span.sip { background: #7fffd4; } span.dip { background: #fed0e0; } dl { width: 100%; } dt { font-size: 120%; } dd { width: 100%; } </style> </head> <body> <?php echo "<h1>Log finder</h1>"; echo "<dl>"; if (existCacheFile($timestamp, $cf)) { echo file_get_contents($cf); } else { echo "ファイルを作成中"; } echo "</dl>"; ?> </body> <script type="text/javascript"> var dt_list = $('dt'), dlen = dt_list.length, dt_txt = ''; for (i=0; i<dlen; i+=1) { if (dt_txt !== (dt_txt = $(dt_list[i]).text())) { $(dt_list[i]).css({'background':'#ccc', 'margin-top':'20px', 'font-weight':'bolder', 'color':'#00f'}); } else { $(dt_list[i]).css({'font-size':'5px', 'background':'#f0f0f0', 'color':'#fff'}); } } </script> </html>