[GH-ISSUE #629] packet injection vulnerability #253

Closed
opened 2026-03-07 23:02:33 +03:00 by kerem · 17 comments
Owner

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.

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.
Author
Owner

@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.

<!-- gh-comment-id:442166002 --> @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.
Author
Owner

@aep commented on GitHub (Nov 27, 2018):

hah, thanks for the instant response.
That sounds bad. I'll dig around what everyone else does.

<!-- gh-comment-id:442166613 --> @aep commented on GitHub (Nov 27, 2018): hah, thanks for the instant response. That sounds bad. I'll dig around what everyone else does.
Author
Owner

@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>

<!-- gh-comment-id:442167051 --> @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>
Author
Owner

@aep commented on GitHub (Nov 27, 2018):

that would be odd. I did some research, and indeed pretty much everyone filters these.

<!-- gh-comment-id:442177622 --> @aep commented on GitHub (Nov 27, 2018): that would be odd. I did some research, and indeed pretty much everyone filters these. - [x] musl https://git.musl-libc.org/cgit/musl/tree/src/network/res_msend.c#n138 - [x] glibc https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/resolv/res_send.c#L1315 - [x] java (calls libc) - [x] golang (calls libc) - [x] Qt5 (calls libc) - [x] android https://github.com/aosp-mirror/platform_bionic/blob/92c6f7ee9014f434fbcce89ab894c745e36732d2/libc/dns/resolv/res_send.c#L1234
Author
Owner

@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?

<!-- gh-comment-id:442182698 --> @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?
Author
Owner

@aep commented on GitHub (Nov 27, 2018):

yup. fix looks easy

<!-- gh-comment-id:442183019 --> @aep commented on GitHub (Nov 27, 2018): yup. fix looks easy
Author
Owner

@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.

<!-- gh-comment-id:442212240 --> @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.
Author
Owner

@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_stream all 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?

<!-- gh-comment-id:442274365 --> @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_stream` all 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?
Author
Owner

@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.

<!-- gh-comment-id:442290323 --> @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.
Author
Owner

@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.

I think trust-dns couldn't resolve it since talking to the ip that was pushed over dhcp didn't work

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.

<!-- gh-comment-id:442291522 --> @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. > I think trust-dns couldn't resolve it since talking to the ip that was pushed over dhcp didn't work 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.
Author
Owner

@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.

<!-- gh-comment-id:442292605 --> @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.
Author
Owner

@aep commented on GitHub (Nov 28, 2018):

My point is that I don't think glibc is rejecting these by default.

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.

<!-- gh-comment-id:442441591 --> @aep commented on GitHub (Nov 28, 2018): > My point is that I don't think glibc is rejecting these by default. 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.
Author
Owner

@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.

<!-- gh-comment-id:442568047 --> @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.
Author
Owner

@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.

<!-- gh-comment-id:443255193 --> @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.
Author
Owner

@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.

<!-- gh-comment-id:443256288 --> @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.
Author
Owner

@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

<!-- gh-comment-id:443257702 --> @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
Author
Owner

@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.

<!-- gh-comment-id:443259029 --> @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.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#253
No description provided.