基于流量分析IPS告警&误报

目录

  • 前言
  • 实现逻辑
  • 代码设计
  • 效果演示
  • 一些建议

前言

开发这个工具主要还是工作中碰到了一些问题。
问题描述:收到一些数据包,拿过来在IPS设备上测试出现了一些告警。如何判断测试的这些数据包中,有哪些数据包告警了,又有哪些数据包存在误报?

实现逻辑

分析:公司的IPS设备有日志功能,也有取证功能。那么可以利用取证功能来解决问题。(下文中:取证包,告警的数据包是同一个意思;tid号是规则的编号)
工具原理:
首先读取一个取证包,然后去遍历测试的所有数据包:

  1. 如果某个测试的数据包的内容,包含了取证包的内容,就认为测试的数据包告警了。
    如果测试的数据包中能够提取出有效的tid号,就可以判断是正确告警,还是误报。
    如果测试的数据包中不能提取出有效的tid号,就不能研判是否误报情况

  2. 如果某个测试的数据包的内容,没有完整包含取证包的内容,就认为测试的数据包没有告警。
    其他:

  3. 测试详情是不会写入到结果文件中的(结果文件保存在桌面)

  4. 测试的时间信息是不会写入到结果文件中的

  5. 测试的系统上必须有tshark,并且已经加入到了环境变量

  6. 每次运行脚本,都会重写导出在桌面的结果文件。因此导出的结果文件可删可不删

  7. 由于取证包的内容,是从测试的数据包中截取出来的。因此脚本读取数据包列表视图判断包含关系即可,但是需要删除数据包列表视图中的序号和时间。

代码设计

import re
from subprocess import PIPE, Popen
import os
from colorama import init, Fore
import time


# 提取出告警数据包数据包列表视图的内容,不要行号和时间
def deal_alert_pcap(path_f):
    command = 'tshark.exe -r "' + path_f  # 1:读取数据包
    p = Popen(command, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()

    regex = r"\d+\x2e\d+\x2e\d+\x2e\d+[\s\S]*?\\r\\n"
    alert_pcap = re.findall(regex, str(stdout))
    return alert_pcap


# 提取出原始数据包数据包列表视图的内容,不要行号和时间
def deal_original_pcap(original_path):
    command = 'tshark.exe -r "' + original_path  # 1:读取数据包
    p = Popen(command, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()

    regex = r"\d+\x2e\d+\x2e\d+\x2e\d+[\s\S]*?\\r\\n"
    original_pcap = re.findall(regex, str(stdout))
    return original_pcap


'''
工具原理:
首先读取一个取证包,然后去遍历测试的所有数据包:
1. 如果某个测试的数据包的内容,包含了取证包的内容,就认为测试的数据包告警了。
    如果测试的数据包中能够提取出有效的tid号,就可以判断是正确告警,还是误报。
    如果测试的数据包中不能提取出有效的tid号,就不能研判是否误报情况
2. 如果某个测试的数据包的内容,没有完整包含取证包的内容,就认为测试的数据包没有告警。

其他:
1. 测试详情是不会写入到结果文件中的(结果文件保存在桌面)
2. 测试的时间信息是不会写入到结果文件中的
3. 测试的系统上必须有tshark,并且已经加入到了环境变量
'''

print('数据包告警情况分析工具')
alert_path = input(r'请输入取证包所在的文件夹:')
original_path = input(r'请输入测试的数据包所在的文件夹:')
init(autoreset=True)  # 用来设置终端颜色
log = input('[?] 是否显示测试详情请输入:1(显示),其他字符(不显示):')

if len(log) > 0:
    if log == '1':
        log = 1
        print('[+] 您选择开启测试详情,程序开始运行,请耐心等待……')
    else:
        log = 0
        print('[+] 您选择关闭测试详情,程序开始运行,请耐心等待……')
if log == '':
    log = 0
    print('[+] 您选择关闭测试详情,程序开始运行,请耐心等待……')

list_alert = []  # 用来存储告警数据包的数据包列表视图的内容(删除了行号和时间)
list_original = []  # 用来存储提供的原始数据包的数据包列表视图的内容(删除了行号和时间)
all_pcap_name = []  # 用来存储所有原始的数据包名字
alert_pcap_name = []  # 用来存储原始数据包中,告警了的
noalert_pcap_name = []  # 用来存储原始数据包中,没有告警的
desktop_path = os.path.join(os.path.expanduser("~"), 'Desktop')  # 获取桌面路径
txt_path = os.path.join(desktop_path, '数据包告警情况分析结果.txt')  # 给出分析的结果,方便展示
file_txt = open(txt_path, 'w', encoding='utf8')  # 打开文件,准备写入结果

for current_folder, list_folders, files in os.walk(alert_path):
    for f in files:  # 用来遍历所有的文件,只取文件名,不取路径名
        if f.endswith('pcap'):  # 操作数据包
            path_f = current_folder + '\\' + f  # 给出数据包的绝对路径
            list_alert = deal_alert_pcap(path_f)  # 用来存储告警数据包的数据包列表视图的内容(删除了行号和时间)

            # 开始映射,拿告警的数据包与所有原始的数据包内容做对比
            for current_folder1, list_folders1, files1 in os.walk(original_path):
                for f1 in files1:  # 用来遍历所有的文件,只取文件名,不取路径名
                    if f1.endswith('pcap'):  # 操作数据包
                        localtime = time.strftime("%H:%M:%S", time.localtime(time.time()))
                        path_f1 = current_folder1 + '\\' + f1  # 给出数据包的绝对路径
                        list_original = deal_original_pcap(path_f1)  # 用来存储提供的原始数据包的数据包列表视图的内容(删除了行号和时间)

                        # 给出一个时间info信息
                        dt_info = '[' + str(localtime) + '] '
                        if f1 not in all_pcap_name:
                            all_pcap_name.append(f1)
                        if log:
                            print(dt_info + '[#] 当前测试的取证包:' + f + '\t当前测试的原始数据包:' + f1)
                            # file_txt.write(dt_info + '[?] 当前测试的取证包:' + f + '\t当前测试的原始数据包:' + f1)  # 测试记录不写入到报告中
                            # file_txt.write('\n')

                        # 开始进行内容匹配,检测告警数据包的内容是否出现在提供的数据包中
                        if set(list_alert).issubset(set(list_original)):
                            alert_pcap_name.append(f1)  # 测试的数据包告警了

                            # 由于测试的数据包的文件名中可能不存在tid,因此需要分析能不能研判误报
                            f1_tid = re.findall('\d{5}', f1)
                            f1_tid = str(f1_tid).replace("[", '').replace("]", '').replace("'", '')  # 提取出tid号
                            if len(f1_tid) == 5:  # 可以从测试的数据包中提取出tid号,并且tid号只有1个,由5位数字组成
                                # 分析误报情况
                                alert_tid = f.lower().split('event', 1)[-1].split('.')[0]  # 从取证包的文件名中提取出tid编号
                                if f1_tid == alert_tid:
                                    message = '[√] 测试的原始数据包:' + f1 + '\t' + '\t正常触发告警tid:' + alert_tid + '\t' + '\t对应取证包:' + f
                                    file_txt.write(message)
                                    file_txt.write('\n')
                                    print(Fore.GREEN + dt_info + message)
                                else:
                                    message = '[×] 测试的原始数据包:' + f1 + '\t' + '\t疑似误报告警tid:' + alert_tid + '\t' + '\t对应取证包:' + f
                                    file_txt.write(message)
                                    file_txt.write('\n')
                                    print(Fore.RED + dt_info + message)
                            else:
                                errer_message = '[?] 测试的原始数据包:' + f1 + '\t' + '\t无法研判是否误报:' + f + '\t\t[info] 确认触发告警,无法研判原因是从原始数据包中检测到多个tid号:' + f1_tid
                                file_txt.write(errer_message)
                                file_txt.write('\n')
                                print(Fore.BLUE + dt_info + errer_message)

# 给出原始数据包的告警情况
alert_pcap_name = list(set(alert_pcap_name))
print(Fore.YELLOW + '[+] 提供的原始数据包中:')
print(Fore.YELLOW + '[-] 告警的数据包有:{}个'.format(str(len(alert_pcap_name))))
file_txt.write('\n')
file_txt.write('[+] 提供的原始数据包中:')
file_txt.write('\n')
file_txt.write('[-] 告警的数据包有:{}个'.format(str(len(alert_pcap_name))))
file_txt.write('\n')
for i in alert_pcap_name:
    print(i)
    file_txt.write(i)
    file_txt.write('\n')

all_pcap_name = list(set(all_pcap_name))
for i in all_pcap_name:
    if i not in alert_pcap_name:
        noalert_pcap_name.append(i)
print(Fore.YELLOW + '[-] 不告警的数据包有:{}个'.format(str(len(noalert_pcap_name))))
file_txt.write('\n')
file_txt.write('[-] 不告警的数据包有:{}个'.format(str(len(noalert_pcap_name))))
for i in noalert_pcap_name:
    print(i)
    file_txt.write(i)
    file_txt.write('\n')

file_txt.close()
print(Fore.YELLOW + '程序运行结束,请在桌面查看结果')
os.system('pause')  # 在脚本中运行时可以删除此行

效果演示

开启测试详情:

关闭测试详情 (GIF时长约2分钟):

一些建议

如果提供的数据包中不存在tid号,脚本肯定是不能判断误报的。如果存在tid号(一组5位数字),但是文件名如下图红框中的
基于流量分析IPS告警&误报_第1张图片

脚本还是不好准确判断是否存在误报。但是我肉眼可以直接观察到哪些是tid号,解决办法就是批量修改文件名,你可以使用uTools、Quicker这样的效率工具实现,或者是更专业的菲菲更名宝贝,经过这样优化之后,你会发现脚本能够判断所有测试的数据包了。

  1. 优化数据包(只要数据包中只有一组5位数字即可)

基于流量分析IPS告警&误报_第2张图片

  1. 再次测试,全部都能就准判断!(GIF约2分钟)

  1. 由于结果默认只给出txt,如果单纯想要获取误报的,或者正常告警的,可以通过如下方法快速筛选出来!

方法1:
基于流量分析IPS告警&误报_第3张图片

方法2:
基于流量分析IPS告警&误报_第4张图片

你可能感兴趣的:(入侵检测,python,IPS)