工控协议漏挖入门之初尝boofuzz与电线鲨鱼抓包分析Modbus

0x00 前言

并非有意开新坑的,但是其实昨天上课学到了一下下,然后四节课里面塞了那么多东西感觉云里雾里的,所以想趁着还没忘记赶紧总结一下,以供日后不时之需,其实学习到的这方面的东西尚且非常浅薄。。。还请海涵

而且听说工控的溢出漏洞其实很难转化成RCE,因为缺少很多库函数,但是,也恰恰是因为这是工控,如果存在远程的dos漏洞,就足以形成损失了,所以还请不要在fofa乱测试,如果真的伤及生活中真正的人,比如更改了某个线圈寄存器的状态,导致某个机械臂或者闸门伤到人了。。。。这是很不好的事情

请先下载Modbus Poll、Modbus Slave这两个模拟器备用

如果想要较为完整的介绍,可以看看https://getiot.tech/zh/modbus/,还涉及到Modbus协议相关开发内容了

https://mp.weixin.qq.com/s?__biz=MjM5NzA1OTc2MA==&mid=2651357872&idx=2&sn=27aeb15dce3d09c5d6003c2b2ab7a2ef&chksm=bd23ef6f8a546679b530b8dff4836fd29c58ff7df70df95c0daf1bf814b09005df7422394f8d&scene=27

更为详细的基础知识,但是似乎直接看这个也有点劝退

0x01 Modbus协议

0x011 Modbus协议

复制百度百科:

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

其实可以这样理解:Modbus算是一个主站(poll那个模拟软件,客户端,发起控制请求)操控从站(slave,反而是服务端,可以理解为,这里是负责为主控服务的)的软件

典型的主设备包括现场仪表和显示面板,典型的从设备为可编程逻辑控制器(PLC)。

0x012 Modbus通信模式

Modbus通信有两种模式:RTU、TCP

0x0121 Modbus RTU(RemoteTerminal Unit)

Modbus RTU 通常基于 RS-485 串行通信链路,RS-485 总线布线规范规定其必须是总线式拓扑结构。在实际布线中,通常采用手牵手菊花链布线方式。例如,Slave 1/2/3 三台 RS-485 设备,Slave1 的 485+ 接入 Slave2 的 485+ 端口,Slave2 的 485+ 再连接到 Slave3 的 485+ 端口,以此类推,485- 的接线方式和 485+ 一样。

似乎单台电脑是模拟不了这种的,下面这个才是能依据本人目前的设备能够模拟出来的

可以模拟,是我开小差了,请使用MX虚拟串口来进行模拟

0x0122 Modbus TCP

相对于使用串行链路的 Modbus RTU,Modbus TCP 基于以太网通信,因此其网络拓扑结构更为灵活,就是说可以是星型或者总线型的都可以。从串行链路上一主多从的构造,演变为多客户端/多服务器端的构造模型。

modbus TCP和modbus RTU基本相同,但是也存在一些区别

a.从机地址变得不再重要,多数情况下忽略。从某种意义上说从机地址被IP地址取代;

b.CRC校验变得不再重要,甚至可以忽略。由于TCP数据包中已经存在校验,为了不重复造轮子,modbus TCP干脆取消了CRC校验。

使用 Modbus TCP,主站设备(客户端)可以通过 IP 地址找到 Modbus 从设备(服务器),并通过 Modbus 网关连接到另一个 Modbus RTU 网络。

0x013 Modbus寄存器的种类

  • 线圈寄存器(Coil Register):线圈寄存器用于控制从站设备的开关状态,通常用于控制外部设备的开关(例如,打开或关闭电机、阀门等)。线圈寄存器中的数据是布尔型(即只能是开或关,1或0)。线圈寄存器的地址范围通常为0~9999。
  • 保持寄存器(Holding Register):保持寄存器用于存储数据,通常用于存储从站设备的参数、状态信息等。保持寄存器中的数据可以是各种数据类型,如整数、浮点数等,具体取决于设备的实际需求。保持寄存器的地址范围通常为40000~49999。

0x014 Modbus常用功能码

有效功能码有二十几种,但是一般使用上都以1、2、3、4、5、6、15、16这八种最为常用,以及另外特殊使用的20、21两种,此为General Reference Register,绝大部份的Modbus设备并不会提供此Register。

0x015 Modbus异常响应

modbus存在以下四种情况:

  • 正常接收,正常处理,返回正常响应报文。
  • 因为通信错误等原因,造成从站设备没有接收到查询报文,主站设备将按超时处理。
  • 从站设备接收到查询报文,但报文存在错误(例如 LRC、CRC 错误等),从站设备将丢弃报文不响应,主站设备将按超时处理。
  • 从站设备接收到正确的报文,但是超过处理范围(例如不存在的功能码或者寄存器等),从站设备将按返回包含异常码(Exception Code)的响应报文。

那么如果接收正确/出错了的话,modbus的接收端会返回如下的数据报文:

异常功能码 = 正常功能码 + 0x80

前人之述备矣:

0x016 Modbus安全问题

由于Modbus出现的很早很早,上世纪七八十年代出现并被广泛应用,其设计阶段并没有注重操作权限管理、认证、加密通信等安全问题,导致只要接入网络就能对Modbus进行监听和操控

(1)缺乏认证

在Modbus 协议通信过程中,没有任何认证方面的相关定义,攻击者只需要找到一个合法的地址就可以使用功能码就能建立一个Modbus 通信会话,从而扰乱整个或者部分控制过程。

(2)缺乏授权

Modbus 协议没有基于角色的访问控制机制,也没有对用户分类,没有对用户的权限进行划分,这会导致任意用户可以执行任意功能。

(3)缺乏加密

Modbus 协议通信过程中,地址和命令全部采用明文传输,因此数据可以很容易的被攻击者捕获和解析,为攻击者提供便利。

(4)功能码滥用

功能码滥用是导致Modbus 网络异常的一个主要因素。例如不合法报文长度,短周期的无用命令,不正确的报文长度,确认异常代码延迟等都有可能导致拒绝服务攻击。

现实中一些厂商会在Modbus的上层实现鉴权、加密传输之类的机制以修复这些安全问题,问题得到解决,但也有可能又会引入新的漏洞

那么,如果想进行工控漏洞研究,需要对协议进行fuzz,如何进行fuzz呢,入门阶段可以尝试一下对协议进行无状态fuzz的boofuzz,这个是不需要我们进行太多测试用例生成的方面的设置的。基本思想就是不断改变保持寄存器的状态,主从设备不断同步,然后观察主从设备的连接情况,以探测是否有crash

0x02 Modbus协议Fuzz(TCP模式下)

0x021 Modbus主从端建立连接(TCP模式)

为了模拟Modbus的通讯过程以研究其协议安全,可以使用Modbus Poll、Modbus Slave这两个模拟器来协助我们的学习

0x0211 Modbus slave(从站)

更改为TCP/IP

默认监听502端口

0x0212 客户端Modbus poll(主站)

更改为TCP/IP

记得要把默认的0.0.0.0(路由器的默认地址)改为127.0.0.1(本机),然后启动

Tx 代表发送帧数,

Err代表错误帧,

ID表示从站的ID号,

F代表功能码,这里的03是写寄存器的

SR代表轮询周期,

右下角显示IP地址和端口号。

然后发现两者的这些保持寄存器实现了同步,更改任意一个,另一个都会被同步变化,比如:

更改slave或者poll的40001地址的11451为1919,可以观察到:

这样我们就完成了poll主站与slave从站的连接,并且尝试了一次对保持寄存器的修改

0x022 boofuzz(TCP)

比如

# 026f 0000 0006 01 06 0000 000a

其中有很多东西是不能fuzz的,确定的,比如块的长度、功能码等,这些要是非法的话,会被丢掉不处理的

块的长度是动态的,其中,块包含了:

  1. UnitID:一个字节长的字段,表示Modbus协议中的单元标识符。
  2. FuncCode:一个字节长的字段,表示Modbus协议中的功能码。
  3. RefNumber:一个两字节长的字段,表示Modbus协议中的参考号。
  4. Data:未知字节长的字段,此处希望更改寄存器的话,是两字节,一个int值,表示Modbus协议中的数据。

那么如果我们想要对其进行fuzz,可以从事务处理标识箱(说白了是TransID,第几号传输报文)、Data来进行fuzz

那么代码块的核心部分就确定了,感谢老师


# 026f 0000 0006   01 06 0000 000a
s_initialize("modbus_tcp_fuzzer")
s_bytes(value=b'\x02\x6f', name='TransID', size=2, fuzzable=True)
s_bytes(value=b'\x00\x00', name='ProtocolID', size=2, fuzzable=False)
s_size(block_name="Modbus", length=2, endian=BIG_ENDIAN, fuzzable=False)
with s_block(name='Modbus'):
    s_byte(value=b'\x01', name='UnitID', fuzzable=False)
    s_byte(value=b'\x06', name='FuncCode', fuzzable=False)
    s_bytes(value=b'\x00\x00', name='RefNumber', size=2, fuzzable=False)
    s_bytes(value=b'\x00\x0a', name='Data', size=2, fuzzable=True)


session.connect(s_get("modbus_tcp_fuzzer"))
session.fuzz()

启动,然后就是不断检测对面的crash情况,这里问了一下老师,原来是通过测量服务是否掉线的方式,当然可以通过钩子函数什么的,此处并未深入

一个有趣的现象是,slave变化是十分迅速的,但作为沟通主站、fuzz的主要目标的poll,变化却很慢,然后关掉poll发现数据仍然在变化,这让我知道原来slave是开放502端口那个,一开始理解错了。poll正如其意,作为周期性(这里是1s)轮询各个slave的主站点,而slave则负责了通过TCP来接收真正的设备发出的讯号。

0x033 Wireshark分析(TCP)

Wireshark看看,发现这个网卡是流量最大,最持续的,理应就是fuzz的那个口

可以看到Wireshark是支持tcp通信下modbus协议的识别和格式的解析的,而且一个是query,poll轮询请求,一个response,是boofuzz发出的

MAC(此处没有体现),IPV4,TCP,层层封装之下,才是应用层的Modbus包,比如这个包是从0x05f6开始

对比boofuzz的回显,原来sending12bytes并非是在这里用modbus协议发包吗,哦哦,这么大个Func: 3: Read Holding Registers没看到。。。这是在读寄存器呢。47789是response查询结果,40001保持寄存器的那个结果是ba ad,也就是

好,尴尬了,到这里然后Wireshark崩溃了,幸好截图了,所以后面包的编号有点对不上。。。重新启动fuzz和Wireshark,两三分钟崩溃一次,

这次一打开直接关注最后一个Func: 6: Write Single Register发包

这12字节是完全一致的,而且电线鲨鱼已经帮我们分析每个字段的意思了,感谢鲨鲨(

0x03 Modbus协议Fuzz(RTU模式下)

工业常用的还是RTU

0x031 Modbus主从端建立连接(RTU模式)

常用的还是RTU,这样配置很轻松就连上了

0x032 boofuzz(RTU)

请注意这里需要关闭主站,然后让boofuzz充当roll的端口,去跟slave”沟通”

变化就是,RTU需要crc校验字段了,以及需要波特率、端口这些

0x033 Wireshark分析(RTU)

似乎Wireshark并不支持直接抓RTU的包来分析,可惜,如果有办法的话请务必告诉我

0x04 总结

学习了modbus协议的一些知识点

搞清boofuzz脚本怎么写,怎么用

抓modbus两种func定义功能的协议包查看其细节

评论

  1. 7 月前
    2024-4-04 11:34:20

    桌宠可爱死了哈哈哈

    • 博主
      风之子.
      6 月前
      2024-4-10 20:17:04

      哎呀不小心截图截到了,感谢师傅观看😊

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
粤ICP备20015830号