目录

理解NAT和内网穿透

不少小伙伴有公网访问局域网的需求,也许是在外面需要访问家里的网络,也可能是公网服务需要回调你在开发的程序,大部分情况下实现内网穿透并没有那么难;我主要是记录下对 NAT 技术的理解,以及它对内网穿透的影响。

看你下大约在 7 年前我就做过一篇关于内网穿透的笔记,现在我觉得有必要进行整理下给正式的发出来,当年还没有 AI 很多解释还需要自己去找,现在问 AI 方便的多,我也是精简一下做个记录。

Tip

由于整个 HTTP 请求到回复过程牵扯到的技术太多,这里默认当作各位有一定的了解,如果不理解可以问下 AI,这对理解和解决一些网络问题,或者配置"魔法"软件的时候很有用。

这里主要关注这个链路中 NAT 和 IP 的部分。

由于联网设备众多和国际上我们分得的 IPv4 数量无法保证为每一个宽带用户提供全球唯一的公网 IPv4 地址。因此很多用户会发现通过路由器端查看到的 WAN 端 IP 与百度 “IP” 关键词所得到的 IP 不一致,并且前者的 IP 为一个私有 IP。

而还有一些情况下,公网IP比较昂贵,企业虽然本身也持有少量的独立的公网IP,但是由于成本限制无法为企业内每一台主机都提供一个公网IP,或者内网并不是所有服务都需要暴露到公网中进行访问,那么企业有可能就会使用 NAT 技术将大量的内网 IP 通过一定规则映射到公网 IP 上。

而最常见的其中一种技术就是 NAPT,也叫“网络端口地址转换”。因为一般一个服务都是通过一个端口来提供,因此通过这种方式可以将特定的服务通过特定的规则开放到少量的公网 IP 上。

国内家庭宽带(移动尤其严重)常见的就是 100 开头的地址,公司学校的网络也可能如此,这种情况无法通过**端口映射 ** 等方法让内网设备暴露给外网,只能通过内网穿透方式。

我当前使用的联通改桥接后获取到的是公网 IP,我目前虽说不需要内网穿透了,但是大部分人并没有我这么好运气。

在计算机网络中,网络地址转换(Network Address Translation,缩写为 NAT),是一种在 IP 数据包通过路由器或防火墙时重写来源 IP 地址或目的 IP 地址的技术。

假设我的 PC 机是 192.168.1.100,我的手机是 192.168.1.101,宽带拨号获取到的公网 IP 是 203.0.11.12,这个 IP 地址就代表着我家网络总出口;

现在如果我访问淘宝,淘宝服务器只能看到 203.0.11.12 这个 IP 的某个端口(某个设备)访问了它,不能确定具体是那个设备,这就导致了即使知道我家 IP 是 203.0.11.12,也不能直接地对网络下具体的设备发起通信。

我们作为客户端请求公网服务使用的是系统分配的临时的随机端口,而淘宝网作为服务是使用的固定端口,也是为了让别人都能找到它,具体就是访问时可能就是 203.0.11.12 的 10101 端口与淘宝服务器的 443 端口建立连接。

但是在路由器大多数也是网关(理解为大门,所有的数据进出都需要经过它)这个关键角色下,它知道并且会记录家里的那个设备访问你外面的那个服务,当别人给你回复数据的时候先经过网关,再按照对应表转换成给具体某个设备的数据包。

大体先有个概念,下面会继续解释。

这种技术被普遍使用在有多台主机但只通过一个公有 IP 地址访问因特网的私有网络中。根据规范,路由器是不能这样工作的,但它的确是一个方便且得到了广泛应用的技术。当然,NAT 也让主机之间的通信变得复杂,导致了通信效率的降低

NAT 最初发明的一大目的就是解决公网 IP 的 IPv4 地址资源枯竭的问题,所以国内大城市的主流运营商经常会使用这种方法来规避资源枯竭的问题(我国普及 IPV6 可能还需要一定时间),端口 NAT 转换让 IP 地址得到了极大的利用,NAT 的一个特点就是:对外隐藏了真实地址,有的甚至还不止一层 NAT。

在一定程度上,NAT 依赖于本地网络上的一台机器来初始化(这个活一般是路由器负责)和路由器另一边的主机的任何连接,它可以阻止外部网络上的主机的恶意活动。这样就可以阻止网络蠕虫病毒来提高本地系统的可靠性,阻挡恶意浏览来提高本地系统的私密性。很多具有 NAT 功能的防火墙都是使用这种功能来提供核心保护的。

运营商会贴心的禁用高危端口,所以即使有公网 IP,80、443、22 这些端口也是没法用的。

这一种也可称作 NAT 或“静态 NAT”,在 RFC 2663 中提供了信息。它在技术上比较简单,仅支持地址转换,不支持端口映射,这种情况应该是很少见的吧。

但是由于改变了 IP 源地址,在重新封装数据包时候必须重新计算校验和,网络层以上的只要涉及到IP地址的头部校验和都要重新计算。

Basic NAT 要维护一个无端口号 NAT 表,结构如下:

内网IP外网IP
192.168.1.55219.152.168.222
192.168.1.59219.152.168.223
192.168.1.155219.152.168.224

这种方式支持端口的映射,并允许多台主机共享一个公网 IP 地址。

支持端口转换的 NAT 又可以分为两类:源地址转换和目的地址转换

前一种情形下发起连接的计算机的 IP 地址将会被重写,使得内网主机发出的数据包能够到达外网主机

后一种情况下被连接计算机的 IP 地址将被重写,使得外网主机发出的数据包能够到达内网主机

实际上,以上两种方式通常会一起被使用以支持双向通信

NAPT 维护一个带有 IP 以及端口号的 NAT 表,结构如下:

内网IP外网IP
192.168.1.55:5566219.152.168.222:9200
192.168.1.59:80219.152.168.222:9201
192.168.1.59:4465219.152.168.222:9202

在 NAT 网关上会有一张映射表,表上记录了内网向公网哪个 IP 和端口发起了请求,然后如果内网有主机向公网设备发起了请求,内网主机的请求数据包传输到了 NAT 网关上,那么 NAT 网关会修改该数据包的 IP 地址和端口为 NAT 网关自身的 IP 地址和任意一个不冲突的自身未使用的端口,并且把这个修改记录到那张映射表上。

最后把修改之后的数据包发送到请求的目标主机,等目标主机发回了响应包之后,再根据响应包里面的目的 IP 地址和目的端口去映射表里面找到该转发给哪个内网主机。

这样就实现了内网主机在没有公网 IP 的情况下,通过 NAPT 技术借助路由器唯一的一个公网 IP 来访问公网设备。

说一下它的特点吧,也可以说是 NAT 的特点:

  1. 网络被分为私网和公网两部分,NAT 网关设置私网到公网的路由出口.
  2. 网络只能由私网侧发起,公网无法主动访问私网主机 (是这样设计的,但是可以通过打洞的方式,就是内网穿透)
  3. NAT 网关在两个访问方向上完成两次地址的转换,出口替换源地址,入口替换目的地址
  4. NAT 网关的存在对通信双方保持透明
  5. NAT 网关为了实现双向翻译,需要维持一张关联表,将会话信息保存

从这里我们可以看到,NAPT 只解决了内网主机在没有公网 IP 的情况下如何访问公网主机的问题,但是并不能解决公网主机如何主动向内网主机发起请求的问题。

Tip

NAT 还可以用作负载均衡、失效终结、透明代理等。

在确定内外穿透方案之前,需要先了解一下自己网络的 NAT 类型,不同的类型穿透的难度是有区别的,根据映射关系中对外部主机发送的数据的限制可以分为以下几种类型:

NAT 类型映射行为外部主机发送到内部(谁能发回)穿透难度
Full Cone 全锥形固定映射:内IP:内端口 -> 外IP:外端口,对所有外部连接生效。任何外部主机只要知道这个公网IP和端口,就能向内网主机发送数据。最易
Restricted Cone 受限锥形固定映射:内IP:内端口 -> 外IP:外端口内网主机需先向外部主机发送数据,该外部主机才能向内网主机发送数据。容易
Port Restricted Cone 端口受限锥形固定映射:内IP:内端口 -> 外IP:外端口内网主机需先向外部主机发送数据,然后被内网主机访问过的这个外部IP和端口可以回复数据中等
Symmetric 对称形动态映射:内IP:内端口 -> 随机外IP:不同的外端口。每次与新目的通信都可能分配新端口。内网主机与每个外部主机通信时创建的 IP 和端口可以回传数据最难

Port Restricted Cone NAT 和 Restricted Cone NAT 的主要区别点:

特性Restricted Cone NAT (限制锥形 NAT)Port Restricted Cone NAT (端口限制锥形 NAT)
映射端口对于同一个内部 IP:端口,外部映射端口始终不变,无论目标是谁。对于同一个内部 IP:端口,外部映射端口始终不变,无论目标是谁。
入站过滤更宽松更严格
入站条件必须是内部主机主动发送过包的目标 IP 的入站包才允许通过。必须是内部主机主动发送过包的目标 IP 和目标端口 的入站包才允许通过。
源端口限制不限制外部入站包的源端口。只要源 IP 匹配即可。严格限制外部入站包的源端口。源 IP 和源端口都必须匹配。
UDP 打洞更容易。只要双方都向对方的公共 IP 发送过包,就能打通。可以打洞。双方必须同时向对方的公共 IP:Port 发送包,才能打通。因为入站包的源 IP 和源端口都必须匹配。

有些人(大部分游戏玩家)也会将 NAT 分为以下类别:

  • NAT Type 1 (Open / 开放)

    对应的技术 NAT 类型: 无 NAT 或者 Full Cone NAT (如果确实有NAT但非常宽松,行为上等同于没有限制)。

  • NAT Type 2 (Moderate / 中等)

    对应的技术 NAT 类型: Restricted Cone NAT 或 Port Restricted Cone NAT。

  • NAT Type 3 (Strict / 严格)

    对应的技术 NAT 类型: Symmetric NAT。

或者简单粗暴按照上面表格的四种类型 NAT 1、2、3、4,测试自己是那种 NAT 类型可以使用一些公共 STUN,小米也提供了一个1。我也找了一些开源工具2,但是整体来说 Surfboard 自带的 NAT 检测更好用,这类魔法软件大多也自带测试工具。

STUN (Session Traversal Utilities for NAT) 协议的主要作用是帮助内部客户端发现它自己被 NAT 映射后的公共 IP 地址和端口,以及识别其所处的 NAT 类型。

工作原理是客户端向一个已知的 STUN 服务器发送请求。STUN 服务器会回复客户端它看到的客户端的公共 IP 和端口。

仅仅通过一个简单的绑定请求是无法识别 NAT 类型的。STUN 协议定义了更复杂的测试机制来区分不同的 NAT 类型。这通常涉及客户端向 STUN 服务器的不同端口或不同的服务器 IP 地址发送额外的请求,并分析服务器的响应行为。


客户端首先向 STUN 服务器的 IP1:Port1 发送一个请求。服务器在响应中返回客户端的公共 IP:PortA。 然后,客户端要求 STUN 服务器由另一个 IP2:Port2 发送一个响应包(或者 STUN 服务器本身就用不同的 IP/Port 回复)。

如果客户端能成功收到来自 IP2:Port2 的响应,说明 NAT 是 Full Cone NAT。因为 Full Cone NAT 对所有外部流量都是开放的,只要内部有出站连接,外部任何源地址和端口都可以发送数据回来。

如果客户端收不到响应,说明 NAT 可能不是 Full Cone,需要进一步测试。


客户端向 STUN 服务器的 IP1:Port1 发送一个请求,得到映射后的公共 IP:PortA。

然后,STUN 服务器从一个不同的 IP 地址 (例如,IP2:Port2) 向客户端的公共 IP:PortA 发送一个请求(注意,这个请求的源 IP 改变了,但端口可能相同或不同)。

如果客户端能收到来自 IP2:Port1 (相同端口,不同 IP) 的响应,但收不到来自 IP2:Port2 (不同 IP,不同端口) 的响应,则可能是 Port Restricted Cone NAT。

因为 Port Restricted Cone NAT 允许来自原始目标 IP 相同端口的响应,但会过滤掉来自不同端口的连接。

如果客户端能够收到来自 IP2:Port1 和 IP2:Port2 的响应,则可能是 Restricted Cone NAT。 因为 Restricted Cone NAT 允许来自不同 IP 但相同端口的响应,并且可能也允许来自不同端口的响应。

这部分测试通常比较复杂,STUN 客户端会通过多次请求和比较响应来确定 NAT 的过滤规则。


客户端从同一个内部 IP:Port,向 STUN 服务器的不同 IP 地址或不同端口发送绑定请求。

如果每次请求得到的外部映射端口都不同,那么几乎可以确定是 Symmetric NAT。


UDP 打洞对于 Full Cone NAT 和 Restricted Cone NAT 还是很简单的,对于 Port Restricted Cone NAT 的关键在于外网IP_目标是受限制的,必须是内部主机主动发送过包的目标 IP。即使外部包的源端口不同,只要源 IP 匹配,它就能进来。

STUN 主要用于识别 NAT 类型和获取映射信息。它不能直接穿透 Symmetric NAT,因为它无法预测 Symmetric NAT 每次都会改变的端口映射。

对于 Cone NATs(Full, Restricted, Port Restricted),STUN 可以协助进行 UDP 打洞。

复习下网络方面的知识,这个肯定都学过,至于还记不记得…..

因特网域名分配组织 IANA 组织(Internet Assigned Numbers Authority)保留了以下三个 IP 地址块用于私有网络。

10.0.0.0 - 10.255.255.255 (10/8比特前缀)

172.16.0.0 - 172.31.255.255 (172.16/12比特前缀)

192.168.0.0 - 192.168.255.255 (192.168/16比特前缀)

主流的家用路由器使用 C 类私有地址作为路由器 LAN 端的 IP 地址较多,所以我们可以看到路由器设置页面的 IP 一般都为 192.168 开头。

一些大型企业就需要使用B类甚至A类地址段作为内部网络的地址段。A类地址的容量最大,可以容纳16777214个主机,B类地址可以容纳65534个主机,C类地址可以容纳254个主机。

还有就是 100 开头的这种 IP,大多也不是公网,一般用作运营商级 NAT。

先说说家庭宽带的情况吧。家庭宽带如果没有公网 IP,那么意味着你在本机上监听的任何端口,都只能在本机网卡所在的网络中访问(LAN),也就是常说的内网/局域网。

如果这种情况下想要让 WAN 端(如果运营商为你分配了公网 IP,那么 WAN 端所连接到的网络通常就是公网),那么需要在路由器上做端口映射。比如说路由器的 LAN IP 为 192.168.1.1,WAN IP 为 23.23.23.23;

我想让内网 192.168.1.2 主机的 80 端口提供的 HTTP 服务器直接能够在公网中通过 http://23.23.23.23 访问,那么就要将 192.168.1.2:80 映射到 23.23.23.23:80 上(当然,家庭宽带是不会给你开放 80 端口的)。

但是通常情况下,运营商是不会给普通用户公网 IP 的。那么用这种方法映射,在公网仍然是无法访问的,因为你的路由器 WAN 端连接的又是运营商更上一级的路由器 LAN 端,严重一点,甚至是层层连接最后才到公网,这种行为称作流量穿透

国内某电,某动的宽带就有大量这种行为。通过流量穿透的方式来提供的宽带服务,看似便宜,实则影响很大,由于大家公用一个 IP,可能会导致很多网站的反 SPAM 策略伤及无辜,或者内部为了节省带宽,使用缓存,导致一些不该缓存的敏感安全页面被缓存起来,甚至导致部分网站缓存失效完全打不开。

有的人发现,即使自己有公网IP,但是仍然无法通过常规方法架设服务器,这是怎么回事呢?这是运营商为了防止个人随意开设各种非法服务,也防止黑客通过扫描器进行抓鸡和批量扫描,将一些常用端口进行了封禁,比如说我们这的中国电信就将 80,8080 等端口封禁了。

这样封禁,虽然一定程度上保证了我们的网络安全,比如说前段时间的勒索病毒正因为国内大部分用户没有独立的公网IP,并且操作系统最容易爆发漏洞的一些,135,139 等端口被运营商封禁了,使得国内个人家庭电脑中招的概率小了很多;但是导致即使有公网 IP,也无法使用常用端口向外网提供服务,只能更换到其他端口。这样有什么不好呢?比较典型的问题就是 WEB 网站默认使用 80 端口,那么在输入网址的时候可以不用带上端口号,显得比较美观。

还有的时候我们在对企业做渗透测试的时候,发现企业某台公网服务器只对公网开放了常见的 80 端口,而我们提权时需要用到的 3389 等端口没有对公网开放,这个时候又该怎么办呢?

我们如果在自己拥有一台具有公网 IP 服务器的情况下,我们可以借助这台公网 IP 服务器提供转发服务。解决这些问题的方式就是内网穿透了,比较流行的软件比如 Frp,当然如果能进行 UDP 打洞实现 P2P,要比中转快的多(服务器带宽不高的情况下)。

先介绍通用方案,先假设我们自己有一台公网服务器,他的 IP 为 45.45.45.45,通过这台服务器建立一个隧道,转发我们的数据请求,有点类似代理。

我们的一台内网服务器 IP 为 10.10.10.10 ,我们现在想把它上的 HTTP 服务通过 45.45.45.45 提供对外访问。

也就是借助公网服务器的网络来发布一个内网服务

因为公网服务器 IP 是公开的,我们让内网的 10 机器去连接 45 的某个端口,这样 45 和 10 之间建立你一个长连接;

然后当有任何客户端主动连接公网 45 的 80 端口时,公网接收到数据之后通过先前建立好的隧道转发到内网主机 10,内网主机接收到来自隧道的数据包后再主动连接内网主机自身的 80 端口,待 HTTP 服务器程序处理完这个数据包,生成了响应报文之后再原路转发回去,最终到达公网的 80 端口,然后返回给最开始请求公网服务器 80 端口的客户端。

这种原封不动的转发方式通常叫做透明传输或者透明代理。

这种方法基本适用于所有的网络环境,只要你的设备能上互联网,前提是你得拥有一台拥有"公网IP"的主机充当"跳板",比较流行的工具有 Frp。


UDP 打洞虽然一般也需要一个 STUN 服务器做探测,以决定使用什么方式穿透,但是建立 P2P 连接后就不需要走服务器流量了,除非是

像一些视频、语音通话,P2P 下载,都可以使用这种方式。

UDP Hole Punching(UDP 打洞)原理是当两个位于不同 NAT 后面的客户端想要直接通信时,它们都向一个已知的“打洞服务器”(或协调服务器)发送一个 UDP 包,由协调服务器通知双方对方的 IP 和端口信息。

客户端 A 向打洞服务器发送一个 UDP 包。NAT A 为 A 创建一个临时映射(例如 外A_IP:外A_端口)。 客户端 B 向打洞服务器发送一个 UDP 包。NAT B 为 B 创建一个临时映射(例如 外B_IP:外B_端口)。

打洞服务器将 外A_IP:外A_端口 告知 B,将 外B_IP:外B_端口 告知 A。 此时,A 知道 B 的公共地址,B 知道 A 的公共地址。它们会同时向对方的公共地址发送 UDP 包。

如果 NAT 是 Cone 类型:当 A 的 UDP 包到达 NAT B 时,NAT B 会检查这个包。由于 B 之前已经向外发送过包,NAT B 可能会认为这是一个合法的返回流量,从而允许这个包通过。同理,B 发给 A 的包也能通过 NAT A。这样,两个客户端之间的“洞”就被打通了,可以直接进行 P2P 通信。

适用性: 对 Full Cone NAT、Restricted Cone NAT、Port Restricted Cone NAT 效果很好。

局限性: 无法穿透 Symmetric NAT。因为 Symmetric NAT 每次向不同目的发送包时会使用不同的外部端口。当 A 知道 B 的公共 IP 和端口,并向其发送包时,NAT A 会为这次通信分配一个新的外部端口,这个新端口可能不是 B 的 NAT 所期望的那个端口,从而导致包被丢弃。

为了安全起见,通常会在网络中加入防火墙,防火墙有入站规则和出站规则。如果不是非常严格的安全管控,通常是不会设置出站规则的,但是入站规则一般都会设置的,比如说外部可以通过80端口传入内网的WEB服务器访问网页,但是不能通过3389端口登陆内网的远程桌面。

而在内网渗透的过程中碰到这种情况,我们也可以借助上面内网传统的方式实现穿透防火墙的入站规则。因为防火墙通常只拦截了入站,没有拦截出站,那么我们可以让内网服务器主动出站(主动连接到黑客的服务器),与黑客自己的服务器打通隧道,最终绕过防火墙连上3389远程桌面。

还有一种情况就是我们已经拿下了内网其中一台并没有做任何防火墙规则的白名单服务器,但是我们想连上内网另一台做了入站规则的目标服务器,那么我们可以让这台白名单服务器作为一个跳板,让他先监听自身任意一个端口,然后在有任何用户连上这个端口之后,白名单服务器就主动连上内网的目标服务器,然后借助这台白名单服务器打通黑客和目标服务器的连接隧道。

而在黑客工具中大名鼎鼎的 lcx 原理也就是如此,前者的实现是 lcx 的 listen 和 slave 命令,后者的实现是lcx的tran命令。

IP 隧道是指一种可在两网络间用网际协议进行通信的通道。在该通道里,会先封装其他网络协议的数据包,之后再传输信息。

在IP隧道中,每个IP包、来源/目的地址信息都被封装在一个数据包中,该数据包用于实际物理网络传递。

因为防火墙的本质及原始数据报文被隐藏了,IP 隧道经常用于绕过简单的防火墙规则,通常需要通过内容控制软件才能对 IP 隧道进行筛查。

如果你拥有公网 IP,下面的 DDNS 和端口映射你大概也需要。

适用于有公网 IP 的,在路由器的管理界面应该看到过,由于运营商分配的公网 IP 地址不断变动,DDNS的原理就是内网设备(一般是路由器)每隔一段时间对DDNS服务器发起请求,DNS服务器将请求的IP记录下来并且刷新相关域名的解析记录(一般是A记录),这样每次拨号怎么变动 ,域名总是指向路由器所分配的公网IP。

一般DDNS服务都是免费的,常见的路由器里会集成,但是并不好用,推荐 DDNS-GO。

不过让我想到了以前玩远控时候用的动态域名,肉鸡上线后会自动链接这个域名,然后通过 DDNS 就相当于都在链接你的电脑了。

应用:解决很多网络视频服务器和网络摄像机通过远程访问时需要一个固定的 IP,这也是要公网 IP 的最好接口,家里有摄像头需要。

上面说过一些端口例如 80 运营商的防火墙可能会拦截,并且只通过一个公网 IP 无法确定具体是找你局域网的那台设备,因为这个公网 IP 是你家庭所有设备通过 NAT 共用的;

路由器作为家庭网络的入口,可以在上面配置当有人请求我(公网 IP)的 8000 端口的时候,转发到内网的 192.168.9.1 这台机器的 80 端口上,这个规则就是一条端口映射。

UPnP (Universal Plug and Play) 是一套网络协议的集合,旨在使网络设备(如电脑、手机、路由器、打印机、智能家电、游戏机等)能够彼此自动发现、互相通信和协同工作,而无需用户进行复杂的配置。 它的核心理念是“即插即用”——设备连接到网络后,能够自动与其他设备和应用程序交互。

简单来说,UPnP 就是让家里的智能设备们能够“说上话”,并且能自动地完成一些网络配置任务。

内网穿透语境中,表示允许局域网内的设备自动请求路由器进行端口映射 (Port Mapping)。 也就是说,内部设备可以向支持 UPnP IGD 的路由器发送指令,告诉路由器“请帮我把外部的某个端口映射到我内部的某个端口和 IP 地址”。

路由器中这个功能默认是关闭的,我也不建议开启,它带来的风险比较大。

简单安全的暴露自己的 web 服务可以使用 CloudflareTunnels,但是隐藏在 CF 的后面必然会慢一些,国内某些地方的网络也可能不通。


  1. 例如 https://www.checkmynat.com 在线探测,小米(stun.miwifi.com:3478)和搜索关键字 public-stun ↩︎

  2. 例如 NatTypeTesterNATMap ↩︎