参加Pwn2Own 2021 Austin竞赛:巅峰时刻的伊卡洛斯
引言
2021年,我终于抽出时间研究使用了多年的消费级路由器。最初这只是个周末项目,想看看与平时不同的东西。此外,这也是个玩新工具、学新东西的好机会。
我下载了Ghidra,抓取固件更新,开始逆向运行在NETGEAR DGND3700v2设备上的各种MIPS二进制文件。很快我就对发现的东西感到震惊,并在周末写了Longue vue 🔭(也许下次再讲这个故事?)。安全性简直是个笑话,第二天我就扔掉了路由器,买了个新的。我简直不敢相信这东西在我的网络里待了好几年。呃😞。
总之,我最终收到了全新的TP-Link路由器,也开始研究它。很高兴看到代码质量好多了,我下班后慢慢啃代码。最终,在2021年5月,宣布了Pwn2Own 2021 Austin比赛,目标是路由器、打印机和手机。太兴奋了。参加这种比赛一直在我的待办清单上,但我一直说服自己没能力参加😅。
但这次不同。我决定投入时间专注于一个目标,看看会发生什么。没什么损失。此外,我的一些朋友也有兴趣和动力来破解代码,所以我们就这样做了。在这篇博客中,我将带您了解与mofoffensive团队一起准备和参加比赛的旅程。
目录
- 引言
- 目标选择
- 获取目标shell
- 枚举攻击面
- 追逐幽灵
- NetUSB的把戏
- 在QEMU中启动NetUSB
- Zenith登场
- 参加比赛
- 总结
目标选择
此时,@pwning_me、@chillbro4201和我在Discord上积极聊天。我们的最终目标是参加比赛,看了比赛规则后,阻力最小的路径似乎是瞄准路由器。我们对路由器更有经验,硬件容易且便宜,所以感觉是正确选择。
至少,我们当时认为这是阻力最小的路径。参加比赛后,也许打印机至少同样软,但奖金更高。但不管怎样,我们不是为了钱,所以专注于路由器类别并坚持下去。
在5个候选设备中,我们决定专注于消费设备,因为我们假设它们会更软。此外,我对TP-Link有点经验,组里有人熟悉NETGEAR路由器。所以我们就选了这两个目标,然后上亚马逊订购硬件开始。很兴奋。
TP-Link AC1750智能Wi-Fi路由器到了我家,我开始动手。但从哪里开始?嗯,这种情况下最好的办法是获取设备的root shell。怎么获取不重要,只要有一个就能找出有趣的攻击面。
如引言所述,在之前的几个月里玩自己的TP-Link路由器时,我发现了一个后认证漏洞,可以执行shell命令。虽然从攻击者角度看这没用,但对获取设备shell和启动研究有用。不幸的是,目标不脆弱,所以我需要另找办法。
哦,还有趣事:我最初订错了路由器。原来TP-Link卖两条看起来很像的产品线:A7和C7。我买了前者,但比赛需要后者,哎呀🤦🏽♂️。特别感谢Cody告诉我😅!
获取目标shell
逆向Web服务器几天后,找低挂果实没找到,我意识到需要另找办法获取设备shell。
谷歌了一下,我找到同胞写的文章:Pwn2own Tokyo 2020: Defeating the TP-Link AC1750 by @0xMitsurugi and @swapg。文章描述了他们在2020年Pwn2Own Tokyo上如何攻破路由器,但也描述了如何获取设备shell,太好了🙏🏽。问题是我真的没有任何硬件经验。一点都没有。
但幸运的是,我有很酷的朋友。我联系了@bsmtiam,他推荐订购FT232 USB线,我就订了。硬件很快到了,我去他家。他拆开路由器,放在工作台上开始干活。
试了几次后,他成功焊上了UART。我们把FT232 USB线接到路由器板子上,插到我的笔记本上:
用Python和minicom库,我们终于能进入交互式root shell💥:
太棒了。为庆祝这个小胜利,我们去当地酒吧吃了汉堡喝了啤酒🍻。好日子。
枚举攻击面
是时候找出该专注哪些区域了。我读了很多东西,因为这路由器多年来在Pwn2Own上被多次瞄准。我觉得尝试新领域可能好,降低重复参赛的几率,也最大化找到东西参赛的机会。在考虑重复之前,我需要一个漏洞。
我开始做一些非常基本的攻击面枚举:运行的进程、iptable规则、监听的套接字、crontab等。没什么花哨的。
|
|
第一眼,以下进程看起来有趣:
- uhttpd HTTP服务器,
- 第三方dnsmasq服务,可能未修补上游漏洞(不太可能?),
- tdpServer,2021年爆过,是sync-server漏洞的载体。
追逐幽灵
因为我熟悉家用路由器上uhttpd HTTP服务器的工作原理,我觉得至少花几天看看目标路由器上运行的。HTTP服务器能运行和调用Lua扩展,我觉得漏洞可能在那里:命令注入等。但有趣的是,所有现有的公共Lua工具都分析不了这些扩展,既沮丧又困惑。长话短说,路由器上用的Lua运行时似乎被修改了,操作码表看起来打乱了。结果,编译的扩展会破坏所有公共工具,因为操作码不匹配。傻。我最终反编译了一些扩展,找到一个漏洞,但从攻击者角度看可能没用。是时候继续了,因为我觉得那里潜力不够大。
我浪费时间的另一件事是浏览TP-Link为这路由器发布的GPL代码存档:ArcherC7V5.tar.bz2。因为许可,TP-Link必须(?)“维护”包含设备上使用的GPL代码的存档。我觉得这可能是弄清dnsmasq是否正确修补了近年发布的漏洞的好方法。看起来有些漏洞没修补,但反汇编显示不同😔。死胡同。
NetUSB的把戏
上面netstat输出中有两行奇怪的地方突出给我:
|
|
为什么这些套接字没有关联进程名呢🤔?嗯,原来谷歌和查看后,这些套接字是由……等等……内核模块打开的。听起来很疯狂,我也是第一次见。不过有点兴奋。
这个NetUSB.ko内核模块实际上是KCodes公司写的软件,用于USB over IP。另一件疯狂的事是我记得在NETGEAR路由器上见过同样的模块。奇怪。谷歌后,也不奇怪看到过去发现和利用过多个漏洞,TP-Link确实不是唯一搭载这模块的路由器。
虽然我觉得不太可能在那里找到有趣的东西,但还是投入时间查看和感受。静态逆向几天后,看起来确实比最初想的复杂得多,所以我决定再坚持久一点。
啃了一段时间后,事情开始合理:我逆向了一些重要结构,能跟踪不受信任的输入深入代码。枚举了很多解析和使用攻击者输入的地方后,我找到了这个点,可以在算术中溢出整数,喂给分配函数:
|
|
我最初以为这会导致严重溢出类漏洞,因为代码会尝试读大量字节到这个缓冲区,但我还是继续做了PoC。那时我意识到我错了。仔细看,SoftwareBus_fillBuf函数实际定义如下:
|
|
KTCP_get基本上是ks_recv的包装,这意味着攻击者可以强制函数返回而不读取整个BufferLen字节数。这意味着我可以强制分配小缓冲区,用任意多数据溢出它。如果您有兴趣了解如何首先触发这代码路径,请查看zenith-poc.py中的握手工作方式,或阅读@maxpl0it的CVE-2021-45608 | NetUSB RCE Flaw in Millions of End User Routers。以下代码可以触发上述漏洞:
|
|
另一个有趣的细节是分配函数是mallocPageBuf,我不熟悉。查看实现后,它最终调用_get_free_pages,是Linux内核的一部分。_get_free_pages分配2**n页数,使用称为二进制伙伴分配器的东西实现。我不熟悉这种分配器,最终有点着迷。您可以在Chapter 6: Physical Page Allocation中阅读更多。
哇好吧,所以也许我能用这漏洞做点有用的事。还是遥不可及,但根据我的理解,漏洞会给我对内容的完全控制,我能用任意多数据溢出页。唯一不能完全控制的是传给分配的大小。唯一限制是,由于整数溢出,我只能用以下区间的大小触发mallocPageBuf调用:[0, 8]。mallocPageBuf将对齐传递的大小到下一个2的幂,并计算顺序(n在2**n中)来调用_get_free_pages。
另一个对我有利的事是内核没有KASLR,我也注意到内核尽最大努力在遇到访问违规等时保持运行。它不会在第一次出问题时崩溃和重启,而是尝试运行直到不能再运行。甜。
我最终还发现驱动程序通过网络泄漏内核地址。在上面的片段中,kc_printf被调用诊断/调试字符串。查看代码,我意识到字符串实际上通过网络发送到不同端口。我觉得这对同步和泄漏驱动程序的一些分配也有帮助。
|
|
挺搞笑的吧?
在QEMU中启动NetUSB
虽然我有设备上的root shell,但无法调试内核或驱动程序代码。这使得甚至思考利用这漏洞都非常困难。此外,我是完全的Linux菜鸟,这种缺乏内省的方式行不通。我有什么选择?
嗯,如我之前提到的,TP-Link维护着一个GPL存档,其中有他们使用的Linux版本信息、应用的补丁,以及理论上构建内核所需的一切。我觉得他们非常友好,这应该给我一个好的起点,能够在QEMU下调试这驱动程序。我知道这不会给我最精确的模拟环境,但同时,会大大改善我当前的情况。我能够连接GDB,检查分配器状态,并希望取得进展。
结果这比我想的难得多。我首先尝试通过GPL存档构建内核。表面上,一切都在,简单的make应该就行。但不行。我花了