CVE-2019-14899:探测并劫持VPN隧道TCP连接

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29

最近我们发现了Linux、FreeBSD、OpenBSD、MacOS、iOS以及Android中的一个漏洞,恶意访问点(access point,ap)或者邻近用户可以利用该漏洞判断相连用户是否在使用VPN链路、推测其他用户正在访问的站点、确定正在使用的TCP序列号(Sequence Number)及确认号(Acknowledgment Number),最终成功将数据注入TCP流中。利用这些信息,攻击者可以劫持VPN隧道内的活动连接。
该漏洞适用于OpenVPN、WireGuard以及IKEv2/IPSec型VPN,我们还没有对tor进行完整测试,但由于tor工作在SOCKS层,涉及用户空间中的身份认证及加密,因此我们相信tor并没有受该漏洞影响。然而需要注意的是,该漏洞效果并不会因为用户具体使用的VPN技术而受到影响,即使来自用户的响应数据经过加密,我们还是可以根据数据包的大小及发送的数据包数量(比如challenge ACK)来判断正在通过加密VPN隧道发送的数据包类型。
我们已经在今年早些时候向Android报告了与该问题相关的一个漏洞,拿到的编号为CVE-2019-9461。在这个漏洞的CVE描述中,Android设备会响应通过无线接口发送给用户虚拟IP地址的未经请求的数据包,但这并没有解释清这种攻击的根本问题,因此Android的最新补丁并没有修改关于反向路径(reverse path)的相关设置。
在Ubuntu 19.10发行之前,这种攻击方法并不适用于我们测试过的Linux发行版,后面我们注意到受影响的系统中rp_filter被设置为“松散”(loose)模式。2018年11月28日,systemd仓库中sysctl.d/50-default.conf的默认设置从“严格”(strict)模式更改为“松散”模式。因此在此日期之后,如果发行版使用的是未修改配置的systemd,那么就会受该漏洞影响。我们测试的大多数Linux发行版使用的是其他init系统,对应的值为0,这也是Linux内核的默认值。
 
0x01 漏洞复现
在漏洞报告中,我们描述了在Linux系统上复现该漏洞的过程,也介绍了攻击过程在不同架构上的区别。
执行该攻击需要3个步骤:
1、探测VPN客户端的虚拟IP地址;
2、使用虚拟IP地址来推测活动的连接;
3、根据对未经请求数据包的加密响应来确定活动连接的序列号及确认号,以便劫持TCP会话。
为了复现攻击过程,我们需要使用4个组件:
1、受害者设备(连接到AP,地址为192.168.12.x、10.8.0.8);
2、AP(攻击者可控,地址为192.168.12.1);
3、VPN服务器(攻击者不可控,地址为10.8.0.1);
4、Web服务器(攻击者不可控,实际环境中的某个公网IP地址)。
受害者设备连接到某个AP,在测试环境中,该AP为运行create_ap的一台笔记本。随后,受害者设备与VPN服务商建立连接。
AP可以在整个虚拟IP空间中(OpenVPN的默认地址范围为10.8.0.0/24)向受害者发送SYN-ACK报文,从而判断受害者的虚拟IP地址。当SYN-ACK发送到受害者设备的正确虚拟IP时,设备会响应RST包;当SYN-ACK发送到错误的虚拟IP地址时,攻击者不会收到任何数据。
为了快速演示这种差别,我们可以在运行create_ap的AP设备上使用nping命令,其中源IP为AP网关,目的IP为分配给VPN客户端tun接口的虚拟IP地址。ap0为create_ap在攻击者设备上创建的接口,目的MAC地址为受害者无线网卡的MAC地址。
例如,如下命令可以让通过正确地址让受害者生成RST响应:
nping --tcp --flags SA --source-ip 192.168.12.1 --dest-ip 10.8.0.8 --rate 3 -c 3 -e ap0 --dest-mac 08:00:27:9c:53:12
而如下命令中的地址不正确,因此受害者不会返回响应包:
nping --tcp --flags SA --source-ip 192.168.12.1 --dest-ip 10.8.0.9 --rate 3 -c 3 -e ap0 --dest-mac 08:00:27:9c:53:12
与之类似,如果想测试受害者是否与特定网站(如64.106.46.56)存在活动连接,我们可以从64.106.46.56的80端口(或443端口)向受害者IP所使用的整个端口空间发送SYN或SYN-ACK报文。如果四元组正确,那么受害者每秒最多返回2个ACK响应;如果四元组不正确,受害者会针对收到的每个报文返回RST响应。
为了快速测试这种场景,我们可以在受害者设备上创建一个netcat连接,命令如下:
Netcat 64.106.46.56 80 -p 40404
生成challenge ACK的正确四元组如下所示:
nping --tcp --flags SA --source-ip 64.106.46.56 -g 80 --dest-ip 10.8.0.8 -p 40404 --rate 10 -c 10 -e ap0 --dest-mac 08:00:27:9c:53:12
如果使用如下不正确的四元组,则会对收到的每个报文生成一个RST:
nping --tcp --flags SA --source-ip 64.106.46.56 -g 80 --dest-ip 10.8.0.8 -p 40405 --rate 10 -c 10 -e ap0 --dest-mac 08:00:27:9c:53:12
最后,一旦攻击者检测到目标用户与外部服务器建立活跃TCP连接,就可以尝试推测下一个序列号以及确认号,以便将伪造报文注入当前连接。为了找到正确的序列号及ACK编号,我们将使用前面的方法,让客户端在加密连接中返回响应。攻击者不断在连接中注入伪造的重置报文,直到嗅探到ACK。攻击者可以分析与伪造报文对应的加密响应的大小及时间,正确推断出从客户端发往VPN服务端的数据包是否为challenge ACK。如果收到的重置报文中包含当前连接TCP窗口内的序列号,那么受害者的设备就会触发TCP challenge ACK。例如,如果客户端使用OpenVPN来与VPN服务端交换加密报文,那么当触发challenge ACK时,客户端将始终返回大小为79的一个SSL报文。
攻击者必须在整个序列号空间中伪造重置报文,直到其中某个报文触发加密的challenge ACK。伪造报文大小是非常关键的一个因素,会影响正确序列号的推断,但应该适当挑选,避免跳过客户端的接收窗口。在实际使用中,当攻击者认为已经嗅探到经过加密的challenge ACK时,可以伪造具有相同序列号的X个报文来验证这一点。如果触发了大小为79的X个加密响应,那么攻击者就能确定已正确触发challenge ACK(每秒最多2个大小为79的报文)。
当攻击者成功推测出客户端当前连接的窗口内序列号时,可以快速推测出注入攻击所需的正确序列号及窗口内ACK。首先,攻击者可以猜测窗口内ACK号,使用该编号来伪造空的push-ACK。一旦伪造的报文触发另一个challenge ACK,那么攻击者就找到了正确的窗口内ACK号。最后,攻击者使用该ACK号及序列号来继续伪造空的TCP数据报文,每次发送后都递减序列号。一旦攻击者伪造的序列号等于正确序列号的值减1,受害者就会响应另一个challenge ACK。现在攻击者可以使用推测出的ACK及下一个序列号,将任意payload注入当前加密连接中。

[1] [2]  下一页