1.相关琐碎
上两节中使用的系统仿真方法相对来说比较简单,这一次选择使用一种更通用的固件服务仿真方法。
2.工具准备
IDA
下载地址链接: https://pan.baidu.com/s/12NG9mOtbV6WDk_Xz8T-HLA
提取码: 8cp4
Tenda固件
Tenda AC15下载地址:https://github.com/VulnTotal-Team/IoT-vulhub/tree/master/Tenda/CVE-2018-5767/firmware
binwalk
安装教程:https://blog.csdn.net/qq_50854790/article/details/123391951
binwalk是用于搜索给定二进制镜像文件以获取嵌入的文件和代码的工具。具体来说,binwalk是一个固件的分析工具,旨在协助研究人员对固件非分析,提取及逆向工程用处。简单易用,完全自动化脚本,并通过自定义签名,提取规则和插件模块,还重要一点的是可以轻松地扩展。
网上很多安装教程,安装时注意要成功安装.deps.sh 脚本中的binwalk依赖,否则提取出的固件会不完整。
qemu-arm-static
安装命令:apt install qemu-user-static
3.开始实验
使用binwalk提取固件中的文件系统
命令:
binwalk -Me US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin
在提取出的文件夹中,即可找到固件的文件系统
尝试开启固件网络服务
首先查询固件的系统架构,命令如下:
file ./bin/busybox
发现是32位,小端,arm架构,使用qemu-arm-static进行模拟Tenda路由器的网络服务一般是名称为/bin/httpd,开启命令如下:
sudo chroot ./ ./qemu-arm-static ./bin/httpd
发现服务开启失败,需要对httpd二进制程序进行逆向patch
动态调试并patch httpd服务
将httpd拉入ida32中进行分析,定位到sub_2CEA8函数即为main函数
在main函数中加入断点,在ubuntu系统中使用qemu-arm-static开启httpd服务,并-g 指定调试接口,命令如下:
sudo chroot ./ ./qemu-arm-static -g 23946 ./bin/httpd
使用ida远程链接,选中ida菜单栏中的Debuger选项,Select Debuger,选中remote GDB debugger
然后点击绿色运行按钮,填入ubuntu虚拟机的正确ip地址后,即可开启远程调试,按F9键程序开始运行,并断在刚才所打的断点处,F8单步步过,F7单步步入
分析后发现httpd服务开启有两个检测条件:
1.check_network()<=0时则会陷入sleep循环
2.ConnectCFM() 不符合条件则会提示"connect cfm failed"
使用ida的key_patch对这两处判断进行修改,可以进行绕过
修改成如下结果即可
修改完成之后ip依旧有问题
于是根据关键字符串“httpd listen ip",寻找问题源头,定位到sub_1A36C函数中
使用gdb 跟踪定位一下,开启调试端口
sudo chroot ./ ./qemu-arm-static -g 23946 ./bin/httpd_patch
然后gdb连接,按c直接运行到sub_1A36C函数
gdb-multiarch
set architecture arm
b *0x1A36C
target remote :23946
输入bt指令即可查看sub_1A36C函数调用链
定位到283e0附近的SUB_28338函数,分析得知sub_28030调用了此函数
重复上面的步骤,可以得到具体的调用链为:sub_2CEA8(main函数)-> sub_2D3F0(initWebs函数) -> sub_28030 -> sub_28338 -> sub_1A36C
接下来分析printf的ip参数v8进行跟踪:v8关联到s.sa_data[2],s.sa_data[2]关联到a1,a2
a1关联到g_lan_ip函数,最终回溯到主函数中
可以看到ip的值与s和v19有关
此处我们进行详细分析,根据函数名猜测getIfIp的作用的是获取ip地址,进入函数查看具体实现。
getIfIp为外部导入函数,对函数名进行搜索,查找存在的动态链接库
#查看可执行程序需要的动态链接库
readelf -d ./bin/httpd | grep NEEDED
#列出所有的函数名,与要寻找的函数对比
nm -D ./lib/libcommon.so
发现getlflp函数的本体存在于libcommon.so中
大致分析伪代码,可以看到一个系统调用ioctl(fd, 0x8915u, dest),查看这个系统调用所实现的功能
一般来讲ioctl在用户程序中的调用是:
ioctl(int fd,int command, (char*)argstruct)
ioctl调用与网络编程有关,文件描述符fd实际上是由socket()系统调用返回的。参数command的取值由/usr/include/linux/sockios.h 所规定。第三个参数是ifreq结构,在/usr/include/linux/if.h中定义。
参考:https://www.cnblogs.com/zxc2man/p/9511856.html
到头文件中查看,可以发现,第二个参数实现的功能正是获取IP地址
第三个参数的含义需要进一步分析,先看函数整体的流程,应该就是成功获取ip地址返回v2,v2的值为0的话,在main函数中的判断就不会进入if循环,而ip地址的值则由v19决定。进一步跟进v19,就是在getIfIp函数中的a2,由系统调用获取ip地址后赋给a2即main函数中的v19。那么想让函数按我们分析的执行,还需要分析第三个参数的含义。第三个参数与main函数的v7有关,进一步分析,与getLanIfName函数有关,依照上面的步骤发现getLanIfName函数依然存在于libcommon.so中,查看函数的本体。
getLanlfName函数进一步关联到get_eth_name函数,且参数写死为0。依照上面的步骤发现get_eth_name函数依然存在于libChipApi.son中,查看函数的本体,函数返回v1,即网卡的名称,上述系统调用的第三个参数也就清楚了。
至此,我们可以梳理一下整个流程。在main函数中,首先调用getLanIfName函数进而调用get_eth_name函数获取网卡名称。然后将网卡名称作为参数输入到getIfIp中,函数功能为寻找网卡名称为br0的ip地址并传递给V17。
所以,想让二进制程序监听正确的ip地址需要新建一个名为br0的网卡。
sudo brctl addbr br0
sudo ifconfig br0 192.168.20.12/24
重新启动,找到了名为br0的网卡并获取了ip地址:
4.开启服务
将webroot_ro中的文件复制到web_root目录下,最后就可以在本机中访问模拟出的路由器网站