参考
https://www.zhiwanyuzhou.com/index.php/2022/09/17/%e4%bb%8ecve%e5%88%b0draytek-vigor%e7%b3%bb%e5%88%97%e4%bc%81%e4%b8%9a%e8%b7%af%e7%94%b1%e5%99%a8%e5%9b%ba%e4%bb%b6%e5%88%86%e6%9e%90/Vigor 3910分析与利用.pdf
漏洞信息
https://nvd.nist.gov/vuln/detail/CVE-2022-32548Trellix 公司的安全研究员发现一个严重的远程代码执行 (RCE)漏洞 (CVE-2022-32548),影响29款 DrayTek Vigor 系列企业路由器易受攻击的机型如下:
-
Vigor3910
-
Vigor1000B
-
Vigor2962 系列
-
Vigor2927 系列
-
Vigor2927 LTE 系列
-
Vigor2915 系列
-
Vigor2952 / 2952P
-
Vigor3220 系列
-
Vigor2926 系列
-
Vigor2926 LTE 系列
-
Vigor2862 系列
-
Vigor2862 LTE 系列
-
Vigor2620 LTE 系列
-
VigorLTE 200n
-
Vigor2133 系列
-
Vigor2762 系列
-
Vigor167
-
Vigor130
-
VigorNIC 132
-
Vigor165
-
Vigor166
-
Vigor2135 系列
-
Vigor2765 系列
-
Vigor2766 系列
-
Vigor2832
-
Vigor2865 系列
-
Vigor2865 LTE 系列
-
Vigor2866 系列
-
Vigor2866 LTE 系列
固件
https://www.draytek.co.uk/support/downloads/vigor-3910/older-firmware/firmware-4311
vigor-3910发行说明:https://www.draytek.co.uk/support/downloads/vigor-3910/older-firmware/firmware-4311/send/1143-4311/2592-readme-v3910-4311
什么是.all文件
固件分析
用010 Editor打开v3910_4311.all
本人的010 Editor经过ida pro破解了
binwalk解包
binwalk -Me v3910_4311.all
使用binwalk进行初步分析,发现无法解包;发现enc_image,初步认为该固件是加密的
既然固件加密,可查看旧版本固件是否有不加密的固件进行“过度”操作
旧版本固件分析
递归提取文件:binwalk -Me v3910_3972.all
$ binwalk -Me v3910_3972.all
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
273054 0x42A9E Unix path: /home/eason_jhan/1000b/cavium/firmware/bdk/libbdk-os/bdk-rlock.c
283704 0x45438 SHA256 hash constants, little endian
469662 0x72A9E Unix path: /home/eason_jhan/1000b/cavium/firmware/bdk/libbdk-os/bdk-rlock.c
480312 0x75438 SHA256 hash constants, little endian
573012 0x8BE54 SHA256 hash constants, little endian
835156 0xCBE54 SHA256 hash constants, little endian
2032941 0x1F052D Neighborly text, "neighbor %d too different %d from average %d, picking %d.LMC%d.R%d: MAJORTY: Byte %d: picking majority of %d over average %d."
2079947 0x1FBCCB Unix path: /home/eason_jhan/1000b/cavium/firmware/bdk/libbdk-os/bdk-rlock.c
2096368 0x1FFCF0 SHA256 hash constants, little endian
2119768 0x205858 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 2285442 bytes
2886232 0x2C0A58 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 311880 bytes
3000920 0x2DCA58 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 630943 bytes
3155032 0x302458 device tree image (dtb)
3160664 0x303A58 device tree image (dtb)
3165272 0x304C58 device tree image (dtb)
3165784 0x304E58 device tree image (dtb)
3172440 0x306858 device tree image (dtb)
3180632 0x308858 device tree image (dtb)
3184728 0x309858 device tree image (dtb)
3185752 0x309C58 device tree image (dtb)
3191896 0x30B458 device tree image (dtb)
3192408 0x30B658 device tree image (dtb)
3199576 0x30D258 device tree image (dtb)
3203160 0x30E058 device tree image (dtb)
3207256 0x30F058 device tree image (dtb)
3214424 0x310C58 device tree image (dtb)
3219544 0x312058 device tree image (dtb)
3228760 0x314458 device tree image (dtb)
3233368 0x315658 device tree image (dtb)
3239000 0x316C58 device tree image (dtb)
3243096 0x317C58 device tree image (dtb)
3243608 0x317E58 device tree image (dtb)
3247704 0x318E58 device tree image (dtb)
3253336 0x31A458 device tree image (dtb)
3257432 0x31B458 device tree image (dtb)
3258456 0x31B858 device tree image (dtb)
3265112 0x31D258 device tree image (dtb)
3272280 0x31EE58 device tree image (dtb)
3276376 0x31FE58 device tree image (dtb)
3277400 0x320258 device tree image (dtb)
3284056 0x321C58 device tree image (dtb)
3287640 0x322A58 device tree image (dtb)
3288152 0x322C58 device tree image (dtb)
3295320 0x324858 device tree image (dtb)
3295832 0x324A58 device tree image (dtb)
3302488 0x326458 device tree image (dtb)
3306072 0x327258 device tree image (dtb)
3330136 0x32D058 device tree image (dtb)
3346008 0x330E58 device tree image (dtb)
3361880 0x334C58 device tree image (dtb)
3377752 0x338A58 device tree image (dtb)
3400792 0x33E458 device tree image (dtb)
3425368 0x344458 device tree image (dtb)
3442264 0x348658 device tree image (dtb)
3463768 0x34DA58 device tree image (dtb)
3478616 0x351458 device tree image (dtb)
3493976 0x355058 device tree image (dtb)
3509848 0x358E58 device tree image (dtb)
3526232 0x35CE58 device tree image (dtb)
3541592 0x360A58 device tree image (dtb)
3557526 0x364896 Copyright string: "Copyright (C) 2016, Cavium Inc."
3559079 0x364EA7 Copyright string: "copyright notice and this permission notice shall be"
3561562 0x36585A Unix path: /sys/class/gpio/gpio472) for DSL model
5052256 0x4D1760 CRC32 polynomial table, little endian
5099137 0x4DCE81 Motorola S-Record; binary data in text format, record type: header
5259312 0x504030 Microsoft executable, portable (PE)
5499456 0x53EA40 SHA256 hash constants, little endian
12693552 0xC1B030 ELF, 64-bit LSB shared object, version 1 (SYSV)
12741544 0xC26BA8 gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
12820184 0xC39ED8 Intel x86 or x64 microcode, sig 0xffffff80, pf_mask 0x00, 1DE0-08-26, size 2048
12820328 0xC39F68 Intel x86 or x64 microcode, sig 0xffffff80, pf_mask 0x00, 1DE0-08-26, size 2048
12923200 0xC53140 LZO compressed data
12925744 0xC53B30 CRC32 polynomial table, little endian
13188944 0xC93F50 Copyright string: "Copyright (c) 1999-2006 Intel Corporation."
13196536 0xC95CF8 Copyright string: "Copyright (c) 2009 - 2012 Intel Corporation."
13197320 0xC96008 Copyright string: "Copyright (c) 1999-2008 Intel Corporation."
13199736 0xC96978 Copyright string: "Copyright (c) 2013 - 2016 Intel Corporation."
13410380 0xCCA04C Certificate in DER format (x509 v3), header length: 4, sequence length: 14848
14404968 0xDBCD68 Unix path: /dev/vc/0
14479296 0xDCEFC0 Ubiquiti partition header, header size: 56 bytes, name: "PARTNAME=%s", base address: 0x74790A00, data size: 23295090 bytes
14486472 0xDD0BC8 xz compressed data
14569424 0xDE4FD0 Unix path: /lib/firmware/updates/4.9.0-OCTEONTX_SDK_6_2_0_p3_build_38
14712280 0xE07DD8 Copyright string: "Copyright(c) 1999-2006 Intel Corporation"
14789199 0xE1AA4F Copyright string: "Copyright 2005-2007 Rodolfo Giometti <[email protected]>"
14812265 0xE20469 Copyright string: "Copyright(c) Pierre Ossman"
14848048 0xE29030 Unix path: /sys/firmware/devicetree/base
14848848 0xE29350 Unix path: /sys/firmware/fdt': CRC check failed
14864129 0xE2CF01 Neighborly text, "neighbor table overflow!ate is %x"
15613000 0xEE3C48 LZ4 compressed data, legacy
15613426 0xEE3DF2 Executable script, shebang: "/bin/sh"
16971299 0x102F623 Unix path: /dev/net/tun.
17236166 0x10700C6 mcrypt 2.5 encrypted data, algorithm: "w", keysize: 332 bytes, mode: """,
19705376 0x12CAE20 XML document, version: "1.0"
20045201 0x131DD91 VMware4 disk image
20045848 0x131E018 Executable script, shebang: "/bin/sh"
20111190 0x132DF56 Base64 standard index table
21756040 0x14BF888 Unix path: /sys/class/net/%s/phy80211
22373111 0x15562F7 Base64 standard index table
22816893 0x15C287D MPEG transport stream data
23678637 0x1694EAD Copyright string: "Copyright 1995-2005 Jean-loup Gailly "
24029059 0x16EA783 Unix path: /dev/net/tun
24533651 0x1765A93 eCos RTOS string reference: "ecosW"
24543379 0x1768093 HTML document header
24563221 0x176CE15 LUKS_MAGIC
24564177 0x176D1D1 xz compressed data
24873394 0x17B89B2 OpenSSL encryption, salted, salt: 0xC00770252D323573
24884301 0x17BB44D HTML document header
24906969 0x17C0CD9 Private key in DER format (PKCS header length: 4, sequence length: 2345
25343574 0x182B656 Unix path: /usr/local/shk
25480049 0x184CB71 Executable script, shebang: "/bin/bash"
26449086 0x19394BE SHA256 hash constants, little endian
26965826 0x19B7742 Cisco IOS microcode, for ""
27792979 0x1A81653 gzip compressed data, maximum compression, has header CRC, last modified: 1974-10-07 02:33:45 (bogus date)
30516402 0x1D1A4B2 HTML document header
30707709 0x1D48FFD Base64 standard index table
32138317 0x1EA644D HTML document header
32463276 0x1EF59AC PNG image, 32 x 24, 8-bit/color RGBA, interlaced
32463352 0x1EF59F8 Zlib compressed data, default compression
32492091 0x1EFCA3B GIF image data, version "89a", 5 x
32548082 0x1F0A4F2 JPEG image data, EXIF standard
32574798 0x1F10D4E Zlib compressed data, best compression
32577456 0x1F117B0 Zlib compressed data, best compression
32580729 0x1F12479 JPEG image data, JFIF standard 1.02, thumbnail 11x98
32751022 0x1F3BDAE Zlib compressed data, best compression
32760485 0x1F3E2A5 Zlib compressed data, default compression
32767499 0x1F3FE0B JPEG image data, JFIF standard 1.02
32822738 0x1F4D5D2 Zlib compressed data, best compression
32831773 0x1F4F91D XML document, version: "1.0"
32866201 0x1F57F99 JPEG image data, JFIF standard 1.02
32905743 0x1F61A0F GIF image data, version "89a", 30 x 30
32943989 0x1F6AF75 Zlib compressed data, best compression
32947100 0x1F6BB9C Zlib compressed data, best compression
32954824 0x1F6D9C8 XML document, version: "1.0"
32972713 0x1F71FA9 JPEG image data, JFIF standard 1.01
33011108 0x1F7B5A4 PNG image, 52 x 5, 8-bit/color RGBA, non-interlaced
33201563 0x1FA9D9B XML document, version: "1.0"
33236782 0x1FB272E PNG image, 1000 x 280, 8-bit/color RGBA, interlaced
33318819 0x1FC67A3 Zlib compressed data, best compression
33382838 0x1FD61B6 JPEG image data, JFIF standard 1.01
33382868 0x1FD61D4 TIFF image data, little-endian offset of first image directory: 8
33516114 0x1FF6A52 ZBOOT firmware header, header size: 32 bytes, load address: 0x9C54505A, start address: 0x8B844857, checksum: 0x4F8B885F, version: 0xFF979388, image size: 991901497 bytes
33609975 0x200D8F7 PNG image, 300 x 84, 8-bit/color RGBA, non-interlaced
33776158 0x203621E XML document, version: "1.0"
33809104 0x203E2D0 PNG image, 310 x 531, 8-bit/color RGBA, non-interlaced
33809203 0x203E333 Zlib compressed data, best compression
34312241 0x20B9031 JPEG image data, JFIF standard 1.01
34352652 0x20C2E0C TIFF image data, big-endian, offset of first image directory: 8
34799229 0x212FE7D Base64 standard index table
35282151 0x21A5CE7 HTML document header
35375557 0x21BC9C5 Base64 standard index table
38441243 0x24A911B Certificate in DER format (x509 v3), header length: 4, sequence length: 873
38694241 0x24E6D61 Executable script, shebang: "/bin/bash"
38694568 0x24E6EA8 Unix path: /dev/net/tun
39750489 0x25E8B59 Unix path: /usr/lib64/tc/
39881890 0x2608CA2 Copyright string: "Copyright (C) 2004 by Harald Welte <[email protected]>"
40546844 0x26AB21C Unix path: /home/ruby/X
40667261 0x26C887D Copyright string: "Copyright (C) 2018, Thomas G. Lane, Guido Vollbeding"
40692035 0x26CE943 Unix path: /home/ruby/X
42108450 0x2828622 Copyright string: "Copyright (C) 2018, Thomas G. Lane, Guido Vollbeding"
42240067 0x2848843 Copyright string: "Copyright (C) 2018, Thomas G. Lane, Guido Vollbeding"
42600735 0x28A091F gzip compressed data, ASCII, from VM/CMS, last modified: 1995-08-24 06:41:07
42608185 0x28A2639 ELF, 64-bit LSB processor-specific,
42845890 0x28DC6C2 Neighborly text, "neighbor C %s"
43871781 0x29D6E25 Unix path: /home/ruby/X
44237701 0x2A30385 Executable script, shebang: "/bin/sh"
44249877 0x2A33315 OpenSSH RSA public key
45239782 0x2B24DE6 SHA256 hash constants, little endian
45601353 0x2B7D249 gzip compressed data, ASCII, from VM/CMS, last modified: 2008-04-20 10:46:28
47200461 0x2D038CD SHA256 hash constants, little endian
通过binwalk的分析结果可以知道,这个固件应该不是加密固件,但是binwalk并不支持它的解包操作,如此我们先大概回顾一下固件的基本结构:通过以上固件的基本结构,结合binwalk的信息,我们可以这样假设:
偏移 | 功能 | 备注 |
---|---|---|
0-0x205857 | BootLoader | 先大概确定内核,再往上推演 |
0x205858-0xEE3DF1 | Kernel | 因为有LZMA的标识,可以大概推断为内核,并且这区间有dtb设备树、PE结构等标识 |
0xEE3DF2-末尾 | Rootfs | 从这里开始有Executable script的标识,表示存在实际的shell脚本文件内容了,以及后面有图片、网页的标识,可以断定为文件系统 |
截取0xEE3DF2的部分内容,可以看出此的确为shell脚本的内容,但是脚本里面充斥着很多非ASCII的内容,经过经验分析,此脚本可能被压缩了。再来分析binwalk的解包结果,在0xC26BA8处有个gzip的标识,经过解压缩,可以知道其为内核的配置信息:知道内核决定了支持的文件系统的类型,那么其配置信息内部可能会定义文件系统的相关配置,如此我们搜索rootfs关键字,最终找到了initramfs.cpio.lz4的字样:后缀名lz4,可以联想到假定的rootfs区段内shell脚本之前有个LZ4压缩数据,那么可以断定从0xEE3C48应该为文件系统的起始位置。
15613000 0xEE3C48 LZ4 compressed data, legacy
15613426 0xEE3DF2 Executable script, shebang: "/bin/sh"
现在知道了,binwalk可能对lz4压缩不太支持,那么我们手动对binwalk进行功能添加:
# Ubuntu系统 添加lz4压缩工具
apt install liblz4-tool
# 加入lz4的压缩支持(binwalk安装路径/proc/thread-self/cwd/tools/binwalk-2.3.3/src/binwalk/config/extract.conf,如果找不到用find命令查找)
^lz4 compressed data:lz4:lz4 -d '%e' '%e.bin'
使用binwalk -Me解包固件,获取文件系统
新版本固件分析
旧版固件是未加密的,新版固件是加密的,那么未加密->加密的过程会有个“过渡”操作,通过关键字“firmware”对整个文件系统进行查找,最终找到一个文件fw_upload里面有对旧版和新版固件的操作:通过分析可知固件的结构:
序号 | 偏移 | 功能 |
---|---|---|
1 | 0x0-0x03 | 标识,字符串6216 |
2 | 0x04-0x07 | 填充,值0 |
3 | 0x08-0x0B | header的checksum校验值,值0x3EFB072A |
4 | 0x0C | 值1 |
5 | 0x0D-0x3F | 固件版本号,字符串4.3.2_RC5a |
6 | 0x40-0x43 | 长度,值0x5 |
7 | 0x44-0x48 | 型号,字符串v3910 |
8 | 0x49-0x4C | 填充1,值0 |
9 | 0x4D-0x50 | 未知,值0x00000059 |
10 | 0x51-0x54 | nonce内容的checksum校验值,值0x6B075354 |
11 | 0x55-0x88 | 填充2,值0 |
12 | 0x89-0x8C | 字符串nonce的长度,值0x5 |
13 | 0x8D-0x92 | 字符串nonce |
14 | 0x93-0x95 | nonce内容的长度,值0xC |
15 | 0x96-0xA1 | nonce内容,字符串xRDYwRMx0B7u |
16 | 0xA2-0xA5 | 填充1,值0 |
17 | 0xA6-0xA9 | 未知,值0x02FD1A51 |
18 | 0xAA-0xAD | enc_Image内容的checksum校验值,值0x26B0401D |
19 | 0xAE-0xE1 | 填充2,值0 |
20 | 0xE2-0xE5 | 字符串enc_Image的长度,值0x9 |
21 | 0xE6-0xEE | 字符串enc_Image |
22 | 0xEF-0xF2 | enc_Image内容的长度,值0x02FD1A00 |
23 | 0xF3-0x2FD1AF2 | enc_Image内容 |
… | … | 以下内容以此类推,包含以下区块: |
enc_thunder-bootfs-uboot-t81.img | ||
fw_release | ||
fw_ver | ||
pid | ||
oid | ||
uver | ||
bdk_ver | ||
linux_ver | ||
drayqemu_ver | ||
fw_release | ||
fw_ver | ||
pid | ||
oid | ||
uver | ||
bdk_ver | ||
fw_release | ||
fw_ver | ||
pid | ||
oid | ||
uver | ||
bdk_ver |
每个区块间隔0x40字节,即填充1到填充2之间的内容。checksum校验算法:
def checksum(data, num):
length = len(data)
num = (~num) & 0xFFFFFFFF
if not num:
num = 0xFFFFFFFF
tmp1 = 0
for i in range(length):
tmp1 = data[i]
for j in range(8):
tmp2 = tmp1 ^ num
num >>= 1
if (tmp2 & 1):
num ^= 0xEDB88320
tmp1 >>= 1
return (~num) & 0xFFFFFFFF
需要关心的就是enc_Image区段,它是chacha20加密的,那么找到key,nonce就能解密了
解密
采用了chacha20算法加密了固件,需要两个值进行解密,一个是secret、一个是noncenonce为10 78 DE AA 0D 6A F8 F5 A6 DE EF 98,其次也发现了enc_image,从0xF3开始进⾏解密,接下去就是需要寻找⼀下secret值以完成第⼀步的解密
通过解密之后的固件提取文件系统中⼀个名为fw_unpacker的bin⽂件中寻找我们需要的内容:①字符串查找secret值:②需要复制我们需要的nonce到⼀个单独⽂件中:③这⾥需要针对enc_image进⾏操作,所以需要进⾏分割,将0xF3之前的全部删除,然后跑:第⼀次解密的exp:
import sys
from Crypto.Cipher import ChaCha20
def usage():
print("{} path to file path to nonce".format(sys.argv[0]))
def go():
if len(sys.argv) < 2:
return usage()
with open(sys.argv[1],"rb") as f:
data = f.read()
with open(sys.argv[2],"rb") as f:
msg_nonce = f.read()
if len(msg_nonce) != 0xc:
print("Invalid nonce len")
return
secret = b"0DraytekKd5Eason3DraytekKd5Eason"
cipher = ChaCha20.new(key=secret,nonce=msg_nonce)
plaintext = cipher.decrypt(data)
with open(sys.argv[1] + "_decrypt","wb") as f:
f.write(plaintext)
if __name__ == "__main__":
go()
随后将解密后的固件用010 Editor打开lz4加密,给出exp:
import sys
import os
# from Crypto.Cipher import ChaCha20
def usage():
print("Usage: {} image to extract".format(sys.argv[0]))
def go():
if len(sys.argv) < 2:
return usage()
with open(sys.argv[1],"rb") as f:
data = f.read()
data_start = data.find(b"x02x21x4cx18")
if data_start == -1:
print("LZ4 header not found")
return False
data_end = data.find(b"TRAILER!!!")
temp_end = data_end
while temp_end != -1:
data_end = temp_end
temp_end = data.find(b"TRAILER!!!",data_end+1)
# print(data_end)
if data_end == -1:
print("LZ4 trailer not found")
return False
data_end += 0x14-5
print("Found LZ4 from {:x} to {:x}".format(data_start,data_end))
filename_out = sys.argv[1] + "_fs.lz4"
with open(filename_out,"wb") as f:
f.write(data[data_start:data_end])
os.system("lz4 -d {}".format(filename_out))
return True
if __name__ == "__main__":
go()
原文始发于微信公众号(Th0r安全):记一次Vigor3910路由器敏感溢出分析