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 将为您计算正确的值)。