Разбор журналов Snort с PyParsing - PullRequest
8 голосов
/ 04 августа 2010

Возникла проблема с анализом журналов Snort с помощью модуля pyparsing.

Проблема в том, что нужно разделить журнал Snort (в котором есть многострочные записи, разделенные пустой строкой) и заставить выполнять синтаксический анализ каждой записи как целого куска, а не читать строку за строкой и ожидать, что грамматика будет работать с каждым линия (очевидно, это не так.)

Я пытался преобразовать каждый чанк во временную строку, удаляя символы новой строки внутри каждого чанка, но он отказывается работать правильно. Я могу быть совершенно не на том пути, но я так не думаю (подобная форма отлично работает для журналов типа syslog, но это однострочные записи, поэтому они пригодны для вашего основного файлового итератора / обработки строк)

Вот пример журнала и код, который у меня есть:

[**] [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, вы делаете неправильно, и т. Д., Очень ценится.

Ответы [ 3 ]

13 голосов
/ 04 августа 2010
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']]
4 голосов
/ 04 августа 2010

У вас есть возможность отучиться от регулярного выражения, но, надеюсь, это не будет слишком болезненным. Самым большим виновником вашего мышления является использование этой конструкции:

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

Каждый подпарапер внутри синтаксического анализатора является в значительной степени автономным и работает последовательно через входящий текст. Таким образом, термин Regex не может заглянуть к следующему выражению, чтобы увидеть, где остановится повторение '*'. Другими словами, выражение Regex(".*") будет просто читать до конца строки, поскольку именно здесь ".*" останавливается без указания многострочного.

В pyparsing эта концепция реализована с использованием SkipTo. Вот как написана ваша строка заголовка:

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

Ваша проблема ". *" Будет решена путем изменения ее на:

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

То же самое для кл.

Последняя ошибка, ваше определение даты короткое на один ':' + 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("[**] [") + .... Мне никогда не нравилось видеть пробелы, встроенные в литералы таким образом, так как он обходит некоторые из парсинга, который дает автоматическую пропускную способность при разборе. Если по какой-либо причине пробел между «[**]» и «[» был изменен на использование 2 или 3 пробелов или табуляции, то ваш подавленный литерал потерпит неудачу. Объедините это с предыдущим предложением, и заголовок начнется с

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

Я знаю, что это сгенерированный текст, поэтому изменение в этом формате маловероятно, но оно лучше подходит для сильных сторон pyparsing.

Как только вы разберете свои поля, начните присваивать имена результатов различным элементам вашего парсера. Это сделает лот более простым для последующего получения данных. Например, измените cls на:

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

Позволит вам получить доступ к данным классификации, используя fields.classification.

0 голосов
/ 04 августа 2010

Ну, я не знаю 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 его.

...