mirror of
https://github.com/hickory-dns/hickory-dns.git
synced 2026-04-25 03:05:51 +03:00
[GH-ISSUE #629] packet injection vulnerability #253
Labels
No labels
blocked
breaking-change
bug
bug:critical
bug:tests
cleanup
compliance
compliance
compliance
crate:all
crate:client
crate:native-tls
crate:proto
crate:recursor
crate:resolver
crate:resolver
crate:rustls
crate:server
crate:util
dependencies
docs
duplicate
easy
easy
enhance
enhance
enhance
feature:dns-over-https
feature:dns-over-quic
feature:dns-over-tls
feature:dnsssec
feature:global_lb
feature:mdns
feature:tsig
features:edns
has workaround
ops
perf
platform:WASM
platform:android
platform:fuchsia
platform:linux
platform:macos
platform:windows
pull-request
question
test
tools
tools
trust
unclear
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/hickory-dns#253
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @aep on GitHub (Nov 27, 2018).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/629
It looks like the udp resolver accepts responses from any address,
not just the server it sent a request to. I'm not sure if this is a problem, since you'd have to guess the local port.
Maybe in high load environments with lots of outgoing pending dns requests, this could be abused. Like when implementing a dns server with upstream queries and someone can guess the port for the upstream response.
[edit]
Actually i'm reasonably sure now this is a real problem. There's a bunch of script kiddies doing exactly that on public wifi. You can easily see the source port on the air and just reply faster than an internet server.
@bluejekyll commented on GitHub (Nov 27, 2018):
Yes, it did at one point, here's the location to do it: https://github.com/bluejekyll/trust-dns/blob/master/crates/proto/src/udp/udp_client_stream.rs#L72
The issue is that not all nameservers respond from the same address as the request was sent to. Enabling this will drop legit responses.
@aep commented on GitHub (Nov 27, 2018):
hah, thanks for the instant response.
That sounds bad. I'll dig around what everyone else does.
@bluejekyll commented on GitHub (Nov 27, 2018):
This is explicitly allowed in an RFC, and I can't remember which one. The tactic is to randomize port selection on outbound as well as the DNS request ID>
@aep commented on GitHub (Nov 27, 2018):
that would be odd. I did some research, and indeed pretty much everyone filters these.
github.com/lattera/glibc@895ef79e04/resolv/res_send.c (L1315)github.com/aosp-mirror/platform_bionic@92c6f7ee90/libc/dns/resolv/res_send.c (L1234)@bluejekyll commented on GitHub (Nov 27, 2018):
Of course I can't find any history of having actually enabled that, which means I may have done that to match the original RFC, but this is clarified here: https://tools.ietf.org/html/rfc2181#section-4
Not sure why I didn't enforce that instead of logging. I think I was trying to discover if it would be an issue and never went back and enforced it. I don't see any issues, or history in the code that it was ever enforced. So we should just drop the response.
Do you want to submit a change?
@aep commented on GitHub (Nov 27, 2018):
yup. fix looks easy
@kpcyrd commented on GitHub (Nov 27, 2018):
I don't think it's common that a wrong IP in the response is filtered.
I ran into this in a real setup this summer (south western railway wifi if I recall correctly), in which my trust-dns based program had trouble getting dns to work, while everything else just worked. After some digging I noticed the response is coming from an IP I didn't ask (talking directly to THAT ip returned nothing though).
This meant lookups with dig and trust-dns failed, but everything glibc based was just working fine. I asked a few friends and apparently I was the only person having issues with that kind of setup. Even though glibc has code to reject this, it might not reject it by default.
@bluejekyll commented on GitHub (Nov 28, 2018):
I'm confused, are you saying that trust-dns was failing b/c of the filtering (which I don't think is happening now) or for some other reason? Because over UDP, trust-dns I don't think is currently filtering out based on src address of the Nameserver. We might be filtering further up the stack, but I didn't find than in the
UdpClientStream, I'll dig deeper tonight.btw, if you're in a similar situation in the future and are experiencing responses not being delivered to the resolver, it would be great to enable logging to see if we can identify why.
I just looked at
udp_client_streamall the way back to the beginning, there's basically always been a debug log message, but it's never in the past dropped UDP responses based on a mismatched server address. So this would be a new feature for the library. We can make it configurable in the Resolver, if we have concerns.@kpcyrd, can you clarify the why you think trust-dns was failing in that scenario?
@kpcyrd commented on GitHub (Nov 28, 2018):
I think I mixed up the trust-dns part, this was half a year ago and I didn't pay too much attention back then (since I was on a train). I think trust-dns couldn't resolve it since talking to the ip that was pushed over dhcp didn't work (so I don't know if trust-dns would've accepted or rejected the reply), but /etc/resolv.conf listed 8.8.8.8, which was hijacked by the network (along with everything else on port :53, but always replied from the same ip that was the dns server according to dhcp)
both dig and glibc based programs talked to 8.8.8.8, dig was logging an error because the src ip of the reply didn't match, but glibc happily accepted those replies. My point is that I don't think glibc is rejecting these by default.
@bluejekyll commented on GitHub (Nov 28, 2018):
I’m not sure what the solution is here, as it’s an abuse that others are exploiting, and would cause a failure to resolve due to bad network operators.
DHCP generally overwrites the resolv.conf, but trust-dns currently doesn’t have a “reload” mechanism on change of that file. It could be that it changed, and the update was never picked up.
@bluejekyll commented on GitHub (Nov 28, 2018):
I should note, the src addr in UDP is not trusted anyway, which means that even matching on this wouldn’t really make the packet trustworthy. Only DNSSEC and/or DoH/DoT provide protections against that.
@aep commented on GitHub (Nov 28, 2018):
glibc is pretty difficult to read, so there might be a fallback somewhere i didnt find. However, android does reject it correctly, which is probably the most popular OS on the planet, so people will notice broken dns servers already.
I did elevate the appropriate log message to warn!, so it shows up by default, just in case such a network exists.
@bluejekyll commented on GitHub (Nov 28, 2018):
One option I was thinking about is that we could “capture” the first response (ie the one that doesn’t match the remote src addr), and set a timer of say a second to see if the if the correct one shows up, if the timer goes off, return the only one we received, otherwise return the one that matches in the future.
@bluejekyll commented on GitHub (Nov 30, 2018):
Any objections to the idea to fall back to the wrongly src’d packet in the case of a timeout?
I think that will strike a nice balance between function and improved authenticity of the src (though not strong, better). I can merge #630 and then make that change if there are no objections.
@aep commented on GitHub (Nov 30, 2018):
@bluejekyll no real objection, although i do think the timeout has to be significant, like a second at least.
@bluejekyll commented on GitHub (Nov 30, 2018):
I was thinking of using the one configured in the Resolver already, which I think defaults to 2 seconds today.
Edit: oh it’s actually a default of 5:
https://github.com/bluejekyll/trust-dns/blob/master/crates/resolver/src/config.rs#L568
@aep commented on GitHub (Nov 30, 2018):
that makes sense to me. 5 is a safe default, if i remember correctly, its what android uses for timeout, so anything slower can be considered not working.