5
3

More than 1 year has passed since last update.

よく使う正規表現メモ

Last updated at Posted at 2021-12-23

これは何?

個人的によく使う正規表現のメモです。主にgrepで使います。必要に応じてsortやuniqなどと組み合わせて使います。

正規表現の基本

正規表現 意味
[0-9] 0から9までの数字1文字を示す
[A-z] Aからzまでの英字1文字を示す。大文字小文字両方が対象
[A-Z] AからZまでの英字1文字を示す。大文字のみ。
[a-z] AからZまでの英字1文字を示す。小文字のみ。
. 任意の1文字を示す。記号も含まれる。
+ 直前に記載された文字の1回以上の繰り返し
? 直前に記載された文字の0回または1回の繰り返し
{n} 直前に記載された文字または正規表現のn回の繰り返し
{1,3} 直前に記載された文字または正規表現の1~3回の繰り返し
* 直前に記載された文字の0回以上の繰り返し
[]の中の^ 直後の1文字または正規表現の否定。
^ 直後の1文字または正規表現から始まる
$ 直前の1文字または正規表現で終わる
\ 直後の1文字をエスケープ
[a|b] aまたはb(|は実際にはパイプ記号です)

正規表現の具体例

sample.txtというファイル内を検索する場合の例です。厳密な一致ではなく、極力シンプルにすることと、多少余計なものを拾うことは許容する前提です。なので「〇〇っぽいものにマッチ」と記載しています。

.co.jpで終わる箇所を探す。

「.」は任意の1文字を表すため、ここではエスケープが必要
grep '.co.jp$' sample.txt

IPアドレスっぽいものへのマッチ

IPアドレスっぽい文字列を検索する。厳密なマッチではなく、やや幅を持たせてある理由は、正規表現が複雑すぎると処理に時間がかかるため。
下記は、「数字が0~3回繰り返しの後に.」というパターンが3回繰り返された後に「数字が0~3回繰り返し」を探す。
これだと、999.999.999.999のようなあり得ないIPアドレスっぽい文字列を拾ってしまうが、それはそれで不審な痕跡と言えるので、むしろ拾ってもらったほうがありがたい。
grep '([0-9]{0,3}.){3}.[0-9]{0,3}' sample.txt

応用例として、-oオプションを使って、IPアドレスっぽい文字列だけを抜き出し、それをsortしてuniqするという方法もある。

ユニークするだけなら以下のとおり。-n を入れれば昇順ソートになる。
grep -o '([0-9]{0,3}.){3}.[0-9]{0,3}' sample.txt|sort -u

IPアドレスの数をカウントしたい場合は以下のとおり。ついでに数が多いもの順でソートした例
grep -o '([0-9]{0,3}.){3}.[0-9]{0,3}' sample.txt|sort |uniq -c |sort -nr

IPアドレスが記載されているカラムがわかっているならcutやawkで抜き出しても良い。
スペース区切りの場合は以下で1行目を抜き出せる。上記と同じようにパイプでsortに渡せば同じように処理できる。
cut -d " " -f 1 sample.txt
awk '{print $1}' sample.txt

メールアドレスっぽいものにマッチ

正直やらないのでやるならこんな感じかなというものを記載します。
そもそもメールアドレスの細かい定義がメールサービス提供者によって微妙に違うことがあり、特定の記号が使えたり使えなかったりするので、以下のような「@の前後にスペースが入っていない文字列」くらいの緩い条件が良いかなぁと思ったりします。
[^ ]+@[^ ]

ただこれだと検索対象によっては無関係な文字列がかなりヒットしてしまうと思います。
[^ ]+@[^]+.jp のように、後ろの文字を限定すると良いかもしれません。.comなどに対応するためには、.の後ろに文字が2~3個という条件が妥当でしょうか。
[^ ]+@[^]+.[A-z]{2.3}

Apacheのアクセスログを調査

IPアドレスの抽出

前述したとおり、cutやawkで1行目を抜き出すのが早いと思います。あとはsortやuniqで。

IPだけを抜き出してユニーク
cut -d " " -f 1 sample_access_log.txt |sort -un

IPごとにアクセス数ランキング
cut -d " " -f 1 sample_access_log.txt |sort |uniq -c |sort -r

特定の条件を満たすリクエストを抽出

ここは様々なパターンがあり得ます。
一例として、GETまたはPOSTで.phpの拡張子を持つファイルへのアクセスを抽出します。
egrep -a '(GET|POST) [^ ]+.php' sample_access_log.txt

さらに、ステータスコードが200のものだけに絞り込むなら、「" 200」でgrepすると良いです。「"」はステータスコードの前に記録されている「HTTP/1.1"」の最後の「"」にマッチさせるためです。ただし、「HTTP/1.0"」というパターンがないとも限らないので、「HTTP/1.1" 200」という条件にはしていません。
egrep -a '(GET|POST) [^ ]+.php' sample_access_log.txt |fgrep -a '" 200'

あまり処理速度が上がるとは思えませんが、正規表現で無理やり書くなら以下になるかと思います。
egrep -a '(GET|POST) [^ ]+.php[^ ]+ [^ ]+ 200' sample_access_log.txt
もう少し厳密に、HTTP/1.[0-1]とかを入れても良いかもしれませんが、フォレンジック調査では通常とは異なる出力を除外したくはないので、多少緩めの条件の方が良いと思います。

攻撃を見つけやくするなら、メソッドとURL+クエリ文字列+ステータスコードの抽出というのが良い気がします(抽出する情報は人によるかも)
抽出するだけなら単純にcutやawkで抜き出すのが良いでしょう。
一例ですが、区切り文字を「"」にして、2列目と3列目を出すことで、目的のデータが抽出できます。レスポンスサイズも出るのでなお良いかもしれません。
cut -d '"' -f 2,3 sample_access_log.txt

他、思いついたら追記していきます。

なお、上記はあくまでLinuxコマンドで調査した方が早いサイズのログを想定しています。より多くのログを調査する必要がある場合は、SIEMを使う方が良いでしょう。
個人的には無料で使えて使いやすいSOF-ELKがオススメです。
https://github.com/philhagen/sof-elk

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3