OneShell

I fight for a brighter tomorrow

0%

CVE-2024-5035:TP-Link Archer C5400X认证前命令执行

漏洞描述

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的启动流程如下:

  1. 在自启动脚本/etc/init.d/wireless中,执行命令/sbin/wifi init
  2. 脚本/sbin/wifi中,导入脚本/lib/wifi/tplink_brcm.sh,然后执行导入的函数wifi_init
  3. 在导入脚本/lib/wifi/tplink_brcm.sh中,发生函数调用流程:wifi_init -> wifi_start -> wifi_start_calibrate -> wifi_start_rftest -> rftest
  4. 在最后的rftest函数中,启动程序:/usr/sbin/rftest
    undefined

此处分析程序rftest怎么触发到漏洞函数。在main函数中,依次调用函数FUN_00012a50FUN_00012b00FUN_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;
    /* 监听8888端口 */
    FUN_00012a50();
    /* 监听8889端口 */
    FUN_00012b00();
    /* 监听8890端口 */
    FUN_00012bb0();
    if (iVar1 == __stack_chk_guard) {
    return 0;
    }
    /* WARNING: Subroutine does not return */
    __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

/* WARNING: Restarted to delay deadcode elimination for space: ram */

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';
/* 端口小端存储,实际上是0x22b8=8888 */
local_16c.sin_port = 0xb822;
local_16c.sin_family = 2;
/* 创建socket */
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) {
......
/* 使用select处理连接 */
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");
/* WARNING: Subroutine does not return */
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

如下,成功启动,并开启了三个进程(线程)监听端口。
undefined

此时使用nc访问端口,输入以wl字符串开头,进行命令注入,成功触发命令执行。
undefined

小结

该漏洞从静态分析的角度思考,一般来说很难手工定位到、然后分析该程序,除非是在真实设备上查看端口、端口运行程序,或许能够定位到该程序并开展漏洞挖掘。