漏洞描述
CVE-2024-5053是发生在三频游戏路由器TP-Link Archer C4500X,固件版本1_1.1.6及之前的认证前命令执行漏洞,漏洞发生在程序rftest中。该程序是和射频测试相关的,用于帮助AP进行无线接口自评价,程序会监听在TCP端口8888、8889、8890上。未授权的攻击者可以向这些端口发送构造的请求包,进行命令注入。
存在漏洞的固件和更新后的固件可以在此处下载:Download for Archer C5400X | TP-Link
漏洞分析
首先分析存在漏洞的程序rftest
是如何启动的,然后分析rftest
是怎么从监听端口获取数据,并进行处理,直到命令注入点。
rftest
的启动流程如下:
- 在自启动脚本
/etc/init.d/wireless
中,执行命令/sbin/wifi init
- 脚本
/sbin/wifi
中,导入脚本/lib/wifi/tplink_brcm.sh
,然后执行导入的函数wifi_init
- 在导入脚本
/lib/wifi/tplink_brcm.sh
中,发生函数调用流程:wifi_init
-> wifi_start
-> wifi_start_calibrate
-> wifi_start_rftest
-> rftest
- 在最后的
rftest
函数中,启动程序:/usr/sbin/rftest
此处分析程序rftest
怎么触发到漏洞函数。在main
函数中,依次调用函数FUN_00012a50
、FUN_00012b00
、FUN_00012bb0
分别创建并监听在TCP端口8888、8889、8890端口,简单小结如下:
main
函数:分别调用三个函数在各自端口监听
- 以函数
FUN_00012a50
为例:fork创建子进程
- 子进程中执行函数
FUN_0001218c
:创建tcp连接,监听,处理请求连接,并使用fork创建子进程处理连接数据
- 处理数据的子进程执行函数
FUN_00011538
:校验数据,构造参数,形成命令到函数popen
执行。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| undefined4 main(void)
{ int iVar1; iVar1 = __stack_chk_guard; FUN_00012a50(); FUN_00012b00(); FUN_00012bb0(); if (iVar1 == __stack_chk_guard) { return 0; } __stack_chk_fail(); }
|
三个函数的代码类似,都是在函数中使用fork创建子进程,然后在子进程中监听端口,以函数FUN_00012a50为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void FUN_00012a50(void) { ...... pid = fork(); if (pid < 0) { ...... } } else { if (pid == 0) { prctl(0xf,"rftest",0,0,0); FUN_0001218c(); exit(0); } ...... } ...... }
|
子进程中实现了一个基本的TCP服务器功能,包括:创建socket、绑定端口、监听端口、接受连接、使用多进程处理客户端请求,其中需要注意的就是在绑定的网络端口是以小端的形式存储的,实际上的端口应该是0x22b8=8888。
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
void FUN_0001218c(void)
{ ...... sockaddr_in local_16c;
local_2c = __stack_chk_guard; local_16c.sin_addr.s_addr = 0; local_16c.sin_zero[0] = '\0'; local_16c.sin_zero[1] = '\0'; local_16c.sin_zero[2] = '\0'; local_16c.sin_zero[3] = '\0'; local_16c.sin_zero[4] = '\0'; local_16c.sin_zero[5] = '\0'; local_16c.sin_zero[6] = '\0'; local_16c.sin_zero[7] = '\0'; local_16c.sin_port = 0xb822; local_16c.sin_family = 2; sock_fd = socket(2,1,0); if ((int)sock_fd < 0) { perror("create socket error!\n"); puts("create socket error!"); } else { puts("rftest socket create success!"); iVar3 = bind(sock_fd,(sockaddr *)&local_16c,0x10); if (iVar3 < 0) { printf("socket bind error! bind return value:%d \n"); } else { iVar3 = listen(sock_fd,5); if (-1 < iVar3) { ...... iVar4 = select(sock_fd + 1,__readfds,(fd_set *)0x0,(fd_set *)0x0,(timeval *)0x0); if ((uVar1 & *(uint *)(local_15c + iVar3 * 4 + 0x30)) != 0) goto code_r0x000122e0; goto LAB_00012350; } printf("socket listen error! listen return value:%d \n"); } } ...... local_170 = 0x10; iVar9 = 0; DAT_00026858 = accept(sock_fd,(sockaddr *)&local_16c,&local_170); piVar8 = (int *)local_15c; while (-1 < *piVar8) { iVar9 = iVar9 + 1; piVar8 = piVar8 + 1; if (iVar9 == 0xc) { perror("too many clients"); printf("too many clients"); exit(-1); } } *(int *)(local_15c + iVar9 * 4) = DAT_00026858; if (0 < iVar4) { LAB_00012350: __pid = fork(); if (__pid == 0) { close(sock_fd); FUN_00011538(DAT_00026858); goto LAB_00012474; } ...... } goto LAB_0001229c; }
|
进一步深入到多进程执行的漏洞函数FUN_00011538中,对传入的数据进行了相关的处理后,送入到命令执行函数popen函数中执行。数据处理的流程比较繁琐,大概就是构造执行命令的参数。此处就可以使用命令注入字符,例如;
,直接进行命令执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| iVar6 = strncmp((char *)pbVar16,"wl",2); ...... iVar6 = strncmp((char *)pbVar17,"nvram",5); if (iVar6 == 0) { pcVar9 = strstr((char *)(pbVar17 + 5),"get"); if (pcVar9 == (char *)0x0) { FUN_00011138(local_102c); } else { pFVar10 = popen((char *)local_102c,"r"); memset(acStack_2158,0,100); if (pFVar10 == (FILE *)0x0) { *__s = 0x2023; (&DAT_0002385a)[iVar1] = 0; }
|
漏洞复现
该漏洞可以使用qemu的用户级仿真搭建即可,为了更好看到程序执行的log打印,以及rftest
调用其他程序,采用将qemu-arm-static
复制到根目录,然后chroot运行的方式。
1
| sudo chroot . ./qemu-arm-static ./bin/sh
|
如下,成功启动,并开启了三个进程(线程)监听端口。
此时使用nc
访问端口,输入以wl
字符串开头,进行命令注入,成功触发命令执行。
小结
该漏洞从静态分析的角度思考,一般来说很难手工定位到、然后分析该程序,除非是在真实设备上查看端口、端口运行程序,或许能够定位到该程序并开展漏洞挖掘。