AdGuardHome icon indicating copy to clipboard operation
AdGuardHome copied to clipboard

DS requests don't respect domain-specific upstreams - this might result in giving out wrong IP addresses

Open jkreileder opened this issue 1 year ago • 1 comments

Prerequisites

Platform (OS and CPU architecture)

Linux, ARM64

Installation

Snapcraft

Setup

On one machine

AdGuard Home version

v0.108.0-b.53

Action

tl;dr:

Specifying an upstream for a specific domain only seems to work for A/AAAA records, other request, like DS, are forwarded to the global upstreams. This might be a security issue.

Long explanation:

AVM routers usually are reachable by fritz.box, so an Adguard configuration would look like

[/fritz.box/]192.168.78.1
h3://dns.google/dns-query
https://dns11.quad9.net/dns-query

to forward requests for fritz.box to the router, which then returns local addresses. Recently someone registered a real fritz.box domain:

$ host fritz.box. 8.8.8.8
Using domain server:
Name: 8.8.8.8
Address: 8.8.8.8#53
Aliases:

fritz.box is an alias for example.com.
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
example.com mail is handled by 0 .

With the Adguard config given above, resolving fritz.box now gives these global addresses. This is a security issue because nothing stops the owner of the fritz.box domain from mimicking the AVM login page and start collecting credentials.

I can reproduce the problem with the MacOS resolver and Linux' systemd-resolved (when DNSSEC is enabled) but not with a simple host/dig call directly:

$ host fritz.box 192.168.78.58 # correct result
Using domain server:
Name: 192.168.78.58
Address: 192.168.78.58#53
Aliases:

fritz.box has address 192.168.78.1
fritz.box has IPv6 address fd00::4e37:12ff:febc:1b67
fritz.box has IPv6 address 2001:26e0:285:d500:3e37:12ff:febc:1b67


$ host -t ds fritz.box 192.168.78.58 # this goes to a global upstream
Using domain server:
Name: 192.168.78.58
Address: 192.168.78.58#53
Aliases:

fritz.box is an alias for example.com.
example.com has DS record 370 13 2 BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A8 6764247C

$ host -t ds fritz.box 192.168.78.1 # as the router returns nothing for the request
Using domain server:
Name: 192.168.78.1
Address: 192.168.78.1#53
Aliases:

fritz.box has no DS record


$ ping -4n fritz.box # the OS resolver gets the global address instead of the local one
PING  (93.184.216.34) 56(84) bytes of data.
64 bytes from 93.184.216.34: icmp_seq=1 ttl=55 time=106 ms
64 bytes from 93.184.216.34: icmp_seq=2 ttl=55 time=105 ms
^C
---  ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 105.320/105.685/106.050/0.365 ms

The DS request which is sent to the global upstreams

Screenshot 2024-02-17 at 01 15 34

seems to break the restriction of fritz.box to the the local upstream given in the configuration.

Expected result

Specifying an upstream for a specific domain should redirect all requests for the given domain to the given upstream.

Actual result

At least DS request don't respect the configured local upstream.

Additional information and/or screenshots

No response

jkreileder avatar Feb 17 '24 01:02 jkreileder

i can´t reproduce it - maybe cause i use unbound ?

root@HomeNetDNS:~# host -t ds fritz.box 192.168.178.10
Using domain server:
Name: 192.168.178.10
Address: 192.168.178.10#53
Aliases:

fritz.box has no DS record

root@HomeNetDNS:~# host -t ds fritz.box 192.168.178.1
Using domain server:
Name: 192.168.178.1
Address: 192.168.178.1#53
Aliases:

fritz.box has no DS record

Ping is the same behaviour...

root@HomeNetDNS:~# ping -4n fritz.box

PING  (93.184.216.34) 56(84) bytes of data.
64 bytes from 93.184.216.34: icmp_seq=1 ttl=53 time=107 ms
64 bytes from 93.184.216.34: icmp_seq=2 ttl=53 time=106 ms
64 bytes from 93.184.216.34: icmp_seq=3 ttl=53 time=105 ms
^C
---  ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 104.803/105.816/106.719/0.786 ms

whyisthisbroken avatar Feb 23 '24 16:02 whyisthisbroken

@jkreileder, hello and apologies for the late response. We've pushed the edge build, that fixes the behavior of TLD domain specifications. I suppose something like the following will work well in your case:

[/box/]192.168.78.1
[/fritz.box/]192.168.78.1
[/*.box/]#
h3://dns.google/dns-query
https://dns11.quad9.net/dns-query

Could you please try it?

EugeneOne1 avatar Apr 26 '24 13:04 EugeneOne1

@EugeneOne1 I've tried: adguard-home v0.108.0-a.895+b9d5e5ba 6878 latest/edge ameshkov✓ - with the suggested config and I'm still getting wrong results:

MacOS:

$ host pi5.fritz.box 192.168.78.58
Using domain server:
Name: 192.168.78.58
Address: 192.168.78.58#53
Aliases:

pi5.fritz.box has address 192.168.78.58
pi5.fritz.box has IPv6 address fd00::da3a:ddff:fea1:efa6
pi5.fritz.box has IPv6 address 2001:16e0:285:d500:da3a:ddff:fea1:efa6
$ ping pi5.fritz.box
PING pi5.fritz.box (45.76.93.104): 56 data bytes
Request timeout for icmp_seq 0
64 bytes from 45.76.93.104: icmp_seq=1 ttl=56 time=170.933 ms

So the resolver still doesn't get 192.168.78.58.

Linux with systemd-resolved and DNSSEC enabled:

$ ping pi5.fritz.box
ping: pi5.fritz.box: Name or service not known
$ resolvectl query pi5.fritz.box
pi5.fritz.box: resolve call failed: DNSSEC validation failed: no-signature

And this in the logs:

2024-04-26T16:13:42.208218+02:00 pi5 systemd[1]: Started systemd-resolved.service - Network Name Resolution.
2024-04-26T16:13:42.211316+02:00 pi5 systemd-resolved[539]: DNSSEC validation failed for question fritz.box IN SOA: no-signature
2024-04-26T16:13:42.211328+02:00 pi5 systemd-resolved[539]: DNSSEC validation failed for question pi5.fritz.box IN DS: no-signature
2024-04-26T16:13:42.211336+02:00 pi5 systemd-resolved[539]: DNSSEC validation failed for question pi5.fritz.box IN SOA: no-signature
2024-04-26T16:13:42.211343+02:00 pi5 systemd-resolved[539]: DNSSEC validation failed for question pi5.fritz.box IN AAAA: no-signature
2024-04-26T16:13:42.211351+02:00 pi5 systemd-resolved[539]: DNSSEC validation failed for question pi5.fritz.box IN A: no-signature

jkreileder avatar Apr 26 '24 14:04 jkreileder

@jkreileder, hello again. We've pushed another edge, since the previous one actually contains a bug with matching, sorry about that. Could you please check if the latest edge routes as intended?

EugeneOne1 avatar May 02 '24 15:05 EugeneOne1

@EugeneOne1 using v0.108.0-a.901+c05bce7a and the configuration above, I'm still seeing DS request going to external servers.

A, AAAA and SOA correctly go to 192.168.78.1:

image

But DS doesn't:

image

So I'm still getting:

$ resolvectl query --cache=no fritz.box
fritz.box: resolve call failed: DNSSEC validation failed: no-signature
$ ping fritz.box
ping: fritz.box: Name or service not known

jkreileder avatar May 02 '24 18:05 jkreileder

@jkreileder, the latest edge build is actually v0.108.0-a.902+17c4eeb6, it has just been uploaded to Snapstore, sorry for the delay.

EugeneOne1 avatar May 03 '24 11:05 EugeneOne1

@EugeneOne1 v0.108.0-a.902+17c4eeb6 does better but still not completely fine.

Using the config given above

[/box/]192.168.78.1
[/fritz.box/]192.168.78.1
[/*.box/]#
h3://dns.google/dns-query
https://dns11.quad9.net/dns-query

DS request for *.fritz.box go to 192.168.78.1 now. But DS requests for box still go to external servers. So the first line of the config doesn't seem to work for DS requests (removing the third line doesn't change anything) -- it does work for other request types.

Because of this resolved still shows DNSSEC errors:

$ resolvectl query --cache=no no.fritz.box
no.fritz.box: resolve call failed: DNSSEC validation failed: no-signature

jkreileder avatar May 03 '24 16:05 jkreileder

@jkreileder, well, the thing is that according to RFC 4035 (See also #6156):

A DS RR SHOULD point to a DNSKEY RR that is present in the child's apex DNSKEY RRset, and the child's apex DNSKEY RRset SHOULD be signed by the corresponding private key.

So DS requests are routed to the parent of the zone. So a DS request for box will be routed to the root zone, which is the default upstream set. As far as I can see, there is no way to both satisfy the RFC requirements and specify an upstream for resolving DS requests for TLDs. Maybe we can add a dnsmasq-like trust-anchor mechanism for such cases, or support the DS/DNSKEY types in $dnsrewrite rules, but DNSSEC is a pretty big topic.

Until then, I think you'll need some kind of proxy to redirect DS box. requests to the appropriate server.

BTW, the [/*.box/]# line should route all non *.fritz.box (like DS abc.example.box) requests to the default servers. Not sure if there is such requirement in your network.

EugeneOne1 avatar May 06 '24 13:05 EugeneOne1