Snortの個人的メモ
随時更新予定
参考
http://manual-snort-org.s3-website-us-east-1.amazonaws.com/node27.html
http://blog.joelesler.net/2010/03/offset-depth-distance-and-within.html
offset [どこから検索するか]
パケットのどこから検索を開始するか指定する
content:"cgi-bin/phf"; offset:4;
パケットの4バイト目以降から"cgi-bin/phf"の検索を行う。ちなみにルール中の一番最初のcontentは自動的にオフセット0から検索されるので、offset:0と明示的に指定する必要はない。
depth [どこまで検索するか]
パケットのどこまで検索するかを指定する
content:"GET"; depth:3;
先頭3バイト以内の"GET"を検索する。
distance [どこから次の検索を開始するか]
content Aが見つかったあと、次のcontent Bの検索をどこから開始するかを指定する
content:"ABC"; content:"DEF"; distance:1;
"ABC"が見つかったら、1バイト無視してから"DEF"の検索を開始する(正規表現のABC.{1,}DEFと等しい)
「"ABC"のあとに"DEF"が現れるはずだけど、何バイト目以降に現れるかは分からない」という場合にはcontent:"ABC"; content:"DEF"; distance:0 とすれば、"ABC"のあとに何バイトあろうと関係なく"DEF"を検索します。
content:"ABC"; content:"DEF"; distance:-1; within:2;
distanceにマイナスの値を指定した場合、contentを前後して検索することができる。withinを併用することで検索範囲を絞り込める。上記はABCの前後1バイトのDEFを検知する(ABC.{1,}DEF、DEF.{1,}ABCのどちらも検知する)
※参考1 参考2
within [~以内を検索する]
content Aが見つかってから、何バイト以内にあるcontent Bを検索する
content:"ABC"; content:"EFG"; within:10;
"ABC"のあとの10バイト以内にある"EFG"を検索する。大体何バイト以内に次のcontentがあるか分かっている場合に有効。
dsize
パケットのペイロードのサイズをチェックします。
dsize:300<>400;
ペイロードのサイズが300から400バイトのパケットをチェックします。ただし、dsizeでペイロードのサイズを指定しても長大なデータに意図せずマッチしてしまうこともあります。例えばTCPは長大なデータを複数のパケットに分割して送信する性質がありますが、分割されたパケットの中にたまたまパターンにマッチするデータが含まれていた場合、これを検知してしまう可能性があります。またdsizeを使用するときは記述の順番に注意する必要があります。
以下の記述を見てみましょう。
content:"|15|"; dsize:1;
上記はサイズが1バイトでかつ0x15(16進数)というデータが含まれたペイロードを検知することを目的としています。本来なら15というパターンにマッチすることを期待してしまいますが、実際には1515151515というパターンにもマッチしてしまいます。1バイトと指定しているにも関わらずなぜそのようなことが起きてしまうのでしょうか。snortにはrecursion(再帰)という性質があり、パターンがマッチするまで検索を続けるのです。1515151515のパターンを例に見てみましょう。1バイト目に0x15が見つかりますが、1バイト目以降もデータが続いています。これでは、「1バイトでかつ0x15」という条件にマッチしないので、snortは引き続き同様のパターンを探します。2バイト目にも0x15が見つかりますが、やはり2バイト目以降もデータが続いているので、「1バイトでかつ0x15」という条件にマッチしないと判断され、snortは検索を継続します。5バイト目に0x15が見つかりました。5バイト目以降は何もデータがありません。ここで初めてsnortは「1バイトでかつ0x15」という条件にマッチしたと判断します。(1515151515)
このような誤検知を避けるにはdsizeをcontentよりも前に指定する必要があります。
dsize:1; content:"|15|";
上記のように記述すれば、snortはまずペイロードが1バイトかどうか確認してから0x15の検索を行うので誤検知を防ぐことができます。(こちらに詳しく書いてあります。)
その他
portやmodifierを使わずにcontentでGETリクエストやPOSTリクエストを正確に検知するには末尾に空白を含める
content:"GET "; depth:4;
content:"POST "; depth:5;
末尾に空白を含めることで"GETTY"や"POSTED"などの誤検知を防ぐ。
GETとPOSTの両方を検知する
content:"T "; offset:2; depth:3;
オフセット2以降、3バイト内の"T "を検索する。"GET "の場合、"T "はオフセット2から3の間に現れ、"POST "の場合、"T "はオフセット3から4の間に現れるので、この書き方でGETとPOSTの両方を検知できる。(ちなみにPUTも検知する)
0123
GET
01234
POST
PCRE あれこれ
先読み: 肯定先読みは"(?="、否定先読みは"(?!"と記述
\w+(?=;)
末尾にセミコロンがついた単語にマッチする
foo(?!bar)
末尾に"bar"が続かない"foo"にマッチする
個人的によく使うパターンは下記のようなもの
pcre:"/(?![a-zA-Z]{10})(?![0-9]{10})[a-zA-Z0-9]{10}/";
英数字が混ざった文字列10文字にマッチする。アルファベットのみで構成された10文字あるいは数字のみで構成された10文字にはマッチしない。
URLのパラメーターに英数字混合のランダムな文字列 (30~90文字)を含んだURLを検知したい。ただし、MD5ハッシュ値 (32文字)は除外する。
pcre:"/\.[a-z]{3,4}\?[a-z]{3,10}=(?![a-z]{30,90})(?![0-9]{30,90})(?![a-z0-9]{32}\s)[a-z0-9]{30,90}\sHTTP\/1\./"
参考
http://www.pcre.org/original/doc/html/pcrepattern.html