开源工具snmp-set-fuzzer介绍

什么是snmp-set fuzzer

snmp-set fuzzer是一个用于对目标设备的snmp可写oid节点数据进行Fuzz的小工具。项目的地址是https://github.com/dark-lbp/snmp_fuzzer

为什么要编写snmp-set fuzzer这个工具

  • 在对许多工业控制设备进行安全测试时发现大量的设备默认开启了snmp服务并支持snmp写操作,且部分设备使用了默认的snmp community值且无法修改。
  • 希望有一个相对灵活且自动化的小工具能够对这些开放了snmp写权限的设备进行检测从而评估设备的安全性。

如何使用snmp-set fuzzer

在对目标设备进行fuzzing之前我们, 我们首先需要对目标设备的可写OID节点进行扫描,snmp-set fuzzer会使用snmp-get-next请求来遍历所有的oid节点,通过将获取到的数据使用snmp-get请求写回目标设备并根据返回值来判断该oid节点是否可写。

下面是对某个设备进行oid节点扫描并将扫描结果进行保存的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
>>> from snmp_set_fuzz import *
>>> target = '192.168.70.50'
# 定义用于监控设备存活的TCP端口
>>> port = 80
# 定义每个oid节点Fuzz的次数
>>> count = 10
# 获取与目标设备连接的网卡名
>>> nic = conf.route.route(target)[0]
# 创建SnmpTarget,定义各个参数
>>> Target = SnmpTarget(name='test', monitor_port=port, community='private',
oid='.1.3', version=2, target=target, nic=nic, fuzz_count=count)
# 扫描目标设备的snmp oid信息
>>> Target.oid_scan()
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.1.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.2.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.3.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.4.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.2.1.1.4.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.5.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.2.1.1.5.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.6.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.2.1.1.6.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.1.7.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.1.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.1.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.2.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.3.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.4.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.5.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.6.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.2.1.2.2.1.7.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.2.1.2.2.1.7.0 is writeable
..............
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.3.1.1.1.1.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.3.1.1.1.2.0
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.3.1.1.1.3.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.3.1.1.1.3.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.3.1.1.1.4.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.3.1.1.1.4.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.4.1.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.4.1.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.4.2.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.4.2.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] Found oid :1.3.6.1.4.1.95.2.4.3.0
[INFO ][snmp_set_fuzz.oid_scan] 1.3.6.1.4.1.95.2.4.3.0 is writeable
[INFO ][snmp_set_fuzz.oid_scan] End of MIB
# 保存扫描的结果
>>> Target.save_scan_result()
>>> # This cmd will save all result to ./output folder.

snmp-set fuzzer也同样支持从保存的pcap数据包文件直接获取可写oid节点清单。

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> from snmp_set_fuzz import *
>>> target = '192.168.70.50'
>>> port = 80
>>> count = 10
>>> nic = conf.route.route(target)[0]
>>> Target = SnmpTarget(name='test', monitor_port=port, oid='.1.3', version=2, target=target, nic=nic, fuzz_count=count)
>>> # 查看前目标的可写oid清单
>>> print(Target.set_packets)
>>> []
>>> # 调用read_test_case_from_pcap来获取可写oid节点数据
>>> Target.read_test_case_from_pcap('./output/192.168.70.50_snmp_set_packet_list.pcap')
>>> print(Target.set_packets)
<192.168.70.50_snmp_set_packet_list.pcap: TCP:0 UDP:37 ICMP:0 Other:0>

在直接扫描或读取以前扫描生成的pcap文件获取到可写oid节点后,就可以使用snmp-set fuzzer进行fuzzing测试了。下面是执行fuzz的例子,测试过程中将基于各个可写oid节点的数据类型对写入数据进行随机变异后使用snmp-set请求发送给目标设备并对其反馈进行检测,当目标设备对某个写请求不进行反馈或反馈异常时,会对目标设备的存活进行检测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> from snmp_set_fuzz import *
>>> target = '192.168.70.50'
>>> port = 80
>>> count = 10
>>> nic = conf.route.route(target)[0]
>>> Target = SnmpTarget(name='test', monitor_port=port, oid='.1.3', version=2, target=target, nic=nic, fuzz_count=count)
>>> Target.read_test_case_from_pcap('./output/192.168.70.50_snmp_set_packet_list.pcap')
>>> Target.fuzz()
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 0/10
[WARNING ][snmp_set_fuzz.fuzz] Target not response with snmp set packet in packet NO.0,TestCase No.0
[INFO ][snmp_set_fuzz.fuzz] Target is still alive!
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 1/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 2/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 3/10
[WARNING ][snmp_set_fuzz.fuzz] Set failed with error code: wrongLength (8) in packet NO.3,TestCase No.0
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 4/10
[WARNING ][snmp_set_fuzz.fuzz] Set failed with error code: wrongLength (8) in packet NO.4,TestCase No.0
[INFO ][snmp_set_fuzz.fuzz] Running test case No.0 5/10
[WARNING ][snmp_set_fuzz.fuzz] Set failed with error code: wrongLength (8) in packet NO.5,TestCase No.0
........
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 4/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 5/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 6/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 7/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 8/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.4 9/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.5 0/10
[INFO ][snmp_set_fuzz.fuzz] Running test case No.5 1/10
[WARNING ][snmp_set_fuzz.fuzz] Target not response with snmp get packet in packet NO.1,TestCase No.5
[ERROR ][snmp_set_fuzz.fuzz] Can't Connect to Target at TCP Port: 80
>>> # 目标设备已崩溃
>>> # 调用save_fuzz_result来保存fuzz结果到./output目录
>>> Target.save_fuzz_result()

工控设备默认开启SNMP的危害

在以往的测试过程中通过snmp-set fuzzer发现过一些工控设备的漏洞,主要集中在对snmp写操作的数据没有进行有效的校验。例如某设备支持通过snmp写操作来修改设备网卡的mac地址,但是没有对mac地址的长度进行校验,只要传入过长或者过短的mac地址都会造成设备瘫痪。还有些设备的网卡可以通过snmp写操作来开启和禁用,这样直接就会造成设备的网络中断影响业务。此外通常厂商还会有其自定义的私有oid节点,这些节点也很可能会存在一些安全问题,导致设备出现各种预期外的异常。

在使用shodan对开启了snmp服务的控制设备进行了简单的搜索后可以发现,目前网络上还是有不少开启了snmp服务的控制设备直接暴露在互联网中,如果这些设备对snmp写操作没有进行有效控制的话,将会面临极大的安全风险。下面是暴露在互联网上的开启了snmp服务控制设备截图。

SNMP协议在带来方便管理的同时也引入了一些安全性上的问题,这些问题在工业控制领域显得尤为突出,大量的设备默认开启SNMP协议且使用public、private等默认community值进行验证。因此如果在实际生产环境中不需要使用到SNMP服务的话,建议还是关闭SNMP的功能,