Анализ журналов Snort с помощью PyParsing

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

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

Я попытался преобразовать каждый фрагмент в временную строку, удалив строки новой строки внутри каждого фрагмента, но он отказывается обрабатывать правильно. Возможно, я полностью ошибаюсь, но я так не думаю (аналогичная форма отлично работает для журналов типа 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, Do Do It It Wrongs и т. Д., Очень ценится.

3 Solutions collect form web for “Анализ журналов Snort с помощью PyParsing”

 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']] 

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

 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.

Последняя ошибка, ваше определение даты коротко одним:: + целым:

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

должно быть:

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

Я думаю, что этих изменений будет достаточно для начала анализа данных журнала.

Вот некоторые другие варианты стиля:

У вас много повторяющихся выражений Suppress("]") . Я начал определять всю мою подавляющую пунктуацию в очень компактном и легко поддерживаемом утверждении следующим образом:

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

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

Вы начинаете заголовок с header = Suppress("[**] [") + ... Мне никогда не нравятся пробелы, встроенные в литералы таким образом, поскольку он обходит некоторые из синтаксического размывания pyparsing дает вам с автоматическим пропуском пробелов. Если по какой-то причине пространство между «[**]» и «[» было изменено, чтобы использовать 2 или 3 пробела или вкладку, то ваш подавленный литерал завершится ошибкой. Объедините это с предыдущим предложением, и заголовок начнется с

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

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

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

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

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

Ну, я не знаю Snort или pyparsing , поэтому извиняюсь заранее, если я говорю что-то глупое. Я не понимаю, 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 его.

Python - лучший язык программирования в мире.