tenda_2018-16333栈溢出漏洞复现
这是真正意义上第一次复现IOT漏洞,也是参考了许多大佬的博客,算是“站在巨人的肩膀上”进行入门学习了。
固件获取
固件地址:pwn/IOT/Tenda_CVE-2018-16333 at master · Snowleopard-bin/pwn (github.com)
这里有poc和固件。
漏洞分析
前期处理
首先是使用binwalk提取固件。
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin --run-as=root |
根据官方解释进入squashfs-root/bin文件夹下找到了漏洞程序httpd。

这也和做pwn一样,首先要知道漏洞程序才能做接下来的分析,只不过pwn题是把漏洞程序直接给我们了。在这里就需要我们自己去找了,目前我还不知道如何定位这样的程序,等以后深入了解慢慢积累吧 : )
然后我们看看程序开启了什么保护机制:这里是只开了NX保护,并且程序是arm架构下的32位程序。
❯ checksec httpd |
定位漏洞函数
根据CVE的描述,There is a buffer overflow vulnerability in the router’s web server. While processing the ssid parameter for a POST request, the value is directly used in a sprintf call to a local variable placed on the stack, which overrides the return address of the function, causing a buffer overflow.漏洞的原因是web服务在处理post请求时,对ssid参数直接复制到栈上的一个局部变量中导致栈溢出。根据ssid字符串定位到form_fast_setting_wifi_set函数。
使用IDA字符串功能找ssid字符串可能并不准,我们可以使用以下技巧:
ALT + T 搜索字符串 |
分析
有一定pwn基础的同学应该能够比较敏锐的发现,这里strcpy没有做检查就复制给dest,会造成栈溢出。(这也是为什么听师傅说这一类函数容易出现漏洞)

但是我们需要注意,我们通过第一个strcpy溢出s造成栈溢出,那么必然会覆盖了src指针;如果这里没有注意到,假设src被覆盖为aaaa,那么在第二次strcpy时取src指针地址内容就会报错。所以为了保证地址合法,我们将src覆盖为一个可读地址。(并且为了避免截断,不要包含\x00)
可以选择如下地址:

利用思路
寻找gadget:libc.so.0文件存在squashfs-root/lib目录下
ROPgadget --binary ./libc.so.0 --only "pop" |grep r3 |
BLX:跳转到寄存区reg给出的目的地址处并将返回地址存储到LR(连接寄存器,也即R14),类似与x86的call。
过程:
- 栈溢出跳到
gadget1,控制r3寄存器为system地址,然后控制pc为gadget2地址 - 接着程序会跳转到
gadget2,我们需要控制r0寄存器为要执行的指令cmd - 执行
system(cmd)
qemu用户级调试
先安装qemu。
apt install qemu |
回到squashfs-root目录下:
cp $(which qemu-arm-static) . |
启动httpd后发现程序卡住:

在IDA中使用字符串搜索和交叉引用定位到函数位置:在这里会进行网络check,如果返回值小于0就会sleep,接着再次进入网络check,陷入死循环。

所以我们将MOV R3,R0patch为MOV R3,#1,这样就能跳出循环了。

进修改后的httpd文件覆盖原文件,再次运行:

按照同样的方法定位错误:

patch后:将MOV R3,R0patch为MOV R3,#1

再次运行:发现监听的ip地址不对

尝试在本机建立虚拟网桥br0并重新执行程序:
安装配置网络的工具 |
可以看到IP地址正确了。

注意:这里使用用户级模拟的程序会导致复现不了漏洞,所用我们使用以下的qemu系统级模拟
qemu系统级模拟
宿主机 |
下载的依赖:
wget https://people.debian.org/~aurel32/qemu/armhf/debian_wheezy_armhf_standard.qcow2 |
启动:
sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress -initrd initrd.img-3.2.0-4-vexpress \ |
输入密码账号均为:root
启动之后,eth0网卡没有分配ip地址,并且没有像参考文章中所说那样就可以与宿主机通信(也就是ping不通),这时需要再qemu虚拟机中给eth0网卡设置ip地址。然后就能愉快地使用scp上传文件系统了。
注意,这里必须要关闭宿主机的ens32或者ens33网卡,sudo ifconfig ens32 down,这样qemu虚拟机才能ping通宿主机的br0网卡。
在前面网络通信卡了好久(呜呜呜
测试一下宿主机和qemu虚拟机是否能ping通:

测试通过,接下来实现将宿主机中的squashfs-root目录传到qemu虚拟机中,我们使用scp服务。
在宿主机中压缩整个文件目录 |
在qemu虚拟机中,下载文件到/root目录下 |

解压目录并删除压缩包:
tar xzf squashfs-root.tar.gz && rm squashfs-root.tar.gz |
已经解压出文件目录了:

挂载文件目录:
mount -o bind /dev /root/dev && mount -t proc /proc /root/proc |
切换文件工作目录并打开sh终端,这时程序会将/root目录当做根目录:
chroot /root sh |

为qemu虚拟机添加虚拟网卡并激活:
brctl addif br0 |

终于到了这里,最后一步就是启动程序了:
./bin/httpd/ & |
qemu虚拟机退出方法:同时按ctrl+a之后,再按c,出现(qemu)时输入q即可退出。

接下来就是宿主机远程调试qemu中运行的httpd了。
去github中下载静态编译好的gdbserver:
用scp将gdbserver下载到虚拟机。
使用gdbserver启动httpd程序,设置端口为9999:
./gdbserver 0.0.0.0:9999 ./bin/httpd |
在宿主机中,使用gdb-multiarch来进行远程调试:
gdb-multiarch -q ./bin/httpd |
关闭地址随机化:
echo 0 > /proc/sys/kernel/randomize_va_space |
poc
import requests |



