logo

早期网络流量异常检测的无监督深度学习模型

Published on

目录

摘要

迄今为止,针对此类攻击的最先进的防御系统主要依赖于从整个流或签名中提取的预定义特征。这些特征定义是手动的,提取流特征后阻止恶意流可能会太迟。在本研究中,我们提出了一种有效的异常流量检测机制,名为D-PACK,它由一个卷积神经网络(CNN)和一个无监督的深度学习模型(如自动编码器)组成,用于自动描述流量模式并过滤异常流量。值得注意的是,D-PACK仅检查每个流的前几个数据包的前几个字节,以进行早期检测。我们的实验结果表明,仅通过检查每个流的前两个数据包,D-PACK的准确性接近100%,同时具有极低的误报率,例如仅为0.83%。这种设计可以启发对在线异常检测系统的新兴努力,该系统通过减少处理数据包的数量,并及时阻止恶意流量,以提高网络安全性。

介绍

论文主要贡献的工作:

  1. 提出了一种基于深度学习的无监督网络流量异常检测模型,该模型可以自动构建流量特征,并在早期检测到异常流量。该模型仅检查每个流的前几个字节和前几个数据包,以实现早期检测。
  2. 实验结果表明,该模型在仅检查每个流的前两个数据包时,仍具有近100%的准确性,并且具有极低的误报率,例如0.83%。
  3. 该模型可以启发新兴的在线异常检测系统,这些系统可以减少处理的数据包数量并实时阻止恶意流量。

相关的工作

网络流量异常检测的四种主要方法:基于端口/规则的方法、深度/随机数据包检查方法、统计方法和行为方法。其中,基于端口/规则的方法、深度/随机数据包检查方法和统计方法在过去几十年中取得了较高的入侵检测性能,而行为方法在多个指标上表现不佳,例如高误报率。由于正常流量特征的复杂性和大规模原始数据中手动干预的缺乏,行为方法在定义“正常”边缘方面可能表现不佳。因此,深度学习是一种有效的方法来改进异常检测性能。

Maple

论文的工作有两个主要区别:(1)D-PACK可以通过检查每个流的第一个数据包,而不是检查流中的总数据包来构建流量的概况;(2) D-PACK可以直接处理原始数据包,即构建模式、读取输入数据和做出检测决策。

检测流量异常的方法是新颖的。我们不是收集整个流量,而是将每个流量的前几个数据包打包成固定长度的段,从而显著减少了计算和内存空间。

分类和检测的学习策略

采样网络流

考虑通过提取每个流的前 nn 个数据包来对流进行采样,并且每个数据包被修剪成固定长度的 ll 字节,从报头字段开始(必要时使用零填充)。注意,流的原始数据包是根据它们的5元组信息(源IP、源端口、目的IP、目的端口和传输层协议)进行分类的。 nnll 可根据部署时的网络流量特点灵活调整。

Maple

例如上图一个流由十个数据包组成,我们只检查前三个数据包并修剪它们的长度。将每个流中的修剪字节视为具有 n×ln\times l个元素的一维向量,并将其转换为CNN模型的输入进行训练。

自动构建流量模型

1D-CNN适用于序列数据或语言等数据;2D-CNN适用于图像等数据;3D-CNN适用于视频或体积图像等数据。1D-CNN也被广泛用于网络流量分析。

Maple

  1. 我们假设对每个流的前 nn 个数据包进行采样,并且每个采样的数据包被修剪成前 ll 个字节(主要在数据包头中,如果报文数小于 nn 或报文大小小于 ll 字节,则用0填充数据)。 2. 卷积层运算,将卷积层滤波器的核大小设置为 6(在表头中,最大的字段是 MAC 地址) 3. 然后在特征映射上应用max-over-time池化操作,并将最大值max(h)max(h)作为下一层的特征。 4. 使用多个卷积层和池化层来提取高级特征。这些特征构成了层,并被传递到一个完全连接的致密层,其输出是良性流类型的概率分布
  2. nnll 的理想配置是系统性能在几个度量之间达到平衡的阈值,例如,高精度和低处理流量。论文在实证实验中发现,当n=2n = 2l=80l =80 时,系统可以在大多数选定的数据集上达到这一目标。 6. 在CNN中的隐藏层(第5层),连接到自动编码器模块,用于学习流的特征。

Maple

无监督机器学习模型

数据集 USTC-TFC2016

是一个优秀的数据集,匹配论文的需求。表2总结了数据集中良性流量和恶意流量的统计数据。根据他们的陈述,总共有十种类型的错误 从2011年到2015年,在一个真实的网络环境中从公共网站收集软件流量。

Maple

对于基于mirai的DDoS流量,我们使用了来自罗伯特·戈登大学的数据集,用Mirai-RGU表示。

Maple

基于深度学习的无监督异常检测体系结构

Maple

在分类方面,根据良性流量的MSELoss分布设置阈值来区分良性流量和恶意流量。注意,在检测攻击时,自动编码器训练集(即良性流)产生的MSELoss分布用于确定检测阈值。MSELoss根据模糊特征计算流,将这些特征作为不同的向量输入到MSE中计算损失属性。为了避免MSELoss极值的影响,将最大值与MSELoss的99%百分位数进行比较。对于阈值,如果这两个值的差值超过MSELoss分布的三倍标准差,则设置第99个百分位数作为检测阈值;否则,将最大MSELoss设置为阈值。

调优的超参数

  1. nnll:这是早期检测的同时保持高精度的关键参数。
  2. 批量归一化:由于深度学习架构的深度,每层间的批量归一化可以保持参数分布稳定,加快学习效率,缓解梯度消失,避免过拟合。
  3. CNN 由 3 个隐藏层组成,第二个隐藏层的大小为 25 时可以显著提高该模型的性能
  4. 所有致密层都采用分层贪婪预训练进行初始化:分层贪婪预训练设计的目标是减轻深度构建模型中消失梯度问题和过拟合的影响。

结果评估

环境

Python: 3.6.12
pytorch: 1.10.2

指标

Accuracy、Precision、Recall、F1、FAR、FNR

Maple

场景

场景一 训练集:良性流量 测试集:USTC-TFC 2016的良性流量、Mirai-CCU的恶意流量

Maple

场景二

  • 训练数据和测试数据均来自USTC-TFC 2016数据集
  • 训练集与测试集中良性流量的比例为7:1
  • 只有恶意流量出现在测试集中

    Maple

场景三

  • 训练和测试数据来自Mirai-RGU数据集
  • 只有良性流量训练自动编码器
  • 测试数据包括来自Mirai-RGU数据集的良性和恶意流量

    Maple

源代码

链接

疑点难题

1. 怎么把类似于下列的 csv 数据输入到模型进行训练

Rank,StartTime,Flgs,Proto,SrcAddr,Sport,DstAddr,Dport,TotPkts,TotBytes,State,LastTime,Seq,Dur,Mean,StdDev,Sum,Min,Max,SrcPkts,DstPkts,SrcBytes,DstBytes,Rate,SrcRate,DstRate,Label,Tactic,Technique,SubTechnique
918,1637482156.077642,e,udp,192.168.1.103,123,103.159.118.4,123,1,90,REQ,1637482156.077642,166,0.0,0.0,0.0,0.0,0.0,0.0,1,0,90,0,0.0,0.0,0.0,0,Normal,Normal,Normal
919,1637482156.077655,e,udp,192.168.1.103,123,220.132.100.157,123,1,90,REQ,1637482156.077655,18,0.0,0.0,0.0,0.0,0.0,0.0,1,0,90,0,0.0,0.0,0.0,0,Normal,Normal,Normal

查看源码发现:

def forward(line):
    tuple5 = list()
    tuple5.append(line[4])
    tuple5.append(line[6])
    tuple5.append(line[3].upper())
    if(line[5] != ''):
        tuple5.append(int(line[5]))
    else:
        tuple5.append('')
    if(line[7] != ''):
        tuple5.append(int(line[7]))
    else:
        tuple5.append('')
    return tuple5

def retreat(line):
    tuple5 = list()
    tuple5.append(line[6])
    tuple5.append(line[4])
    tuple5.append(line[3].upper())
    if(line[7] != ''):
        tuple5.append(int(line[7]))
    else:
        tuple5.append('')
    if(line[5] != ''):
        tuple5.append(int(line[5]))
    else:
        tuple5.append('')
    return tuple5

normal_flow = list()
abnormal_flow = list()
for line in lines:
    #normal 5-tuple information
    if(line[26] == '0' and line[3] == 'tcp'):
        normal_flow.append(forward(line))
        normal_flow.append(retreat(line))

    #abnormal 5-tuple information
    elif(line[26] == '1' and line[3] == 'tcp'):
        abnormal_flow.append(forward(line))
        abnormal_flow.append(retreat(line))
  • 如果Label 是0并且Prototcp时,表示它是 tcp 协议的正常流量,则会进入normal_flow 这个数组中;如果Label 是1并且Prototcp时,表示它是 tcp 协议的异常流量,则会进入abnormal_flow 这个数组中;
  • 我尝试运行了forwardretreat这两个函数并且打印出了部分数据
    normal flow :[['192.168.1.212', '192.168.1.11', 'TCP', 55938, 80], ['192.168.1.11', '192.168.1.212', 'TCP', 80, 55938]]
    abnormal flow :[['192.168.1.11', '192.168.1.102', 'TCP', 51245, 4444], ['192.168.1.102', '192.168.1.11', 'TCP', 4444, 51245]]
    
  • 上图数据存储了源 Ip、目的 Ip、协议、源端口、目标端口
normal_pcap = []
abnormal_pcap = []
for pkt in pkts:
    if(pkt.time > timestamps_start[attacks.index(attack)] and pkt.time < timestamps_end[attacks.index(attack)]):
        tuple5 = list()
        if(pkt.payload.name == 'ARP' and (pkt.payload.psrc in host_address[attack] or pkt.payload.pdst in host_address[attack])):
            tuple5.append(pkt.payload.psrc)
            tuple5.append(pkt.payload.pdst)
            tuple5.append(pkt.payload.name)
            tuple5.append('')
            tuple5.append('')
        elif(pkt.payload.name == 'IP' and (pkt.payload.src in host_address[attack] or pkt.payload.dst in host_address[attack])):
            tuple5.append(pkt.payload.src)
            tuple5.append(pkt.payload.dst)
            tuple5.append(pkt.payload.payload.name)
            tuple5.append(pkt.payload.payload.sport)
            tuple5.append(pkt.payload.payload.dport)
        if(tuple5 in normal_flow):
            normal_pcap.append(pkt)
        elif(tuple5 in abnormal_flow):
            abnormal_pcap.append(pkt)
  • 上图代码是处理抓取的pcap文件,筛选了符合条件的流量包(例如 Ip 地址、协议、时间戳等等),然后同样也是存储了源 Ip、目的 Ip、协议、源端口、目标端口
  • 有了以上的基础,我们来看如何处理论文中提到的数据集

USTC-TFC2016 数据集的处理

以下是处理的源代码(有稍作删减)

benigns = ['WorldOfWarcraft', 'Weibo-1', 'SMB-1', 'Skype', 'Outlook', 'MySQL', 'Gmail', 'FTP', 'Facetime', 'BitTorrent']
for benign in benigns:
    pkts = rdpcap("Benign/" + benign + ".pcap")
    s_pkts = pkts.sessions()
    del_keys = []

    # Delete all Not TCP Session
    for key in s_pkts.keys():
        if 'TCP' not in key:
            del_keys.append(key)
    for key in del_keys:
        s_pkts.__delitem__(key)

    # Sessions into flow, each flow is bidirectional and identified by 5-tuple
    f_pkts = []
    for i,j in itertools.combinations(s_pkts.keys(),2):
        if [p in j for p in i.split(' ')].count(False) == 0:
            f_pkts.append(sortappend(s_pkts[i],s_pkts[j])) # sortappend函数是用于合并两个数据包列表,并按照数据包的时间戳对它们进行排序。
    del s_pkts
    del del_keys
    del pkts

    # Processing flow data to input data
    data=[]
    img_shape=(60,3) # n=60,l=3 的情况
    for flow in f_pkts:
        f = []
        for pkt in flow[:img_shape[1]]:
            if(pkt.payload.name == 'ARP'):
                #random IP
                pkt.payload.psrc = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
                pkt.payload.pdst = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
            elif(pkt.payload.name == 'IP'):
                #random IP
                pkt.payload.src = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
                pkt.payload.dst = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
            #random MAC
            pkt.src = rand_mac()
            pkt.dst = rand_mac()
            #packet -> bytes
            # Scapy 库的 raw() 函数,该函数可以将数据包转换为其原始字节序列。然后,使用列表推导式将字节序列中的每个字节提取出来
            pkt_50 = [field for field in raw(pkt)]
            # 后面不够的进行填0
            pkt_50.extend([0]*img_shape[0])
            f.extend(pkt_50[:img_shape[0]])
        #deal with pcaket<3
        if(img_shape[1]-len(flow) > 0):
            f.extend([0]*img_shape[0]*(img_shape[1]-len(flow)))
        data.append(f)
    np.save('self_normal/' + benign + '.npy', data)

Mirai-RGU数据集的处理

  • 本人查看源代码发现,Mirai-RGU数据集和USTC-TFC2016 数据集的处理类似,这里就不在赘述了

2. CNN 模型是有监督模型,如何和无监督模型联系起来

CNN(卷积神经网络)模型可以是有监督的,也可以是无监督的,具体取决于其在训练过程中是否使用标记的训练数据。

有监督学习:在有监督学习中,CNN 模型需要标记的训练数据来进行训练。这些标记的训练数据包括输入样本和相应的目标标签。CNN 通过学习输入数据与目标标签之间的关系,来进行分类、回归等任务。例如,在图像分类任务中,CNN 模型接收图像作为输入,并输出该图像所属的类别。

无监督学习:在无监督学习中,CNN 模型没有标记的训练数据,而是通过自身学习数据的内在结构或特征来进行训练。这种模型通常用于聚类、降维、特征学习等任务。例如,自编码器是一种常见的无监督学习模型,它通过学习将输入数据编码为潜在空间中的表示,并尝试通过解码器重构输入数据。

以下是该论文的模型源码:

class CNN_AUTO(nn.Module):
	def __init__(self,input_size):
		super(CNN_AUTO, self).__init__()
		self.conv1 = nn.Sequential(		 # input shape (1, 28, 28)
			nn.Conv1d(
				in_channels=1,			  # input height
				out_channels=32,			# n_filters
				kernel_size=6,			  # filter size
				stride=1,				   # filter movement/step
				padding=5,				  # if want same width and length of this image after Conv2d, padding=(kernel_size-1)/2 if stride=1
			),							  # output shape (16, 28, 28)
			nn.ReLU(),					  # activation
			nn.MaxPool1d(kernel_size=2),	# choose max value in 2x2 area, output shape (16, 14, 14)
		)
		input_size = (1,32,int(((input_size[1]-6+5*2)/1+1)/2))
		self.conv2 = nn.Sequential(		 # input shape (16, 14, 14)
			nn.Conv1d(32, 64, 6, 1, 5),	 # output shape (32, 14, 14)
			nn.ReLU(),					  # activation
			nn.MaxPool1d(kernel_size=2),				# output shape (32, 7, 7)
		)
		input_size = (1,64,int(((input_size[2]-6+5*2)/1+1)/2))
#		self,flatten = nn.Linear(np.prod(input_size[1:]), 10)
		self.dense1 = nn.Linear(np.prod(input_size[1:]), 1024)
#		self,dense1_1 = nn.Linear(1024, 10)
		self.dense2 = nn.Linear(1024, 25)
		self.cnn_out = nn.Linear(25, 10)	# fully connected layer, output 10 classes
		self.encoder_1 = nn.Linear(1024, 512)
		self.encoder_2 = nn.Linear(512, 256)
		self.decoder_1 = nn.Linear(256, 512)
		self.decoder_2 = nn.Linear(512, 1024)