最近刚好看到一段视频,讲述关于 IP 伪造的内容。视频中并没有具体描述如何进行的 IP 伪造。借此机会,小黑屋来唠唠伪造 IP 的几种常见方式。
方式1: X-Forwarded-For
这个是最为认知的 IP 伪造方法,早年的 CTF 题目也经常涉及,然而现在知道的人太多, CTF 都不屑于出这类题目。 X-Forwarded-For 诞生的原因比较简单粗暴。 对于一个非常简单的网络模型, 一个网络请求通常只有两方,即请求方与被请求方,如下所示。这样的网络模型下, Web Server 是可以拿到 User 的真实 IP 地址的,即使拿到的可能是路由器的地址。
User --> Web Server
但是上了规模的网站,其网络模型不会这么简单,它可能长这样:
User --> CDN --> Web Server
在这种场景下, CDN 依旧可以拿到 User 的真实 IP 地址,然而 Web Server 却无法直接拿到。 为了解决这个问题, 有人提出了 X-Forwarded-For, 它作为 HTTP Header 传递给后端的 Web Server,其格式如下:
X-Forwarded-For: <client>, <proxy1>, <proxy2>
假设 User 的真实 IP 地址是 1.0.0.1, CDN 节点的 IP 地址是 2.0.0.2,那么 CDN 会在 HTTP 请求头里附加下面的 Header,通知 Web Server 用户的真实 IP 地址。 Web Server 根据这个 Header 解析出 User 的 IP。
X-Forwarded-For: 1.0.0.1, 2.0.0.2
细心的朋友可能会发现, 我是不是可以直接将 1.0.0.1 改成任意 IP 地址,然后直接将请求发送给 Web Server?没错,这就是非常简单的 X-Forwarded-For IP 伪造攻击。一般这类问题的解决思路是,校验 4 层协议的来源 IP,判断是否为可信 IP,比如是否为 CDN 的 IP。如果可信,才会尝试解析 X-Fowarded-For Header。
方式2: Proxy Protocol
眼尖的朋友可能已经注意到了,X-Forwarded-For 只支持 HTTP 协议,那么 TCP 或者其它 4 层协议怎么办?这时候 Proxy Protocol 应运而生了。它最早于 2010 年被提出,并首先运用于 HAProxy 。 由于 Proxy Protocol 解决了实际应用中的痛点,越来越多的开源软件(如 NGINX), CDN 厂商(如 Cloudflare 和 Cloudfront 等)已经支持 Proxy Protocol 了。
目前 Proxy Protocol 共有两个版本,分别为 v1 和 v2。
Proxy Protocol v1 协议非常简单易懂。由于本文只是介绍,不会写过多的技术细节,力求用最简单的言语让读者知道它是怎么工作的。我们假定网络模型如下所示:
User --> Load Balancer --> TCP Server
V1 的原理说起来也非常简单, 当用户与 Load Balancer 的 4 层链接建立后(可能是 TCP ,也可能是 UDP), Load Balancer 是知道用户的真实 IP 的。 Load Balancer 在和 TCP Server 建立 4 层链接后,不会直接透传用户的请求,而是提前发一个 Proxy Protocol V1 的 header。 这个 Header 具体长这样
PROXY TCP4 1.0.0.1 2.0.0.2 1001 2002\r\n
其中:
PROXY 表示当前是一个4层代理请求
TCP4 表示 User 使用 TCP v4 与 Load Balancer 建立的 4 层链路
1.0.0.1 为 User 的 IP, 2.0.0.2 为目标 IP
1001 为 User 的端口, 2002 为目标端口
当 V1 header 发送到 TCP Server 后, Load Balancer 才会开始透传 TCP 请求。而 TCP Server 需要做一些调整,解析完 Header 后,才开始进行业务逻辑。 幸运的是,目前许多 Server,包含 NGINX,已经支持了 V1 header 的解析,改改配置即可。
类似的, Proxy Protocol 也有 IP 伪造问题。攻击者是可以直接构造一个 V1 header, 直接发送给 TCP Server 的,造成 TCP 来源 IP 地址伪造问题。
Proxy Protocl V2 版本实际上是针对 V1 版本的升级优化。 V1 版本是一个纯文本协议,其最大的缺点是 Header 占用的字节太多了,比如上面的例子中就占用了 38 个字节。然而 Header 是给机器看的,又不是给人看的,可读性这么高有卵用? 因此,V2 实际上是将 V1 升级成了一个二进制版本。它的构造相对来说没那么直观。以 IPv4 版本为例,其格式如下:
0 1 2 3
V2 Header 在 IPv4 版本中, 只固定占用了 28 字节, 比 V1 版本少了约 10 字节(此处注意是“约”, v1 版本是变长的)。
Proxy Protocol V2 本质上只是变更了 Header 的编码方式,还是存在 IP 地址伪造问题。
方式3: TOA (TCP Option Address)
相比前两种协议,TOA 的知名度并没有那么高。 TOA 的原理是利用 TCP 协议中的一个未使用字段。 讲述原理之前,先回顾一下 TCP Header 的格式:
0 1 2 3
可以看到, TCP Header 中是有一个叫 Options 的 Segment 的。 TOA 正是利用这个 Options 。Load Balancer 在接收到用户的请求后,会将用户的 IP 信息塞到 Options 里,其格式如下:
struct toa_data {
TOA 最大的优势在于,其并没有变更协议,不会有兼容性问题。比如 TCP Server 如果不支持 TOA 协议,它依旧可以正常工作,只是获取不到真实的用户 IP 信息。
TOA 也好, Proxy Protocol 也罢,他们的本质都是 Load Balancer 主动将用户 IP 信息传递给 TCP Server。因此, TOA 协议也是有 IP 伪造问题的。在和 TCP Server 建立连接的阶段,我们可以将伪造的 IP 地址塞到 Options 里。
写在最后
以上就是小黑屋总结的 IP 伪造技术。真实世界上,伪造来源 IP 的技术肯定不止这些, 小黑屋只是抛砖引玉。
另外这类 IP 伪造问题的根因都是相似的,即后端服务无条件地信任了别人传递过来的 IP 信息。 解决方式说起来也简单,即判断上一条 IP 是否是可信的,如果不在可信名单里,则停止解析这些 IP 信息。具体做法可阅读 Gin 框架的代码。