使用 iptables 攔截端口掃描

原理和實現

如何攔截端口掃描?其實有個簡單的思路:佈置陷阱。我們隨機監聽一些未使用的端口,假如有 IP 在短時間內前來連接好幾個,那麼很可能就是掃描者。於是可臨時屏蔽該 IP 所有流量,保護那些還未被掃到的端口。

這個思路很簡單,但如何讓實現也簡單?如果使用普通 socket 監聽端口,那麼掃描者可根據連接成功信息,陸續獲得陷阱端口號,之後就可以避開這些端口,一段時間後即可完全破解。因此,陷阱需要在底層實現。

使用 raw socket 或 libpcap 倒是可以,不過需要寫點代碼。況且這兩者只能接收包,無法攔截包。要簡單的實現攔截,還得把 IP 扔給 iptables/ipset 去處理。這需要頻繁和內核交互,浪費資源。

那麼,能不能讓接收和攔截都由 iptables 實現?

動態黑名單

事實上 iptables 非常強大,除了常用的功能,還有豐富的 擴展模塊

這次要介紹的,是一個可以在運行時動態增刪 ipset 的模塊:SET。它的用法很簡單,例如:

iptables 匹配條件 -j SET --add-set ip-set-xxx src

通過它,即可將 iptables 匹配到的包的源 IP 直接添加到 ipset,無需自己實現交互,簡單並且高效!

我們來嘗試一下。首先創建一個名爲 scanner-ip-set 的集合,用於存放掃描者的 IP:

ipset create scanner-ip-set hash:ip

我們假設保護 12345 端口,因此對任何嘗試連接非 12345 的 IP 都當做掃描者,添加到黑名單裏:

iptables \
  -A INPUT \
  -p tcp --syn ! --dport 12345 \
  -j SET --add-set scanner-ip-set src

我們來驗證下,每隔 1 秒列出 scanner-ip-set 表:

watch -n1 \
  ipset list scanner-ip-set

當我們使用另一臺設備連接非 12345 端口時,該設備的 IP 出現在黑名單裏了!

接下來,我們實現攔截的功能。

攔截流量

需要注意,我們得攔截兩種包:

  • 攔截掃描者連接 陷阱 端口(IP 添加到黑名單之前)

  • 攔截掃描者連接 任何 端口(IP 添加到黑名單之後)

如果不攔截第 1 種,掃描者的 SYN 包可能會觸發系統回覆 RST 包,這在使用雲主機的場合下,會暴露雲防火牆開放的陷阱端口,細節後面討論。

至於實現是非常簡單的,這裏大致表示下。攔截第 1 種:

iptables \
  -A INPUT \
  -p tcp --syn ! --dport 12345 \
  -j DROP

攔截第 2 種:

iptables \
  -A INPUT \
  -p tcp --syn \
  -m set --match-set scanner-ip-set src \
  -j DROP

現在,當我們連接非 12345 端口之後,該設備其他任何端口都無法連接了!

使用 ipset flush scanner-ip-set 可清空黑名單。推薦在本地虛擬機上試驗,不要遠程試驗~

過期和累計

即便確定是掃描者,IP 也不能永遠拉黑。長時間拉黑意義並不大,掃描者可以重新撥號不斷換 IP,反而我們可能將之後分到這些 IP 的正常用戶給屏蔽了。

因此我們在創建 ipset 時需要加上過期時間,例如 30 秒:

ipset create scanner-ip-set hash:ip timeout 30

現在展示黑名單時,每條記錄都有 timeout 參數。該值每秒自動 -1,到 0 記錄就刪除了。

如果某 IP 在過期時間內又出現,那麼是重置定時器,還是保持原先倒計時?

默認是不重置的。如果希望重置,可參考 SET 模塊的 --exist 選項。


此外,在本文開頭也提到「IP 短時間內前來連接好幾個」,而在上述實現中,只要連接一個陷阱端口 IP 就拉黑了,這也許太過苛刻。

因此,我們需要加上統計功能。可以通過 ipset 的 counters 選項實現:

ipset create scanner-ip-set hash:ip timeout 30 counters

現在展示黑名單時,每條記錄又多了 packetsbytes 參數。每當包匹配成功時,參與過的 set 記錄的 packets 累加 1,bytes 累加包長度。

那麼,怎樣才能讀取 packets 然後做比較?這需要另一個選項 --packets-gt

我們將該條件加在上述第 2 種攔截(攔截所有端口)中,例如:

iptables \
  -A INPUT \
  -p tcp --syn \
  -m set --match-set scanner-ip-set src \
  --packets-gt 5 \
  -j DROP

這樣,只有當某 IP 在過期時間內訪問 5 次以上陷阱端口時,纔會進行攔截。策略相對寬鬆了一些。

這個值可根據當前風險狀況進行調整。例如在被很多 IP 掃描時,可以降低一些。

上述提到的這些功能,事實上用 -m recent 也能實現,甚至更簡單。當然本文主要介紹的 -j SET 靈活性更高一些。

TCP 狀態

在上述兩種攔截中,我們都只針對 SYN 包,爲什麼不是所有包?

在第 2 種攔截(所有端口)中,只針對 SYN 可以讓已建立的 TCP 連接不受影響。當然這個策略可根據實際情況調整。

在第 1 種攔截(陷阱端口)中,如果攔截所有包,那麼服務器對外的連接可能會受到影響,如果本地端口正好和陷阱端口相同,對方 IP 就進黑名單了。


不過第 1 種情況仍存在問題:假如掃描者不用 SYN 而是用 ACK 進行探測,那麼系統會回覆 RST 包,導致端口暴露。

因此我們還得通過連接跟蹤,將非 SYN 的異常包進行攔截:

iptables \
  -A INPUT \
  -p tcp ! --syn \
  -m conntrack ! --ctstate ESTABLISHED \
  -j DROP

雲防火牆

使用雲主機時,通常會用到廠商提供的防火牆,例如只開放使用的端口,其他端口都屏蔽。

然而這會導致陷阱端口失效,因此我們需要開放一定數量的端口。這個數量不能太少,否則安全性會降低。

假如開放 100 個隨機端口,那麼主機每收到 1 個陷阱包,意味着掃描者其實發送了 655.36 個,其中絕大部分都被雲防火牆丟棄了。如果沒有云防火牆,這個 IP 早被判定爲掃描者了;但現在只收到 1 個,甚至還沒達到攔截計數器的閾值。因此有些策略需要進行調整。

例如,可以在需要保護的端口前後埋設陷阱。假設保護 12345 端口,那麼我們可放行 12340 - 12350 端口,如果掃描器發送的端口號是線性的話,無論遞增還是遞減,都可以大概率提前落入陷阱。

需要注意的是,陷阱端口必須足夠隨機,以防被猜中。如果攻擊者知道陷阱端口號,就可以將源 IP 僞造成正常用戶故意踩陷阱,從而導致正常用戶被拉黑無法訪問。 因此,最好通過雲防火牆 API 定期修改陷阱端口。(當然攻擊者即使不知道陷阱端口,也可以通過發送很多包,把正常 IP 拉黑,只是效率較低。這個問題之後再寫文討論)

雲防火牆雖然用起來比較麻煩,每個廠商的 API 也都不同,但它有個很大的優點:被攔截的流量不佔帶寬。這對安全防護很有用。即使有超大的掃描流量,我們的服務器也不受影響。

延遲反饋

假如我們要保護 80 端口,但掃描者發的第一個包就是 80,那麼是不是就沒辦法了?

如果掃描器在短時間裏發送了多個包,這種情況仍有解決方案:延遲反饋。

當我們收到 80 端口的 SYN 包時,不立即放行到系統,而是先延遲一會。假如在這段時間內,該 IP 命中了其他的陷阱端口,那麼將延遲隊列裏的 80 SYN 刪除,這樣就可以防止首次猜中的情況!

至於延遲多少,取決於安全和性能的平衡。當然,如果掃描器發包很慢的話,這種方案未必奏效。

至於實現,目前暫未研究。iptables 似乎沒有延遲的功能,而 tc 命令只能延遲發送的包,看來需要一些奇技淫巧才能簡單實現~

端口集合

前面爲了簡單表示,我們使用 12345 端口,但現實中端口也許不止一個,並且可能不斷變化。

如果端口變一次 iptables 就得改一次,顯然很累贅。因此不妨將端口也放在 ipset 裏:

ipset create pub-port-set bitmap:port range 0-65535

iptables \
  -A INPUT \
  -p tcp --syn \
  -m set ! --match-set pub-port-set dst \
  -j SET --add-set scanner-ip-set src

之後我們只需操作 pub-port-set 集合即可:

ipset add pub-port-set 12345

完整實現

綜上所述,整理了一個相對完整的版本:

https://github.com/EtherDream/anti-portscan/blob/master/install.sh

由於寫的比較倉促,如果存在問題請及時指出,有更好的思路也可以交流~

在線演示

可以試試多久找出這個 HTTP 端口?

https://raw.githubusercontent.com/EtherDream/anti-portscan/master/demo.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章