为什么在 Linux 上访问 0.0.0.0:443 会被重定向到 127.0.0.1:443 以及如何禁止它?

为什么在 Linux 上访问 0.0.0.0:443 会被重定向到 127.0.0.1:443 以及如何禁止它?

长话短说:访问0.0.0.0:port(例如curl http://0.0.0.0:443)被重定向(内部)到127.0.0.1:port(其中port是任何端口号)(例如,上一个curl命令与 相同curl http://127.0.0.1:443);为什么这会发生吗?如何阻止发往0.0.0.0?的连接

更新2:我找到了一种通过修补 Linux 内核(版本 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);

结果:发送到 IP 0.0.0.0 的数据包去了哪里?:

$ ip route get 0.0.0.0
RTNETLINK answers: Network is unreachable

...他们不!

客户端尝试从 127.1.2.18:5000 连接到 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

(如果您没有应用内核补丁,您将需要一个如下所示的服务器才能使上述客户端能够成功连接:(以 root 身份,在 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

已修补ping(即,ping当目标地址为 0.0.0.0 时,不将目标地址设置为与源地址相同,即注释掉// special case for 0 dst address您看到的下面的 2 行这里):

$ ping -c1 0.0.0.0
ping: connect: Network is unreachable

立即的。但是,如果指定源地址,则需要超时(10 秒)才能完成:

$ 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

更新1:

为什么部分已解释这里但我期待更多关于为什么会发生这种情况的细节,例如(感谢anyoneliberachat #kernel 频道上有昵称的用户):

$ ip route get 0.0.0.0
local 0.0.0.0 dev lo src 127.0.0.1 uid 1000
    cache <local>

这表明,以某种方式,目的地为本地主机的数据包0.0.0.0会被路由到本地主机接口lo,并且它们会获得源 IP 127.0.0.1(如果我解释正确的话),并且因为该路由不会出现在此列表中:

$ 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

这意味着它必须以某种方式存在于 Linux 内核内部。 IE。硬编码的

为了让您了解一下,以下是它如何查找互联网上的 IP(我使用quad1 作为示例 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

我的网关在哪里192.168.1.1,即:

$ 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

因为iptables无法用于感知(从而阻止/丢弃)此类发往 0.0.0.0 且以某种方式路由至 127.0.0.1 的连接,所以可能很难找到阻止它们的方法...但我肯定会尝试找到一种方法,除非有人已经知道了。

@Stephen Kitt(在评论中)建议了一种阻止驻留在 /etc/hosts 中的主机名的方法,因此,
0.0.0.0 someblockedhostname
您可以使用
127.1.2.3 someblockedhostname
127.1.2.3 someOTHERblockedhostname
(除 127.0.0.1 以外的任何名称),但您可以对每个被阻止的主机名使用相同的 IP,除非您想区分)
然后可以使用 阻止哪个 IP iptables

但是,如果您的 DNS 解析器(即下一个DNS, 或者1.1.1.3) 返回0.0.0.0被阻止的主机名(而不是NXDOMAIN),那么您不能执行此操作(当然,除非您想在 中手动添加每个主机/etc/hosts,因为 /etc/hosts 优先 - 假设您没有更改来自hosts: files dns的行/etc/nsswitch.conf


老的:(虽然经过编辑)

在 Linux 上(我尝试了 Gentoo 和 Pop OS!,最新的)如果你有以下行/etc/hosts

0.0.0.0 somehosthere

然后以 root 身份运行它(以模拟侦听端口 443 的本地主机服务器),
# nc -l -p 443 -s 127.0.0.1
然后进入浏览器(已测试 Firefox 和 Chrome/Chromium)并将其放入地址栏中:
https://somehosthere

0.0.0.0:443

https://0.0.0.0

然后您启动的终端nc(又名 netcat)显示连接尝试(一些垃圾文本,包括明文,somehosthere如果您在 url 中使用它)

或者代替浏览器,您可以尝试:
curl https://somehosthere
或者如果您想查看明文请求:
curl http://somehosthere:443

即使使用中的dnsmasq那么长的时间,这似乎也无法缓解,但是当使用您的 DNS 解析器(即 NextDNS 或0.0.0.0 somehosthere/etc/hostsdnsmasqCloudflare 的 1.1.1.3) 返回0.0.0.0而不是NXDOMAIN(真的在撰写本文时)并且该主机名不在您的主机名中/etc/hosts(并且在您告诉dnsmasq/etc/hosts使用的主机名中),那么有两种方法可以缓解它(其中一种或两种都有效):

  1. 使用dnsmasq参数--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.
  1. 使用bogus-nxdomain=0.0.0.0行使其/etc/dnsmasq.conf自身dnsmasq返回NXDOMAIN解析为的任何主机名0.0.0.0(除非该主机名再次位于 /etc/hosts 中(绕过 dnsmasq)以及您告诉 dnsmasq 使用的内容/etc/hosts(如果您这样做))

所以,这个问题的第二部分是如何禁止对 0.0.0.0 的访问被重定向到 127.0.0.1 ?我想要这个是因为当使用 NextDNS (或cloudflare 的 1.1.1.3)作为 DNS 解析器,它返回0.0.0.0被阻止的主机名,而不是 NXDOMAIN,因此在加载网页时,其中的一部分(位于被阻止的主机名上)将尝试访问在端口 443 上运行的本地主机服务器(如果有)并从以下位置加载页面它而不仅仅是被阻止。

相关浏览器特定的公共问题意识到这一点(0.0.0.0 映射到 127.0.0.1):
Chrome/Chromium:https://bugs.chromium.org/p/chromium/issues/detail?id=1300021 火狐浏览器:https://bugzilla.mozilla.org/show_bug.cgi?id=1672528#c17

答案1

为什么会发生这种情况的解释如下连接IP 0.0.0.0成功。如何?为什么?— 简而言之,没有目标地址 (0.0.0.0) 的数据包将其源地址复制到其目标地址,而没有源或目标的数据包将其源地址和目标地址设置为环回地址 (INADDR_LOOPBACK,127.0.0.1);生成的数据包在环回接口上发送出去。

正如您所确定的,此行为是硬编码在 Linux 内核的 IPv4 网络堆栈中,改变它的唯一方法是修补内核:

diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 795cbe1de912..df15a685f04c 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2740,14 +2740,8 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4,
        }
 
        if (!fl4->daddr) {
-               fl4->daddr = fl4->saddr;
-               if (!fl4->daddr)
-                       fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
-               dev_out = net->loopback_dev;
-               fl4->flowi4_oif = LOOPBACK_IFINDEX;
-               res->type = RTN_LOCAL;
-               flags |= RTCF_LOCAL;
-               goto make_route;
+               rth = ERR_PTR(-ENETUNREACH);
+               goto out;
        }
 
        err = fib_lookup(net, fl4, res, 0);

该补丁显示了原始实现,解释了“为什么?”上面的部分:如果数据包没有目标地址(IE是 0.0.0.0):

  • 源地址被复制到目标地址
  • 如果数据包仍然没有目标地址,IE它也没有源地址,两个地址都设置为环回地址(127.0.0.1);
  • 在所有情况下,传出设备都设置为环回设备,并相应地构造路由。

该补丁更改了此行为,改为返回“网络无法访问”错误。

答案2

简短的回答是事实并非如此。情况比这还要糟糕得多。

0.0.0.0 映射到的 IP 地址任何您系统上的界面。因此,如果netstatip说它0.0.0.0:22是开放的,那么您的系统将接受通过环回、以太网、wifi 以及您可能拥有的任何其他网络接口传入的 SSH 连接。只是环回是唯一一个侦听您的端口的接口,或者在接口中具有优先级。

相关内容