一次穿透 iptables 防火牆的 UDP ***報文真實案例分析,揭示了一些在使用 iptables 時不爲人知的安全隱患,但願能給你們帶來幫助! html
原文連接 http://blog.chinaunix.net/uid-1728743-id-3457803.htmlshell
這類***確實少見,也是一次好的排錯過程,因此轉載以總結下來.安全
現象服務器
一次偶然的機會,發現一臺服務器出現了極不正常的狀況:CPU 消耗巨大tcp
經過用 top 觀察,發現 snmpd 進程持續走高ide
抓包ui
重啓 snmpd 進程,在正常了幾分鐘後忽然又重現異常,此時感受是有特殊數據包進來形成的。spa
爲了驗證猜想,對 UDP/161 端口進行了抓包,發現了些端倪。.net
經過抓包發現,原來真的有***,從抓包看,***是突發性質的,當有***時: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,那麼上述方法就無濟於事了。
紅色部分是清除 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 ~]#
知道緣由了:
卸載 conntrack 解決方案
修改
rmmod_r ${IPV}_conntrack
改成
rmmod_r nf_conntrack
由於是定製系統,未作詳細的判斷和動態自適應等工做。
總結