NAT 二三事

本篇着实算不上科普或教程,只能算是心得或随笔吧。

二〇〇几年家里刚通宽带的时候,内网两台电脑(台式机、笔记本)要同时上网。没买路由器之前,台式机在用两块网卡进行连接共享,把入户宽带共享给笔记本。后来购置了路由器,很快就学会了端口映射,想把笔记本的文件通过 FTP 共享到公网,只需在路由器中设置端口映射,将公网 IP 地址的 21 端口映射到内网笔记本 IP 地址的对应端口即可。那时候也不太懂转发的概念,只知道此时 “公网 IP 的某端口等价于内网 IP 某端口” 。

eMule 也可以通过以上方法来把内网 IP 的两个端口暴露到公网来获取 HighID 。但是另一些 p2p 软件可能会通过 UPnP 功能来自动完成以上步骤。 PnP(即插即用)在当时对我来说就是 windows 插上 USB 设备无需装驱动即可正常使用的概念,那么我就联想到 UPnP(通用即插即用)就是内网设备自动向路由器申请开通一些上述端口的映射规则,直到今天(2022 年)都在大量使用,几乎是家用路由器的标配。

就这样过了很长时间,在近几年家用游戏机流行后,我突然在网上了解到 “NAT1” 的概念,也就是家用主机在内网运行时和其他玩家直接建立连接的困难程度。 ipv4 年代大家基本上都在 NAT 后面上网,加之绝大多数游戏都是基于 UDP,那么在自家 NAT 后面和同样在 NAT 后面的其他人建立连接竟然变成了一件有点复杂的事,涉及到 “UDP 打洞” 。下载 NatTypeTester 工具一试,家里的 Merlin 固件路由器带来的 NAT type 竟然是最无趣 “Symmetric”,而 padavan 固件就提供选项可以把 NAT type 设最宽松的 type “full cone”(RFC 3489 标准,最新的 RFC 5780 标准搞得愈发复杂也没什么人用)

我认为这篇博文把 NAT type 讲得很清楚,还说明了利用生日攻击去碰撞端口的方法,很有意思。我对 STUN NAT type 的理解是这样:

假设内网的 src_lan_ip:src_lan_port 欲向公网 dest_ip:dest_port 发送 UDP 包,发到 NAT 网关处,NAT 执行 SNAT 改写,挑一个可用端口 src_wan_port,在公网以 src_wan_ip:src_wan_port 发向 dest_ip:dest_port 。

  • full cone:在 NAT 处存在 src_lan_ip:src_lan_port 到 src_wan_ip:src_wan_port 的映射之后,src_lan_ip:src_lan_port 发往任意别处 dest 的数据都会从原始 src_wan_ip:src_wan_port 出去。这样由点(原始映射)到面(任意公网目标),用几何图形来形容就像一个圆锥,因此称为全锥形 NAT 。此后,在这个映射关系的生命周期内公网上的任何 ip:port 都可以反过来向 src_wan_ip:src_wan_port 发包,会被转回 src_lan_ip:src_lan_port 。
  • Restricted cone:原始 src_lan_ip:src_lan_port 发往任意别处 dest 的数据都会从原始 src_wan_ip:src_wan_port 出去。但是,该映射的生命周期内,只有 dest_ip(端口不限)才能向 src_wan_ip:src_wan_port 发包,会被转回 src_lan_ip:src_lan_port 。(与 full cone 相比,限制了外部 IP)
  • Port Restricted cone:原始 src_lan_ip:src_lan_port 发往任意别处 dest 的数据都会从原始 src_wan_ip:src_wan_port 出去。但是,该映射的生命周期内,只有 dest_ip:dest_port 才能向 src_wan_ip:src_wan_port 发包,会被转回 src_lan_ip:src_lan_port 。(与 Restricted cone 相比限制了 port,与 full cone 相比限制了 IP+port)
  • Symmetric: 原始 src_lan_ip:src_lan_port 发往 dest1 的包会被映射为 src_wan_ip:src_wan_port1,发往 dest2 的包会被映射为 src_wan_ip:src_wan_port2,以此类推(此处以单 WAN 口为例,如果是多个 WAN 口,一定不会出现同 ip:port 的情况)。每一个不同的目的地,都会在 wan 上出现一个新的端口连出去。这样看 WAN 内外部的 UDP 连接像是以 NAT 网关为轴 “对称” 的,所以称为 “对称型 Symmetric”NAT 。且对返回包策略与 Port Restricted 一致,严格限制 ip+port 。

从上到下,规则越来越严格,NAT 打洞也越来越困难。因为 STUN 打洞需要一个第三方服务器,为同在 NAT 后面的通信双方打洞建立连接,STUN 服务器和其中一方一定不会是同一 ip 或 port,从而被严格 NAT 的另一方以 “来源不同” 拒绝连接,所以严格的 NAT 策略会给打洞带来很大的困难。

最近在使用 tailscale 的时候,发现他们的博客 How NAT traversal works 写得非常详尽,连双方都是 CGNAT 这样令人头秃的情况也考虑到了,非常推荐一读。中文译文

tailscale 的思路比较明确,先把 UPnP 列为最优先的打洞方式,直接向 NAT 网关申请端口,省去了后面所有的麻烦。顺便,除了 UPnP,我还在命令行 tailscale netcheck 里学到了,UPnP 在发现阶段之后,通过 xml 文档向 NAT 网关申请端口映射。与 UPnP 类似的技术还有:NAT-PMP 和 PCP,NAT-PMP 由 Apple 开发,PCP 则是 NAT-PMP 的高级演化版,以基于 UDP 的通信来向 NAT 网关申请端口映射规则。

记一次内网论坛 XSS+越权漏洞的综合利用

本文原成于 2020 年末,缘起当时公司举办的针对内网系统的安全众测挖洞活动。本来我对内网实名论坛这种东西没什么兴趣,但在活动之前,有同事邀请在论坛内帮忙点赞刷人气,当时就发现了一些问题但没有深究。于是在活动期间深入研究一番,写了一篇 POC 提交了上去。

论坛在发状态的时候可以选择表情,这是一个很常见的功能。但该论坛里引用的表情图片竟然不是本地文件或者类似于微信的那种 [赞]转义代码,而是外部(新浪微博)的 URL,如 http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/86/2018new_quantou_thumb.png ,而且在发状态的时候,选中的表情以完整的 URL 写在 POST body 中。安全信条说永远都不要相信用户的任何输入,那这种用户能控制的图片 URL 是否可以搞点事情,如储存型 XSS 呢?

把图片的 <img src> 替换为非法地址,附上事件代码 on error=alert ,提交以后弹窗成功。

储存型 XSS 验证成功了,如果引入外部的 js 代码真正构造一个 “跨站” 脚本攻击,还可以做点什么事呢?构造一个调用发帖接口发送内容的 js,在中招人不知情的情况下再发一帖,内容依然是偷摸发帖代码,导致看中招人帖的观众再中招再发帖,如此往复,这就是 XSS 蠕虫了。

使用 Burpsuite 的 CSRF 模块验证了一下,结果发现发帖时需要校验 HTTP 头部的 x-token,验证失败。但是再一番探测之后又发现一个越权查询接口 /……/get/user/info/{用户名} ,把用户名作为参数,在不需要 sessionID 的情况下都可以查到该用户当前的 x-token!至此,一个 XSS 蠕虫攻击的所有要件都已齐备。

试想,攻击者先发一帖,埋入储存型 XSS,看帖的人加载了攻击者所埋的 js 脚本,该脚本通过越权接口查询当前登入用户的 x-token,加到到请求头中,调用发帖接口再发出含有 XSS payload 的帖子,如上所述导致看帖者再中招发帖,似病毒传染一般形成 XSS 蠕虫。

由于我的 javascript 代码水 (ji) 平 (hu) 有 (bu) 限 (hui),况且也不能真的构造一个 js 蠕虫,那样影响实在太大,因此上述 POC 构思也仅仅是停留在当年的纸面上,随着论坛后来的升级改造,连土壤都不复存在。但给我的启示是,一两个漏洞看似危害不大,但是两个甚至更多的小漏洞的互相联合利用,将会带来危险程度呈指数级上升的严重危害。