一次穿透 iptables 防火墙的 UDP ***报文真实案例分析

一次穿透 iptables 防火墙的 UDP ***报文真实案例分析,揭示了一些在使用 iptables 时不为人知的安全隐患,但愿能给你们带来帮助! html

 

原文连接 http://blog.chinaunix.net/uid-1728743-id-3457803.htmlshell

这类***确实少见,也是一次好的排错过程,因此转载以总结下来.安全

 

现象服务器

一次偶然的机会,发现一台服务器出现了极不正常的状况:CPU 消耗巨大tcp

经过用 top 观察,发现 snmpd 进程持续走高ide

image

 

抓包ui

重启 snmpd 进程,在正常了几分钟后忽然又重现异常,此时感受是有特殊数据包进来形成的。spa

为了验证猜想,对 UDP/161 端口进行了抓包,发现了些端倪。.net

image

经过抓包发现,原来真的有***,从抓包看,***是突发性质的,当有***时:unix

频率:≈ 10Hz

周期:≈ 0.1s

类型:请求 snmpd 的根信息 "Linux C"

原理:经过请求比较高级别的根内容,使系统处于高负载状态;因为频率较高,高负载状态很难消除。

恶意性:为了突破某些无状态防火墙,来源端口 sport 特地也是用了与 snmpd 服务相同的 UDP/161,很阴险。

 

 

规则检查

不过奇怪的问题来了,个人防火墙是基于状态检测的,能够轻易区分进来的报文是主动请求进来的,仍是我主动发出后的回包。

既然这样,为什么 snmpd 报文仍是会被系统收到并处理呢?是否是 iptables 规则使用存在问题?

经查看,规则很简单,大致以下:

iptables -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A INPUT -i lo -j ACCEPT

iptables -A INPUT -p tcp --dport 80 -j ACCEPT

iptables -A INPUT -p udp --dport 161 -s IP1 -j ACCEPT

iptables -A INPUT -p udp --dport 161 -s IP2 -j ACCEPT

iptables -A INPUT -p udp --dport 161 -s IP3 -j ACCEPT

iptables -A INPUT -j DROP

经过观察,未发现异常,针对 snmpd 报文,系统只容许了 3 个特殊 IP 访问,那么就奇怪了,这个***报文是怎么进来的呢?

 

 

conntrack 特性分析:UDP 与 TCP 的差别

众所周知,iptables 只是 userspace 的 controller,真正的主体是 netfilter,而 conntrack 则是 netfilter 的一个重要组成部分。

conntrack 的核心价值,就是为 Linux 实现了基于状态的防火墙。

在 Linux 中,当启用了 conntrack 后,即便是 UDP 报文,也是有“新建链接”(NEW)和“已链接”(ESTABLISHED)概念的。

不过与 TCP 不一样的是,TCP 是有三次握手的,经过分析三次握手时数据报文头部的合规性能够知道当前数据是否有效。

可是,UDP 则不一样,在 Linux 中,只有“去”和“回”两个概念,当系统第一次看到 UDP 包,视为 NEW,看到该包的回包后,视为 ESTABLISHED。

 

推理

知道了这个,大概能够猜出问题所在了,必定和 conntrack 有关。

猜想失控过程:

一、系统未启动 conntrack 时,已经有***报文 request 进来了

二、当系统启动 conntrack 机制时,刚好有系统回 response 包,被系统 conntrack 当作了主动发出数据,是 NEW 状态

三、系统的 iptables 规则最后有阻断功能,这个包虽然被拦掉

但后续***报文用一样的 SIP/SPORT 访问一样的 DIP/DPORT,致使 hash 后认为是同一条链接

这次的***报文被无当作上一次 NEW 状态的回包,这次该链接变成了 ESTABLISHED 状态

四、从此全部 SIP/SPORT 到 DIP/DPORT 的数据均通行无阻,致使最开始看到的场面

 

验证

为了证实推理是正确的,刻意写了一个 shell,在第一行 -m state --state RELATED,ESTABLISHED -j ACCEPT 后加入 sleep 以增长重现几率

实验代表,推理彻底正确,UDP ***报文的回包被当作了 NEW,而真正的***则成了 ESTABLISHED。

第一句的位置,也是致使问题出现的主要缘由。

 

尝试解决

为了杜绝这种状况的发生,思考,将 state 匹配放到最后是否可行?

答案是“不能够”,由于同样可能发生前面说的时间差致使的状态误判状况发生。

怎样作才是最稳妥的办法呢?

想了个办法:

一、先 service iptables stop

二、先写第一行规则,无条件 -j DROP 任何东西

三、写其余规则,该怎么写怎么写

四、当规则生效后等一段时间(保证此时间超过 snmpd 报文打进来后的系统响应时间),再删掉第一行规则,让数据涌进来(像开闸放水)

惋惜,经过实测,***报文仍然能够进来,这个太奇葩了!

/etc/init.d/iptables stop 行为分析

看到上述疑点,不得不怀疑 iptables 脚本中 stop 功能的实现了。

理论上 iptables 在 stop 的时候,不只会清除全部规则,也会删掉全部 iptables 所用到的模块,这其中也包含了 conntrack。

推理:若是 iptables 的 stop 功能工做异常,那么就没法正确卸载 conntrack,那么上述方法就无济于事了。

 

image

 

红色部分是清除 conntrack 的关键语句。继续跟踪:

 

IPTABLES=iptables

IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6

 

脚本最初有上述定义,也就是说,最后 IPV 的取值是 "ip",卸载模块时卸载的是 ip_conntrrack。

手动验证一下:

 

[root@platinum-PT ~]# modprobe ip_conntrack

[root@platinum-PT ~]# iptables -A INPUT

[root@platinum-PT ~]# /etc/init.d/iptables stop

Flushing firewall rules: [ OK ]

Setting chains to policy ACCEPT: filter [ OK ]

Unloading iptables modules: [ OK ]

[root@platinum-PT ~]# lsmod |grep conntrack

nf_conntrack_ipv4 22028 0

nf_conntrack 67784 1 nf_conntrack_ipv4

[root@platinum-PT ~]#

 

经过观察发现,conntrack 确实没有被卸载,但注意到了一点,模块名字不是 ip_conntrack 而是 nf_conntrack。

彷佛明白了,验证一下:

 

[root@platinum-PT ~]# cat /etc/redhat-release

Red Hat Enterprise Linux AS release 4 (Nahant Update 2)

[root@platinum-PT ~]# uname -r

2.6.24.4-7

[root@platinum-PT ~]#

知道缘由了:

  • 系统是 RHEL 4U2,默认内核应该是 2.6.9,那时的 netfilter 使用的 conntrack 模块叫 ip_conntrack
  • 系统的当前内核是 2.6.24.4,此时的 netfilter 所使用的 conntrack 模块叫 nf_conntrack 和 nf_conntrack_ipv4
  • 因为 RHEL 4U2 的 /etc/init.d/iptables 脚本是为 2.6.9 内核写的,所以在 stop 时不能正确卸载 2.6.24.4 内核的 conntrack

卸载 conntrack 解决方案

修改

rmmod_r ${IPV}_conntrack

改成

rmmod_r nf_conntrack

由于是定制系统,未作详细的判断和动态自适应等工做。

总结

  • 特殊时机生效的 ACL 致使 conntrack 误认为***报文的回包是 NEW
  • ***报文恶意使用固定 sport,使 conntrack 误认为是同一个 connection
  • iptables 规则的逻辑问题导致穿透***有可能发生(虽然是小几率事件)
  • System script 与 kernel 的不匹配致使不能正确卸载 conntrack,使问题依然存在
相关文章
相关标签/搜索