使用Scapy开发Python DNS隧道:Netcat仅接收首个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




