Python UDP DNS 到 TCP DNS 转换器
Python UDP DNS to TCP DNS converter
我正在尝试使用 NFQUEUE 和 Scapy 将任何 UDP DNS 请求转换为 TCP DNS 请求,然后使用基于 TCP DNS 响应的精心制作的 UDP 数据包响应 UDP DNS 请求。这是我到目前为止写的脚本:
#! /usr/bin/env python2.7
from scapy.all import *
from netfilterqueue import NetfilterQueue
import os
import dns.resolver
myResolver = dns.resolver.Resolver()
def resolv_dns(payload):
udp_query_pkt = IP(payload.get_payload())
domain = udp_query_pkt[DNS].qd.qname
ip_addrs = myResolver.query(domain, "A", tcp=True)
if not udp_query_pkt.haslayer(DNSQR):
payload.set_verdict(nfqueue.NF_ACCEPT)
else:
if domain in udp_query_pkt[DNS].qd.qname:
print str(ip_addrs[0])
udp_resp_pkt = IP(dst=udp_query_pkt[IP].src, src=udp_query_pkt[IP].dst)/\
UDP(dport=udp_query_pkt[UDP].sport, sport=udp_query_pkt[UDP].dport)/\
DNS(id=udp_query_pkt[DNS].id, qr=1, aa=1, qd=udp_query_pkt[DNS].qd,\
an=DNSRR(rrname=udp_query_pkt[DNS].qd.qname, ttl=10, rdata=str(ip_addrs[0])))
send(udp_resp_pkt)
payload.drop()
nfqueue = NetfilterQueue()
nfqueue.bind(1, resolv_dns)
try:
os.system("iptables -A OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 1")
print "[*] waiting for data"
nfqueue.run()
except KeyboardInterrupt:
os.system("iptables -D OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 1")
pass
脚本的问题是它不起作用!
实际上我可以在wireshark中看到相应的DNS数据包,它们似乎没问题:
但是我无法打开任何网站!实际上 UDP DNS 请求超时:
ebrahim@ebrahim:~$ dig www.xyw.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> www.xyw.com
;; global options: +cmd
;; connection timed out; no servers could be reached
怎么了?
更新:
@Pierre 回答后,我更改了 IPTable 规则,将接收到的 UDP DNS 响应发送到 NFQUEUE(而不是发送 DNS 查询),然后我修改了 resolv_dns
函数,如下所示(以替换 IP 地址使用 TCP DNS 查询收到的带有新 IP 地址的 UDP DNS 响应):
def resolv_dns(packet):
pkt = IP(packet.get_payload())
domain = pkt[DNS].qd.qname
ip_addrs = myResolver.query(domain, "A", tcp=True)
pkt[DNS].an.rdata = str(ip_addrs[0])
packet.set_payload(str(pkt))
packet.accept()
但是还是不行!
如果您的盒子是拦截转发数据包的路由器,您的脚本可能会起作用。但是由于您使用的是 OUTPUT
链,我想拦截的数据包来自本地主机。在这种情况下,我认为原始客户永远不会得到你伪造的答案。
在我看来,你最好的选择是
- 编写一个 UDP 服务器,例如监听
127.0.0.1:5300
,读取 DNS 查询(你仍然可以使用 Scapy,使用 DNS(data_from_client)
),解析它(使用 TCP ,就像您的脚本所做的那样),并发送响应(同样,您可以发送使用脚本中的 DNS()
调用创建的数据)。
- 使用
iptables
,但不是拦截带有 NFQUEUE
目标的传出数据包,只需 DNAT
将它们发送到您的服务器(类似于 iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to 127.0.0.1:5300
)。
Update:我不知道你为什么坚持使用 NFQUEUE
而不是带有 DNAT
的简单 UDP 服务器(让你的 IP 堆栈和 netfilter做这份工作),因为这可能是做你想做的事的最好方法,但你可能有充分的理由。请注意,对于您提出的第二个解决方案,您必须从 DNS 服务器获得响应才能使您的脚本正常工作(因此您必须有一个有效且可访问的 UDP DNS 解析器)。
在您的第二次尝试中,您修改了 UDP 数据,因此您必须再次计算 IP 和 UDP 校验和以及长度字段(使用服务器 + DNAT
解决方案,您的内核将完成这项工作)。您可以使用 del pkt[IP].chksum, pkt[IP].len, pkt[UDP].chksum, pkt[UDP].len
来做到这一点(Scapy 将为您计算正确的值)。
我正在尝试使用 NFQUEUE 和 Scapy 将任何 UDP DNS 请求转换为 TCP DNS 请求,然后使用基于 TCP DNS 响应的精心制作的 UDP 数据包响应 UDP DNS 请求。这是我到目前为止写的脚本:
#! /usr/bin/env python2.7
from scapy.all import *
from netfilterqueue import NetfilterQueue
import os
import dns.resolver
myResolver = dns.resolver.Resolver()
def resolv_dns(payload):
udp_query_pkt = IP(payload.get_payload())
domain = udp_query_pkt[DNS].qd.qname
ip_addrs = myResolver.query(domain, "A", tcp=True)
if not udp_query_pkt.haslayer(DNSQR):
payload.set_verdict(nfqueue.NF_ACCEPT)
else:
if domain in udp_query_pkt[DNS].qd.qname:
print str(ip_addrs[0])
udp_resp_pkt = IP(dst=udp_query_pkt[IP].src, src=udp_query_pkt[IP].dst)/\
UDP(dport=udp_query_pkt[UDP].sport, sport=udp_query_pkt[UDP].dport)/\
DNS(id=udp_query_pkt[DNS].id, qr=1, aa=1, qd=udp_query_pkt[DNS].qd,\
an=DNSRR(rrname=udp_query_pkt[DNS].qd.qname, ttl=10, rdata=str(ip_addrs[0])))
send(udp_resp_pkt)
payload.drop()
nfqueue = NetfilterQueue()
nfqueue.bind(1, resolv_dns)
try:
os.system("iptables -A OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 1")
print "[*] waiting for data"
nfqueue.run()
except KeyboardInterrupt:
os.system("iptables -D OUTPUT -p udp --dport 53 -j NFQUEUE --queue-num 1")
pass
脚本的问题是它不起作用!
实际上我可以在wireshark中看到相应的DNS数据包,它们似乎没问题:
但是我无法打开任何网站!实际上 UDP DNS 请求超时:
ebrahim@ebrahim:~$ dig www.xyw.com
; <<>> DiG 9.10.3-P4-Ubuntu <<>> www.xyw.com
;; global options: +cmd
;; connection timed out; no servers could be reached
怎么了?
更新:
@Pierre 回答后,我更改了 IPTable 规则,将接收到的 UDP DNS 响应发送到 NFQUEUE(而不是发送 DNS 查询),然后我修改了 resolv_dns
函数,如下所示(以替换 IP 地址使用 TCP DNS 查询收到的带有新 IP 地址的 UDP DNS 响应):
def resolv_dns(packet):
pkt = IP(packet.get_payload())
domain = pkt[DNS].qd.qname
ip_addrs = myResolver.query(domain, "A", tcp=True)
pkt[DNS].an.rdata = str(ip_addrs[0])
packet.set_payload(str(pkt))
packet.accept()
但是还是不行!
如果您的盒子是拦截转发数据包的路由器,您的脚本可能会起作用。但是由于您使用的是 OUTPUT
链,我想拦截的数据包来自本地主机。在这种情况下,我认为原始客户永远不会得到你伪造的答案。
在我看来,你最好的选择是
- 编写一个 UDP 服务器,例如监听
127.0.0.1:5300
,读取 DNS 查询(你仍然可以使用 Scapy,使用DNS(data_from_client)
),解析它(使用 TCP ,就像您的脚本所做的那样),并发送响应(同样,您可以发送使用脚本中的DNS()
调用创建的数据)。 - 使用
iptables
,但不是拦截带有NFQUEUE
目标的传出数据包,只需DNAT
将它们发送到您的服务器(类似于iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to 127.0.0.1:5300
)。
Update:我不知道你为什么坚持使用 NFQUEUE
而不是带有 DNAT
的简单 UDP 服务器(让你的 IP 堆栈和 netfilter做这份工作),因为这可能是做你想做的事的最好方法,但你可能有充分的理由。请注意,对于您提出的第二个解决方案,您必须从 DNS 服务器获得响应才能使您的脚本正常工作(因此您必须有一个有效且可访问的 UDP DNS 解析器)。
在您的第二次尝试中,您修改了 UDP 数据,因此您必须再次计算 IP 和 UDP 校验和以及长度字段(使用服务器 + DNAT
解决方案,您的内核将完成这项工作)。您可以使用 del pkt[IP].chksum, pkt[IP].len, pkt[UDP].chksum, pkt[UDP].len
来做到这一点(Scapy 将为您计算正确的值)。