Backbone Network Traffic Anomaly Detection

概述

目的

通过持续对流量数据的监测,不断丰富及提取合适的流量特征,以实时检测流量异常
几种主要结果:

  • scanmon
  • ddosmon
  • 暴力破解/垃圾邮件服务器等

Pivot模型

netflow数据是流量的摘要数据,因此通过netflow来检测流量异常也就只能通过摘要统计来提取特征。

一条原始netflow数据形如:

1
2018-03-12_10:56:18 1 TCP 111.75.213.6:80 -> 220.160.22.245:49948 ...PA... 0 5 6888

这样的数据,只有基本的发生时间,持续时长,来源/目的 的IP:端口等等基本信息。

一般情况下,单条数据无法形成有效的事件鉴别特征,如果我们想深入观察某个IP的状况,我们需要累积一定时间窗口内的与该IP相关的所有数据来观察,比如针对目的IP 189.203.188.074,我们累积一段时间的来源流量可以得出如下统计数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
来源Flow总计: 95(in_fsum)
来源Package总计: 95(in_psum)
来源Bytes总计: 113474(in_bsum)
来源包个数统计: _size=1;_nums=63;1=63;(in_pkgnums)
来源包大小统计: _size=28;_nums=63;1500=29;1476=3;1158=2;1063=2;1057=2;847=2;23=2;44=1;1234=1;63=1;1155=1;(in_pkgsize)
来源IP统计: _size=51;_nums=63;211.161.099.125=4;117.035.057.189=2;116.226.156.088=2;120.132.146.115=2;112.126.075.213=2;182.050.118.202=2;182.050.118.223=2;183.129.179.044=2;183.129.249.076=2;218.084.015.245=2;223.223.198.100=1;222.223.026.006=1;120.026.120.015=1;222.222.238.164=1;121.043.148.069=1;123.183.161.199=1;222.222.235.237=1;124.116.223.098=1;124.117.235.138=1;124.126.011.177=1;222.072.158.150=1;221.224.036.022=1;219.235.255.070=1;219.072.250.018=1;211.157.139.253=1;218.031.105.251=1;027.191.130.122=1;036.110.211.029=1;043.247.177.226=1;047.094.246.179=1;058.118.178.084=1;058.213.097.129=1;059.045.193.027=1;059.046.059.174=1;060.205.095.069=1;060.205.146.210=1;061.185.004.122=1;101.081.132.185=1;101.095.027.234=1;101.200.162.015=1;106.002.218.068=1;106.014.238.132=1;110.086.000.162=1;111.113.004.106=1;111.113.027.126=1;114.055.052.155=1;114.141.173.038=1;115.159.102.216=1;115.238.035.233=1;116.231.054.207=1;116.231.155.071=1;(in_ipv4)
来源端口统计: _size=2;_nums=63;0=33;53=30;(in_port)
PS:
1,数值型数据相加
2,枚举型数据组织为count-map
3,枚举类型count-map前面的 _size表示后面的list总长度,也即为unique(list.keys()), _nums表示sum(list.value())

我们可以看到,来源IP比较分散,但是来源Port固定为53和碎片0端口,同时,很多包都是1500 MTU的满包状态,我们就有充足的信心来说,这是一个DNS反射放大攻击事件,是典型的大量的被利用的反射节点给受害者目的IP发送超过1500的大包导致的流量形态。

双向

针对一个IP,我们要想观察它是否异常,我们就需要观察它进出两个方向的流量:

  • 如果是一个Scanner,可能一段时间内只看到它出去的流量,而看不到流向它的流量
  • 如果是一个被DDoS目标,可能一段时间内只看到它进入的流量,而看不到它出去的流量

多层

同时针对一个IP,观察需要按照IP/Proto/Port来分层观察流量:

  • 一个IP上TCP 80端口是正常服务,但是被攻击的是90端口,如果不根据端口来区分观察流量可能会miss
  • 一个IP上TCP 80端口正常服务,但是被攻击的是UDP流量,如果不根据协议来区分观察流量可能会miss

多触发

一般情况下,聚合10分钟的数据,然后整理成所需的格式push到后续流程做检测就够了,但为了数据的时效性以及查全查准,一共有三种检测触发器:

  • STWPop:STW for Sliding Time Window,固定时间窗口流量spike触发检测
  • EarlyPop:一个IP一个时间窗口内最开始出现的部分流量
  • AETWPop: AETW for Auto-Extend Time Window, 当一个固定时间窗口内流量太小,自动扩展更多个时间窗口的数据来触发检测

规则

特征概述

根据我们上述“双向-多层-多触发”模型,任何一个规则条件都可以简单概括为如下:

针对一个IP/IP-Proto/IP-Proto-Port,其:

  • 来源/目的
  • IP/Proto/Port/Flow/PackageNum/PackageSize/Duration
  • 的unique/sum/dispersion/top

是什么

比如上面我们给出的UDP反射放大攻击的case,我们提到“来源IP比较分散,但是来源Port固定为53和碎片0端口”

这里其实需要映射为三个“与关系”的条件:

  • 针对IP 189.203.188.074,其来源IP的dispersion介于[0-5]之间(这里的[0-5]是离散的数值表示)
  • 针对IP 189.203.188.074,其来源Port的Top1等于53
  • 针对IP 189.203.188.074,其来源Port的Top2等于0

规则条件说明

规则条件基本上都是 Calc_DirectionORPosition_Item 的形式。

DirectionPosition

  • in: 进入的流量
  • self_as_dst: 自身作为目的的流量
  • self_as_src: 自身作为源的流量
  • ot: 出去的流量

比如如下5条流量:

1
2
3
4
5
1.1.1.1:1111 -> 2.2.2.2:2221
1.1.1.1:1112 -> 2.2.2.2:2222
1.1.1.1:1113 -> 2.2.2.2:2223
2.2.2.2:2225 -> 3.3.3.3:3331
2.2.2.2:2226 -> 3.3.3.3:3332

整理成我们的Pivot模型后,DirectionPosition的位置如下:

1
2
3
4
5
6
7
8
9
|<--IP 2.2.2.2 Info-->|
in self_as_dst|self_as_src ot
1.1.1.1:1111 -> 2221:2.2.2.2
1.1.1.1:1112 -> 2222:2.2.2.2
1.1.1.1:1113 -> 2223:2.2.2.2
2.2.2.2:2225 -> 3.3.3.3:3331
2.2.2.2:2226 -> 3.3.3.3:3332

Item基本概念:

  • duration: flow持续时长,一个5元组flow数据的持续时长
  • pkgnums:一个flow中的package数,数字越大,说明同一个flow中传输的package越多
  • pkgsize: 一个flow中的package的平均大小,数字越大说明同一个flow中传输的package越大
  • peer: IP+Port是一个peer
  • ipv4:IP
  • ip_b:IP/16
  • ip_c:IP/24
  • port:port
  • tf: TFlags整体
  • fin/ack/syn/psh/rst/urg/nul: TFlags中对应的单独的一位

所有枚举型都会被整理为ordered-count-map,类如{A:100, B:80, … N:1},表示A出现100次,B出现80次,。。。 N出现1次,并且按照大小排序,Calc基本都是根据ordered-count-map的计算

Calc基本概念:

  • lens:unique(ordered-count-map)
  • diss:离散度,0-9999,数字越小说明越分散,5为比较分散,10为比较聚集,20以上为非常聚集。这个数字的step要根据现实流量的态势来调整
  • tops: ordered-count-map的最大的key
  • top2: ordered-count-map第二大的key
  • avgs: ordered-count-map中value的均值,或者sum([k * v for k, v in ordered-count-map])/len(ordered-count-map)
  • span: ordered-count-map中最大最小的key的跨度
  • rate: 比率,往往是和TFlags结合来确定进出tflags的比率

举例:

  • lens_in_ipv4: 进入流量中ip的unique个数
  • diss_ot_port: 出去流量中port的离散程度
  • tops_in_port: 进入流量中最多的port
  • avgs_ot_duration: 出去流量持续时长的均值
  • rate_in_syn: 进入流量的SYN的比例

规则实例

上面说的规则条件都是单个的条件,一条规则实际是多个条件的“与”结合

1
2
3
4
5
6
7
8
9
1 2024->attr tag udp@attack@amp_flood_target
2 2024->cond prot equal 17
3 2024->cond in_locals_spike in_span 15-999999999
4 2024->cond in_global_spike in_span 15-999999999
5 2024->cond diss_in_port in_span 9-999999999
6 2024->cond tops_in_port equal 11211;27960;389;1701;69;111;520;19;1900;123;53;161;137;17;0
7 2024->cond top2_in_port equal 11211;27960;389;1701;69;111;520;19;1900;123;53;161;137;17;0
8 2024->cond tops_in_pkgsize in_span 1000-999999999
9 2024->cond avgs_in_pkgsize in_span 600-999999999

上面是一条完整的规则,attr表示为规则属性,cond表示为规则条件,按行依次解释为:

1
2
3
4
5
6
7
8
9
1 属性:编号为2024的规则是用来匹配“udp@attack@amp_flood_target”事件
2 条件:proto=17 UDP 协议
3 条件:短时间周期内(1小时)流量spike 15倍以上
4 条件:长时间周期内(1天)流量spike 15倍以上
5 条件:来源端口比较聚集
6 条件:来源端口top1是常见反射放大端口其一
7 条件:来源端口top2是常见反射放大端口其一
8 条件:来源包大小top是1000字节以上的大包
9 条件:来源包大小的均值是600以上

规则集

如上一条规则只能捕获一种或几种类型的“udp@attack@amp_flood_target”事件,但是不能捕获所有的“udp@attack@amp_flood_target”事件。同一种事件,在不同的场景下可能有不同的数据形态,在不同的阶段可能有不同的数据形态,因此针对某一特定事件,我们需要多条规则来覆盖所有的场景,以期达到更高的检出率。多条规则之间是“或”的关系,一次匹配,是匹配所有可疑的规则,只要能命中其中任意一条,我们便可判定为真。

同时,存在某些场景下,不同的事件类型但是数据形态非常相似,只有细微的差别,这时候为了规则运维的方便,有一个priority属性,一条数据匹配到多个规则,只会取priority最大的结果。

架构

整体数据流程可以简单的分为“分发 - 汇集 - 匹配 - 输出”四个步骤。

  • 分发:接受不同采集节点的数据,按照一定规则hash(ip)的方式分发数据到汇集的节点
  • 汇集:持续累积给定时间窗口的数据,整理为上述的模型数据,并依照上述模型中的触发条件,将数据POP出来,给到后续匹配流程做检测
  • 匹配:接受数据,根据给定的规则,检测具体的事件
  • 输出:最终结果事件的输出记录

原始数据分发

我们的模型是要针对某一个IP采集进/出两个方向的流量,而一个IP要么出现在src的位置,要么出现在dst的位置,这就意味着,对于一个 A->B 的flow,我们既要将其发送给A对应的汇集点,也要将其发送给B对应的汇集点。

原始数据聚合

聚合的过程是根据数据到达的时间实时进行的,几个要点:

  • 默认聚合过程是汇聚到IP-Proto-Port级别,而IP/IP-Proto级别的数据,是在STWPop的时候动态计算的,这样可以节省内存资源消耗
  • 由于有AETWPop的存在,所以一个固定时间窗口过去后,“即没有多到可以直接判断,又没有少到可以忽略的”这部分数据需要留存下来参与下一次的聚合过程
  • 由于数据分布不均的原因,同一个聚合点缓存数据的Flush实际有两个触发条件,一个是配置的TimeWindow的大小,一个是聚合key的最大值。正常情况下都是前者起作用,但后者可以保证即便某个点数据爆炸,系统还能保持稳定运行

模型数据匹配

匹配过程是一个 “white-black-gray” 串联匹配过程

  • white: 用以初步过滤一些明显的不管新的数据
  • black: 用以匹配出我们已知的恶意事件
  • gray: 对white/black以外的数据进行历史流量建baseline,然后找出可疑的异常

三个节点都是可选的,动态配置。

结果数据输出

略。

附录

规则可用属性

  • tag: 事件类型,基本为proto@event_type@detailed_type 格式
  • index: 规则组,如果想针对不同的数据过不同的规则集
  • priority: 优先级,规则默认优先级是0,如果确认一条规则最精确最准,可以设置一个高优先级,这样会覆盖其他规则的匹配

规则可用条件

大部分规则都符合“规则-规则说明”中的格式,部分特殊的加了额外说明

  • accu: 数据聚合级别,3是IP-PROTO-PORT,2是IP-PROTO,1是IP
  • prot: protocol 协议
  • port: 端口
  • proc_step: 处理阶段,1是STWPop和AETWPop,2是EarlyPop
  • time_win: AETW扩展了几个时间窗口
  • in/ot_fsum: 进/出的flow总计
  • in/ot_psum: 进/出的package总计
  • in/ot_bsum: 进/出的bytes总计
  • diff_flow_io: 进出flow差值
  • diff_flow_oi: 出进flow差值
  • in/ot_locals_spike: 进/出短时间窗口(1小时)spike幅度
  • in/ot_global_spike: 进/出长时间窗口(1天)spike幅度
  • in/ot_spike: in/ot_max(locals_spike/globals_spike)
  • in/ot_spike_type: flow spike/packages spike/bytes spike
  • in/ot_ipv4_count_avg: 进/出IP的平均出现次数
  • in/ot_ipv4_count_top: 进/出IP中出现最多的次数
  • rate_io_avgs_pkgsize: 进/出包大小比率
  • tops_nzport_io_equal: 进/出流量中最大的非0端口是否一致
  • 此处省略100条条件