tl;dr: accessing 0.0.0.0
:port
(eg. curl http://0.0.0.0:443
) gets redirected(internally) to 127.0.0.1:port
(where port
is any port number) (eg. the previous curl
command is the same as curl http://127.0.0.1:443
); why does this happen and how to block connections destined to 0.0.0.0
?
UPDATE2: I've found a way to block it by patching the Linux kernel (version 6.0.9):
--- .orig/usr/src/linux/net/ipv4/route.c
+++ /usr/src/linux/net/ipv4/route.c
@@ -2740,14 +2740,17 @@ struct rtable *ip_route_output_key_hash_
}
if (!fl4->daddr) {
- fl4->daddr = fl4->saddr;
+ rth = ERR_PTR(-ENETUNREACH);
+ goto out;
+ /* commenting out the rest:
+ fl4->daddr = fl4->saddr; // if you did specify src address and dest is 0.0.0.0 then set dest=src addr
if (!fl4->daddr)
- fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
+ fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK); // if you didn't specify source address and dest address is 0.0.0.0 then make them both 127.0.0.1
dev_out = net->loopback_dev;
fl4->flowi4_oif = LOOPBACK_IFINDEX;
res->type = RTN_LOCAL;
flags |= RTCF_LOCAL;
- goto make_route;
+ goto make_route; END of COMMENTed out block */
}
err = fib_lookup(net, fl4, res, 0);
Result: Where do packets sent to IP 0.0.0.0 go?:
$ ip route get 0.0.0.0
RTNETLINK answers: Network is unreachable
...they don't!
A client attempts to connect from 127.1.2.18:5000 to 0.0.0.0:80
$ nc -n -s 127.1.2.18 -p 5000 -vvvvvvvv -- 0.0.0.0 80
(UNKNOWN) [0.0.0.0] 80 (http) : Network is unreachable
sent 0, rcvd 0
(if you didn't apply kernel patch, you will need a server like the following for the above client to be able to successfully connect: (as root, in bash)while true; do nc -n -l -p 80 -s 127.1.2.18 -vvvvvvvv -- 127.1.2.18 5000; echo "------------------$(date)";sleep 1; done
)
Patched ping
(ie. a ping
that doesn't set destination address to be the same as the source address when destination address is 0.0.0.0, ie. comment out the 2 lines under // special case for 0 dst address
that you see here):
$ ping -c1 0.0.0.0
ping: connect: Network is unreachable
instant. However, if specifying source address, it takes a timeout(of 10 sec) until it finishes:
$ ping -I 127.1.2.3 -c1 -- 0.0.0.0
PING 0.0.0.0 (0.0.0.0) from 127.1.2.3 : 56(84) bytes of data.
--- 0.0.0.0 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
UPDATE1:
The why part is explained here but I'm expecting a little bit more details as to why does this happen, for example(thanks to user with nickname anyone
on liberachat #kernel channel):
$ ip route get 0.0.0.0
local 0.0.0.0 dev lo src 127.0.0.1 uid 1000
cache <local>
This shows that somehow packets destined for 0.0.0.0
get routed to the localhost interface lo
and they get source ip 127.0.0.1
(if I'm interpreting this right) and because that route doesn't appear in this list:
$ ip route list table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 169.254.6.5 dev em1 proto kernel scope host src 169.254.6.5
broadcast 169.254.6.255 dev em1 proto kernel scope link src 169.254.6.5
local 192.168.0.17 dev em1 proto kernel scope host src 192.168.0.17
broadcast 192.168.255.255 dev em1 proto kernel scope link src 192.168.0.17
it means that it must be somehow internal to the Linux kernel. ie. hardcoded
To give you an idea, here's how it looks for an IP that's on the internet (I used quad1 as an example IP):
$ ip route get 1.1.1.1
1.1.1.1 via 192.168.1.1 dev em1 src 192.168.0.17 uid 1000
cache
where 192.168.1.1
is my gateway, ie.:
$ ip route
default via 192.168.1.1 dev em1 metric 2
169.254.6.0/24 dev em1 proto kernel scope link src 169.254.6.5
192.168.0.0/16 dev em1 proto kernel scope link src 192.168.0.17
Because iptables
cannot be used to sense (and thus block/drop) such connections destined to 0.0.0.0 that get somehow routed to 127.0.0.1, it might prove difficult to find a way to block them... but I'll definitely try to find a way, unless someone already knows one.
@Stephen Kitt (in the comments) suggested a way to block hostnames that reside in /etc/hosts, so instead of:0.0.0.0 someblockedhostname
you can have127.1.2.3 someblockedhostname
127.1.2.3 someOTHERblockedhostname
(anything other than 127.0.0.1, but you can use the same IP for every blocked hostname, unless you want to differentiate)
which IP you can then block using iptables
.
However if your DNS resolver (ie. NextDNS, or 1.1.1.3) returns 0.0.0.0
for blocked hostnames (instead of NXDOMAIN
) then you cannot do this (unless, of course, you want to add each host manually in /etc/hosts
, because /etc/hosts takes precedence - assuming you didn't change the line hosts: files dns
from /etc/nsswitch.conf
)
OLD: (though edited)
On Linux (I tried Gentoo and Pop OS!, latest) if you have this line in /etc/hosts
:
0.0.0.0 somehosthere
and you run this as root (to emulate a localhost server listening on port 443)# nc -l -p 443 -s 127.0.0.1
then you go into your browser (Firefox and Chrome/Chromium tested) and put this in address bar:https://somehosthere
or0.0.0.0:443
orhttps://0.0.0.0
then the terminal where you started nc
(aka netcat) shows a connection attempt (some garbage text including the plaintext somehosthere
if you used it in the url)
or instead of the browser, you can try:curl https://somehosthere
or if you want to see the plaintext request:curl http://somehosthere:443
This doesn't seem to be mitigable even when using dnsmasq
as long as that 0.0.0.0 somehosthere
is in /etc/hosts
, but when using dnsmasq
and your DNS resolver (ie. NextDNS or Cloudflare's 1.1.1.3) returns 0.0.0.0
instead of NXDOMAIN
(true at the time of this writing) and that hostname isn't in your /etc/hosts
(AND in what you told dnsmasq
is the /etc/hosts
to use) then there are two ways to mitigate it(either or both will work):
dnsmasq
arg --stop-dns-rebind
--stop-dns-rebind
Reject (and log) addresses from upstream nameservers which are in
the private ranges. This blocks an attack where a browser behind
a firewall is used to probe machines on the local network. For
IPv6, the private range covers the IPv4-mapped addresses in pri‐
vate space plus all link-local (LL) and site-local (ULA) ad‐
dresses.
bogus-nxdomain=0.0.0.0
in /etc/dnsmasq.conf
which makes dnsmasq
itself return NXDOMAIN
for any hostname that resolved to 0.0.0.0
(except, once again, if that hostname was in /etc/hosts (bypasses dnsmasq) and what you told dnsmasq to use as /etc/hosts
(if you did))So, the second part of this question is how to disallow accesses to 0.0.0.0 from being redirected to 127.0.0.1 ? I want this because when using NextDNS (or cloudflare's 1.1.1.3) as DNS resolver, it returns 0.0.0.0
for blocked hostnames, instead of NXDOMAIN, thus when loading webpages, parts of them(that are located on blocked hostnames) will try to access my localhost server running on port 443 (if any) and load pages from it instead of just being blocked.
Relevant browser-specific public issues being aware of this(that 0.0.0.0 maps to 127.0.0.1):
Chrome/Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=1300021 Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1672528#c17
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.3