首页 > 解决方案 > 使用 TCP 使用 scapy 的 DNS 请求

问题描述

想在 TCP 中发送一个 DNS 请求,并收到它的响应。我从域名服务器收到的回复似乎......不完整。

下面是我为此编写的代码:

#!/usr/bin/python3

import socket
from scapy.all import *

def main():
        ip = IP(dst="8.8.8.8")
        request = DNS(rd=1, qd=DNSQR(qname = "cnn.com", qtype="A")) # size = 25, hex = 0x19
        twoBytesRequestSize = "\x00\x19" #BIG ENDIAN
        completeRequest = str(request) + twoBytesRequestSize
        # Create TCP Packet with SYN
        SYN = ip/TCP(sport=RandNum(1024,65535), dport=53, flags="S", seq = 32)
        # Send the crafted packet, and get SYN ACK from the other end
        SYNACK = sr1(SYN)
        # We, the client need to send ACK for the server's SYN
        ACK = ip/TCP(sport=SYNACK.dport, dport=53, flags="A", seq=SYNACK.ack, ack = SYNACK.seq +1)
        send(ACK)

        # send the request, and
        DNSREQUEST = ip/TCP(sport=SYNACK.dport, dport=53, flags="PA", seq=SYNACK.ack, ack = SYNACK.seq +1) / completeRequest
        DNSREQUEST.show2()
        DNSREPLY = sr1(DNSREQUEST, timeout=3)

        DNSREPLY.show2()
        #import pdb; pdb.set_trace()

if __name__ == "__main__":
        main()

这是我们发送的内容,DNSREQUEST.show2():

###[ IP ]###
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 127
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = tcp
  chksum    = 0xa7f8
  src       = 192.168.1.200
  dst       = 8.8.8.8
  \options   \
###[ TCP ]###
     sport     = 45597
     dport     = domain
     seq       = 33
     ack       = 3264934925
     dataofs   = 5
     reserved  = 0
     flags     = PA
     window    = 8192
     chksum    = 0xe104
     urgptr    = 0
     options   = []
###[ DNS ]###
        length    = 25127
        id        = 23672
        qr        = 0
        opcode    = 6
        aa        = 0
        tc        = 0
        rd        = 0
        ra        = 0
        z         = 0
        ad        = 1
        cd        = 1
        rcode     = ok
        qdcount   = 23672
        ancount   = 12336
        nscount   = 23672
        arcount   = 12337
        qd        = ''
        an        = ''
        ns        = ''
        ar        = ''
###[ Raw ]###
           load      = "\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x03cnn\\x03com\\x00\\x00\\x01\\x00\\x01'\x00\x19"

我们得到了, 回复:

Received 51 packets, got 1 answers, remaining 0 packets
###[ IP ]###
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 40
  id        = 51159
  flags     =
  frag      = 0
  ttl       = 57
  proto     = tcp
  chksum    = 0xe778
  src       = 8.8.8.8
  dst       = 192.168.1.200
  \options   \
###[ TCP ]###
     sport     = domain
     dport     = 45597
     seq       = 3264934925
     ack       = 0
     dataofs   = 5
     reserved  = 0
     flags     = R
     window    = 0
     chksum    = 0x7465
     urgptr    = 0
     options   = []
###[ Padding ]###
        load      = '\x00\x00\x13\xa9\xa3\x11'

不确定它是否正常工作,或者我没有直接从中提取 DNS 响应。我们得到的 DNS 回复应该有更多的东西,不是吗?我什至无法判断此查询是否成功 - 我们发送的字节数比收到的回复多:

(Pdb) len(DNSREQUEST)
127
(Pdb) len(DNSREPLY)
46
(Pdb)

(Pdb) DNSREPLY
<IP  version=4 ihl=5 tos=0x0 len=40 id=46137 flags= frag=0 ttl=58 proto=tcp chksum=0xfa16 src=8.8.8.8 dst=192.168.1.200 options=[] |<TCP  sport=domain dport=11225 seq=1761828349 ack=0 dataofs=5 reserved=0 flags=R window=0 chksum=0xea51 urgptr=0 |<Padding  load='\x00\x00\x813\x8d\xd2' |>>>

如果是 UDP 查询,则答案显然存在于回复数据包中。

如何可视化来自 TCP 响应数据包的有效 DNS 响应?

编辑 1

#!/usr/bin/python3

import socket
from scapy.all import *

def main():
        ip = IP(dst="8.8.8.8")
        request = DNS(rd=1, qd=DNSQR(qname = "cnn.com", qtype="A")) # size = 25, hex = 0x19
        # Create TCP Packet with SYN
        SYN = ip/TCP(sport=RandNum(1024,65535), dport=53, flags="S", seq = 32)
        # Send the crafted packet, and get SYN ACK from the other end
        SYNACK = sr1(SYN)
        # We, the client need to send ACK for the server's SYN
        ACK = ip/TCP(sport=SYNACK.dport, dport=53, flags="A", seq=SYNACK.ack, ack = SYNACK.seq +1)
        send(ACK)

        # send the request, and
        DNSREQUEST = ip/TCP(sport=SYNACK.dport, dport=53, flags="PA", seq=SYNACK.ack, ack = SYNACK.seq +1) / request

        _, answers = sr(DNSREQUEST, timeout=3, multi=1)
        import pdb; pdb.set_trace()

if __name__ == "__main__":
        main()

编辑2:

正如我从这个页面学到的那样,防止 RST:

iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP

运行以下代码时,我看到 DNS 服务器在我的ngrep中响应:

#!/usr/bin/python3
from scapy.all import *

ip=IP(dst="8.8.8.8")

request = DNS(rd=1, qd=DNSQR(qname = "cnn.com", qtype="TXT")) #size = 27(dec) = 1b (hex)
twoBytesRequestSize = "\x00\x1b" #BIG ENDIAN
completeRequest = str(request) + twoBytesRequestSize

SYN=ip/TCP(sport=RandNum(1024,65535), dport=53, flags="S", seq=42)
SYNACK=sr1(SYN)

ACK=ip/TCP(sport=SYNACK.dport, dport=53, flags="A", seq=SYNACK.ack, ack=SYNACK.seq + 1)
send(ACK)

DNSRequest = ip/TCP(sport=SYNACK.dport, dport=53, flags="PA", seq=SYNACK.ack, ack=SYNACK.seq + 1) / completeRequest
#DNSReply = sr1(DNSRequest, timeout = 1)
DNSReply = sr1(DNSRequest, timeout = 1, multi=1)
import pdb; pdb.set_trace()

但是,我仍然没有在 DNSReply 中看到与 DNS 相关的内容:

(Pdb) DNSReply.show2()
###[ IP ]### 
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 40
  id        = 36387
  flags     = 
  frag      = 0
  ttl       = 120
  proto     = tcp
  chksum    = 0xe22c
  src       = 8.8.8.8
  dst       = 192.168.1.200
  \options   \
###[ TCP ]### 
     sport     = domain
     dport     = 2466
     seq       = 3994942779
     ack       = 130
     dataofs   = 5
     reserved  = 0
     flags     = A
     window    = 28640
     chksum    = 0x77c1
     urgptr    = 0
     options   = []
###[ Padding ]### 
        load      = '\x00\x00^\x97\xf94'

我在这里想念什么?

标签: python-3.xsocketsscapy

解决方案


使用 Scapy 的最新版本(2.4.0),您不需要在 DNS 层的开头添加长度,因为它会自动添加。

您的代码不起作用,因为您在得到答案之前得到了一个普通的 ACK 数据包(没有数据)。

这里的一个选项是使用sr([...], multi=1). 您可以在代码中尝试类似的操作:

    answers, _ = sr(DNSREQUEST, timeout=3, multi=1)
    DNSREPLY = answers[DNS][0]

此外,当您使用 TCP 和 Scapy 时,您需要确保您的操作系统堆栈不会干扰您的数据包(通常,它会使用重置数据包回复您收到的 syn+ack 数据包)。执行此操作的常用方法是使用防火墙来防止 syn+ack 数据包到达操作系统堆栈。


推荐阅读