开发这个工具主要还是工作中碰到了一些问题。
问题描述:收到一些数据包,拿过来在IPS设备上测试出现了一些告警。如何判断测试的这些数据包中,有哪些数据包告警了,又有哪些数据包存在误报?
分析:公司的IPS设备有日志功能,也有取证功能。那么可以利用取证功能来解决问题。(下文中:取证包,告警的数据包是同一个意思;tid号是规则的编号)
工具原理:
首先读取一个取证包,然后去遍历测试的所有数据包:
如果某个测试的数据包的内容,包含了取证包的内容,就认为测试的数据包告警了。
如果测试的数据包中能够提取出有效的tid号,就可以判断是正确告警,还是误报。
如果测试的数据包中不能提取出有效的tid号,就不能研判是否误报情况
如果某个测试的数据包的内容,没有完整包含取证包的内容,就认为测试的数据包没有告警。
其他:
测试详情是不会写入到结果文件中的(结果文件保存在桌面)
测试的时间信息是不会写入到结果文件中的
测试的系统上必须有tshark,并且已经加入到了环境变量
每次运行脚本,都会重写导出在桌面的结果文件。因此导出的结果文件可删可不删
由于取证包的内容,是从测试的数据包中截取出来的。因此脚本读取数据包列表视图判断包含关系即可,但是需要删除数据包列表视图中的序号和时间。
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位数字),但是文件名如下图红框中的
脚本还是不好准确判断是否存在误报。但是我肉眼可以直接观察到哪些是tid号,解决办法就是批量修改文件名,你可以使用uTools、Quicker这样的效率工具实现,或者是更专业的菲菲更名宝贝,经过这样优化之后,你会发现脚本能够判断所有测试的数据包了。