You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Scapy开发Python DNS隧道:Netcat仅接收首个UDP数据包问题

解决Scapy DNS隧道仅能接收第一个UDP数据包的问题

嘿,我之前做UDP相关工具时也踩过类似的坑,结合你的代码和问题描述,咱们拆解下可能的原因和解决办法:

1. 先排查最容易忽略的DNS格式问题

你的代码里用了base64.encodebytes(),这个函数会每76个字符自动添加换行符,而DNS的qname字段是绝对不允许包含换行这类非法字符的。第一个数据包可能因为数据量小,编码后没触发换行,格式正常能被接收;后续数据包编码后带了换行,导致DNS查询包格式无效,要么被DNS服务器直接丢弃,要么Netcat收到后无法识别为有效数据。

修复方法:换成base64.b64encode()(无自动换行),同时处理DNS qname的长度限制(每个标签最多63字节):

import base64
from scapy.all import IP, UDP, DNS, DNSQR, conf

# 提前创建持久化套接字,保证源端口复用
dns_socket = conf.L3socket()

def sendDns(incomingBytes):
    print('sending packet data :\n' + incomingBytes.decode('utf-8'))
    # 用无换行的base64编码
    encoded_data = base64.b64encode(incomingBytes).decode('utf-8')
    # 分割成符合DNS规范的标签(每个<=63字符)
    qname_segments = [encoded_data[i:i+63] for i in range(0, len(encoded_data), 63)]
    # 拼接成合法的qname(必须以点结尾)
    valid_qname = '.'.join(qname_segments) + '.'
    # 构造并发送数据包
    packet = IP(dst=dnsServer)/UDP(dport=53, sport=12345)/DNS(qd=DNSQR(qname=valid_qname))
    dns_socket.send(packet)

2. Scapy默认send()函数的端口复用问题

你虽然指定了sport=12345,但Scapy默认的send()函数每次发送都会创建新的临时套接字,发送完成后就关闭。操作系统会把关闭的端口置于TIME_WAIT状态一段时间,第二次尝试用同一个源端口发送时,可能被系统拒绝,或者实际源端口被改成临时端口——这会导致Netcat因为源端口变化而丢弃后续数据包(部分Netcat版本在UDP模式下会默认只接收第一个连接的源端口数据)。

修复方法:提前创建持久化的L3socket,用它发送所有数据包,保证始终使用指定的源端口12345(上面的代码已经包含这部分)。

3. 验证Netcat的监听参数

确保目标端用了正确的UDP监听命令,比如:

nc -u -l 53

部分Linux发行版需要加-p指定端口:

nc -u -l -p 53

另外检查目标端防火墙规则,确认没有拦截后续UDP数据包(虽然Wireshark看到发送正常,但目标端防火墙可能过滤非初始连接的包)。

最后验证

修改代码后用Wireshark抓包,确认后续DNS数据包的qname无换行符、每个标签长度不超63字节,且所有数据包的源端口都是12345,这样应该就能解决仅接收第一个包的问题了。

内容的提问来源于stack exchange,提问作者Brett Weiland

火山引擎 最新活动