secdev / secdev/scapy

scapy doesn't assemble DNS queries/responses sent over TCP in more than one packet

Open
#4,023 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
12.6k
Forks
2.2k
Avg merge
1d 4h
Merged PRs (30d)
56

Description

Brief description

When DNS messages are sent over TCP in more than one packet scapy dissects each packet separately and since usually they don't pass the sanity checks their payloads get wrapped into Raw. In theory it should be possible to add DNS.tcp_reassembly that could assemble those messages but due to the sanity checks those packets can never reach it currently.

(It came up in https://github.com/secdev/scapy/pull/4012 but since it's kind of tangential I thought it would be better to move it here)

Scapy version

3da71cb275721292131a3ea5196bfa98883741f2

Python version

3.11.3

Operating system

Linux version 6.3.4-101.fc37.x86_64

Additional environment information

No response

How to reproduce

dns-openpgpkey.pcap contains a response with an OPENPGPKEY RR that arrived in two separate packets.

>>> pkts = sniff(offline='dns-openpgpkey.pcap', session=TCPSession)
INFO: Malformed DNS message: invalid length!
INFO: Malformed DNS message: invalid length!
Actual result

scapy wraps the DNS part of those packets into Raw:

>>> pkts.nsummary()
0000 Ether / IP / UDP / DNS Qry "b'd08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org.'"
0001 Ether / IP / UDP / DNS Ans
0002 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain S
0003 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 SA
0004 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain A
0005 Ether / IP / TCP / DNS Qry "b'd08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org.'"
0006 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 A
0007 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 PA / Raw
0008 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain A
0009 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 PA / Raw
0010 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain A
0011 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain FA
0012 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 A
0013 Ether / IP / TCP 1.1.1.1:domain > 10.0.2.100:44681 FA
0014 Ether / IP / TCP 10.0.2.100:44681 > 1.1.1.1:domain A
Expected result

To get the response the Raw payloads should be combined manually using something like

DNS(pkts[7][Raw].load[2:] + pkts[9][Raw].load)
...

The idea is to get DNS.tcp_reassemble to do that :-)

I'm not sure if it's helpful or not but here's what tshark shows:

$ tshark -r dns-openpgpkey.pcap
...
    3   0.404609   10.0.2.100 → 1.1.1.1      TCP 74 44681 → 53 [SYN] Seq=0 Win=64660 Len=0 MSS=1220 SACK_PERM TSval=2859481442 TSecr=0 WS=128
    4   0.431399      1.1.1.1 → 10.0.2.100   TCP 58 53 → 44681 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65480
    5   0.431475   10.0.2.100 → 1.1.1.1      TCP 54 44681 → 53 [ACK] Seq=1 Ack=1 Win=64660 Len=0
    6   0.431646   10.0.2.100 → 1.1.1.1      DNS 183 Standard query 0xb390 OPENPGPKEY d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org OPT
    7   0.432353      1.1.1.1 → 10.0.2.100   TCP 54 53 → 44681 [ACK] Seq=1 Ack=130 Win=65535 Len=0
    8   0.831929      1.1.1.1 → 10.0.2.100   TCP 1454 53 → 44681 [PSH, ACK] Seq=1 Ack=130 Win=65535 Len=1400 [TCP segment of a reassembled PDU]
    9   0.831960   10.0.2.100 → 1.1.1.1      TCP 54 44681 → 53 [ACK] Seq=130 Ack=1401 Win=63440 Len=0
   10   0.832338      1.1.1.1 → 10.0.2.100   DNS 1006 Standard query response 0xb390 OPENPGPKEY d08ee310438ca124a6149ea5cc21b6313b390dce485576eff96f8722._openpgpkey.fedoraproject.org OPENPGPKEY OPT
Related resources

dns-openpgpkey.zip

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with sniff(offline='dns-openpgpkey.pcap', session=TCPSession) and inspect how the split TCP payloads reach DNS parsing and its sanity checks. Use the reported DNS.tcp_reassemble idea as the scope; done means the two TCP segments are combined and dissected as a DNS response instead of being wrapped in Raw.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.