8

pyparsing モジュールを使用した Snort ログの解析に問題があります。

問題は、Snort ログ (空白行で区切られた複数行のエントリがある) を分離し、行ごとに読み込んで文法が各行で機能することを期待するのではなく、pyparsing で各エントリをチャンク全体として解析することです (明らかに、そうではありません。)

各チャンクを一時的な文字列に変換して、各チャンク内の改行を削除しようとしましたが、正しく処理されません。私は完全に間違った方向に進んでいるかもしれませんが、そうは思いません (同様の形式は syslog タイプのログに対して完全に機能しますが、それらは 1 行のエントリであるため、基本的なファイル イテレータ/行処理に適しています)

これまでのログとコードのサンプルを次に示します。

[**] [1:486:4] ICMP Destination Unreachable Communication with Destination Host is Administratively Prohibited [**]
[Classification: Misc activity] [Priority: 3] 
08/03-07:30:02.233350 172.143.241.86 -> 63.44.2.33
ICMP TTL:61 TOS:0xC0 ID:49461 IpLen:20 DgmLen:88
Type:3  Code:10  DESTINATION UNREACHABLE: ADMINISTRATIVELY PROHIBITED HOST FILTERED
** ORIGINAL DATAGRAM DUMP:
63.44.2.33:41235 -> 172.143.241.86:4949
TCP TTL:61 TOS:0x0 ID:36212 IpLen:20 DgmLen:60 DF
Seq: 0xF74E606
(32 more bytes of original packet)
** END OF DUMP

[**] ...more like this [**]

そして更新されたコード:

def snort_parse(logfile):
    header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + Suppress("]") + Regex(".*") + Suppress("[**]")
    cls = Optional(Suppress("[Classification:") + Regex(".*") + Suppress("]"))
    pri = Suppress("[Priority:") + integer + Suppress("]")
    date = integer + "/" + integer + "-" + integer + ":" + integer + "." + Suppress(integer)
    src_ip = ip_addr + Suppress("->")
    dest_ip = ip_addr
    extra = Regex(".*")

    bnf = header + cls + pri + date + src_ip + dest_ip + extra

    def logreader(logfile):
        chunk = []
        with open(logfile) as snort_logfile:
            for line in snort_logfile:
                if line !='\n':
                    line = line[:-1]
                    chunk.append(line)
                    continue
                else:
                    print chunk
                    yield " ".join(chunk)
                    chunk = []

    string_to_parse = "".join(logreader(logfile).next())
    fields = bnf.parseString(string_to_parse)
    print fields

ヘルプ、ポインタ、RTFM、You're Doing It Wrongs など、大歓迎です。

4

3 に答える 3

14
import pyparsing as pyp
import itertools

integer = pyp.Word(pyp.nums)
ip_addr = pyp.Combine(integer+'.'+integer+'.'+integer+'.'+integer)

def snort_parse(logfile):
    header = (pyp.Suppress("[**] [")
              + pyp.Combine(integer + ":" + integer + ":" + integer)
              + pyp.Suppress(pyp.SkipTo("[**]", include = True)))
    cls = (
        pyp.Suppress(pyp.Optional(pyp.Literal("[Classification:")))
        + pyp.Regex("[^]]*") + pyp.Suppress(']'))

    pri = pyp.Suppress("[Priority:") + integer + pyp.Suppress("]")
    date = pyp.Combine(
        integer+"/"+integer+'-'+integer+':'+integer+':'+integer+'.'+integer)
    src_ip = ip_addr + pyp.Suppress("->")
    dest_ip = ip_addr

    bnf = header+cls+pri+date+src_ip+dest_ip

    with open(logfile) as snort_logfile:
        for has_content, grp in itertools.groupby(
                snort_logfile, key = lambda x: bool(x.strip())):
            if has_content:
                tmpStr = ''.join(grp)
                fields = bnf.searchString(tmpStr)
                print(fields)

snort_parse('snort_file')

収量

[['1:486:4', 'Misc activity', '3', '08/03-07:30:02.233350', '172.143.241.86', '63.44.2.33']]
于 2010-08-04T15:45:56.613 に答える
4

やるべきことを学んでいない正規表現がいくつかありますが、これがそれほど苦痛ではないことを願っています。あなたの考えの最大の原因は、この構成の使用です。

some_stuff + Regex(".*") + 
                 Suppress(string_representing_where_you_want_the_regex_to_stop)

pyparsingパーサー内の各サブパーサーは、ほとんどスタンドアロンであり、着信テキストを順番に処理します。'*'したがって、正規表現の用語には、繰り返しを停止する場所を確認するために次の式を先読みする方法がありません。つまり、式Regex(".*")は行の終わりまで読み取られます。これは、".*"複数行を指定せずに停止する場所だからです。

pyparsingでは、この概念はSkipToを使用して実装されます。ヘッダー行の記述方法は次のとおりです。

header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + 
             Suppress("]") + Regex(".*") + Suppress("[**]") 

「。*」の問題は、次のように変更することで解決されます。

header = Suppress("[**] [") + Combine(integer + ":" + integer + ":" + integer) + 
             Suppress("]") + SkipTo("[**]") + Suppress("[**]") 

clsについても同じです。

最後のバグです。日付の定義が1つ短くなっています':' + integer:

date = integer + "/" + integer + "-" + integer + ":" + integer + "." + 
          Suppress(integer) 

する必要があります:

date = integer + "/" + integer + "-" + integer + ":" + integer + ":" + 
          integer + "." + Suppress(integer) 

これらの変更は、ログデータの解析を開始するのに十分だと思います。

他のスタイルの提案は次のとおりです。

繰り返しSuppress("]")表現がたくさんあります。私は、抑制可能な句読点をすべて、次のような非常にコンパクトで保守しやすいステートメントで定義し始めました。

LBRACK,RBRACK,LBRACE,RBRACE = map(Suppress,"[]{}")

(展開して、他の句読文字を好きなように追加します)。これで、これらの文字を記号名で使用できるようになり、結果のコードが少し読みやすくなりました。

ヘッダーは。で始めますheader = Suppress("[**] [") + ...。このようにリテラルに埋め込まれたスペースを見るのは好きではありません。これは、構文解析の堅牢性の一部をバイパスするため、pyparsingによって自動的に空白がスキップされるためです。何らかの理由で「[**]」と「[」の間のスペースが2つまたは3つのスペース、またはタブを使用するように変更された場合、抑制されたリテラルは失敗します。これを前の提案と組み合わせると、ヘッダーはで始まります

header = Suppress("[**]") + LBRACK + ...

これは生成されたテキストであることを私は知っているので、この形式のバリエーションはありそうにありませんが、pyparsingの長所よりもうまく機能します。

フィールドを解析したら、パーサー内のさまざまな要素に結果名を割り当て始めます。これにより、後でデータを取得するのがはるかに簡単になりますたとえば、clsを次のように変更します。

cls = Optional(Suppress("[Classification:") + 
             SkipTo(RBRACK)("classification") + RBRACK) 

を使用して分類データにアクセスできるようになりますfields.classification

于 2010-08-04T15:53:30.183 に答える
0

まあ、私は Snort も も知らないpyparsingので、ばかげたことを言ったら、前もってお詫びします。問題がエントリを処理できないことにあるのか、それとも正しい形式でpyparsing送信できないことにあるのか、私にはわかりません。pyparsing後者の場合、このようなことをしてみませんか?

def logreader( path_to_file ):
    chunk = [ ]
    with open( path_to_file ) as theFile:
        for line in theFile:
            if line:
                chunk.append( line )
                continue
            else:
                yield "".join( *chunk )
                chunk = [ ]

もちろん、 に送信する前に各チャンクを変更する必要がある場合は、送信する前に変更pyparsingできますyield

于 2010-08-04T14:36:13.847 に答える