通过iptables的u32模块实现对应用层协议匹配

本文由CollinXia(https://blog.csdn.net/qq_42768012)原创,未经许可禁止转载

iptables是Linux环境下功能强大的防火墙规则管理工具,因为最近在做一个工控防火墙的项目,就想看用iptables能不能实现简单的应用层协议匹配,之前看到过很多讲Netfilter有一个Layer7模块可以实现应用层匹配的,但是Layer7实在是太老了,只支持2.x的内核版本,在新版内核下完全无法编译安装,之前反复尝试了很多次还是不能用。后来发现了这个叫做u32的模块,他的逻辑是并不管你什么层,只管一次拿32bits出来做匹配,那其实我们只需要把网络层和运输层的报头跳过去,剩下的不就是应用层了吗?

基于这种想法,这里以工控网络中最常见的Modbus协议为例,做了一次简单的应用层匹配

前期准备工作

首先一般情况下Ubuntu是自带iptables的,如果没有,可以自行安装

sudo apt install iptables 

安装成功后输入iptables会提示iptables v1.6.1: no command specified(我虚拟机上自带的1.6.1版本)说明成功

理论基础

iptables一般情况下是不支持对数据包内部信息进行匹配的,只能根据诸如-saddr-dport这类关键字来匹配源/目标端口或者地址
好在iptables支持各种模块的扩展,这里使用了一个叫做“u32”的模块来帮助我们对于数据包内部的内容进行匹配识别

u32模块一次可以读取32bits内容,同时可以执行跳跃、移位、匹配等功能,其使用样例可表示为:

iptables -m u32 --u32 "Start&Mask=Range"

表示从Start(数据包的第几个字节,从0计数)处开始读取32位,并与mask做逐位与运算后判断是否在range的范围内,若在则返回true,也就是匹配到符合规则的数据包;否则返回false,即不对该数据包做任何处理

若要匹配到应用层的内容,我们首先要把ip头和tcp头跳过
首先是ip报头
ip数据报示意图
我们发现报头长度是位于第0个32bits字的第4~7位上,注意到IP报头中的报文长度是以4字节为单位存储的,所以在跳过时候还要乘四
在这里我用0>>22&0x3c@来实现这个功能
0表示从第0个32bits字开始,>>22表示向右移22位,&0x3c是和0x3c做逐位与运算 实际上这里本应右移24位,但是我们要把长度乘4,那么刚好右移22位再和0x3c(00111100)做与运算,得到的就是乘四的报文长度

最后用一个@表示跳过前面这么多字节

对于TCP报文段头部,可以用类似的方法处理,这里就不再详细展开了

成功跳过了ip报头和tcp报头之后,我们此时实际上达到了应用层的内容开头了 这里我测试的是Modbus协议,modbus协议的“功能码”放在了第7个字节的位置,假设当功能码为16的时候,是一个敏感操作,那么我们就可以用7>>24&0xff=0x10来匹配所有功能码为16的包,注意到这里u32模块中用的是十六进制

以上就是利用iptables的u32模块进行对应用层内容进行匹配的理论方案,接下来进行实践测试

实践测试

这里我使用了一个名为Modbus Poll的软件来模拟modbus发包,这个软件是运行在Windows环境下的,使用界面非常清晰明了,就不再说具体使用方法了
linux虚拟机上,用python写了一个简单的监听程序,每次收到消息后会在Terminal中输出,并以明文直接返回一个“Message Received"
(注意这个返回实际上是不符合modbus协议通讯规定的,这一点在稍后还会再次提到)
首先打开监听程序,一般情况下Modbus协议通过502端口通讯,这里就是用的是502端口(实际上modbus poll软件中可以指定任意端口)
运行在LinuxTerminal中的监听程序
接下来点击Modbus Poll软件的Connection选项,设置连接参数,这里注意我们要使用的是Modbus TCP/IP模式,每次发包间隔(Delay Between Polls)默认好像是10ms,这里我为了方便改为了1000ms,是最大支持的值
下面的ip地址填写自己虚拟机的地址,点击OK就可以建立连接
在这里插入图片描述
正常情况下建立连接后就会开始发包,效果如下
在这里插入图片描述
注意到上面软件中提示的Transaction Error,实际上就表示接收程序发回的数据包中是不符合规定的无效协议
有兴趣的朋友可以了解一下pymodbus这个python库,定义了modbus相关的函数,可以编写一个符合modbus通讯协议的程序进行进一步测试
这里只是简单的一个小测试 所以没有在意这一点
左边的Terminal中显示的就是Modbus Poll发出的数据,从中不难看出第七个字节为’0x03’,就表示当前操作的功能码为3.
这时我们尝试一下发送一个功能码为16的包,下面Terminal中一长串的那条就是
在这里插入图片描述
现在在iptables中加入一条规则

iptables -A INPUT -m u32 --u32 "0>>22&0x3c@ 12>>26&0x3c@ 7>>24&0xff=0x10" -j DROP

对于功能码为16的包将会静默丢弃
再发送一次

在这里插入图片描述
此时我们发现提示的不再是Transaction Error, 而是Timeout Error
Modbus要求从机在收到主机的数据包后返回数据包,而我写的监听程序确实会在收到后返回数据包,只不过不符合规则,所以之前会产生Transaction Error,而这里的Timeout Error则表示从机未在有效时间内返回一个数据包
再来看Terminal这边,我们发现并没有提示收到功能码为0x10的数据包,说明刚刚的那条16号指令已经被丢弃掉了

可能会遇到的错误

如果在iptables设置丢弃规则之前就出现timeout error的情况,是因为Modbus Poll没有在规定时间内收到返回的数据包,可以按照以下顺序进行troubleshooting

  1. 修改监听程序,每次收到包后返回一个包(内容任意)
  2. 查看Modbus Poll软件中的计时时间Connection-Connect-Response Timeout 最大可以改到1000ms
  3. 查看IP地址和端口与监听程序是否匹配
  4. 查看本机防火墙设置

简要总结

  • iptables可以通过u32模块实现对于应用层内容的识别与匹配

  • 可以通过取出报头长度字段并用@来跳过ip和tcp的报头,从而到达应用层的位置

  • u32一次读取的是32bits(四个字节),在匹配的时候要先用掩码提取有效位

本文由CollinXia(https://blog.csdn.net/qq_42768012)原创,未经许可禁止转载

发布了5 篇原创文章 · 获赞 5 · 访问量 254

猜你喜欢

转载自blog.csdn.net/qq_42768012/article/details/105055269