Zyxel设备固件提取与口令破解
背景介绍
在受客户之邀进行渗透测试过程中,我们利用弱口令成功拿下了Zyxel ZyWALL统一安全网关(USG)设备的管理权限,这些设备被用作分支机构的防火墙和VPN集线器。另外,这些设备通常是面向中小型企业的,而且很受欢迎,至少根据Shodan的结果来看确实如此。
获取管理权限后,我们有进一步转储了相关的配置,结果发现存储在这些设备上的一系列口令通过某种方式进行了加密。当我们在谷歌上搜索时,我们无法在互联网上找到任何关于这些口令是如何存储的公开信息。根据我们的观察,这些口令必须用可逆算法进行加密,因为它们是设备本身使用的口令,比如VPN的PSK。
由于我们有一些闲置的预算,我们决定在eBay上购买一台类似的设备,并花一些时间自己审计它。我们将在下一篇文章中为读者介绍针对该物理设备的分析结果。同时,设备到达之前,我们还可以先做一些准备工作,就是下面我们要介绍的内容。
作为Zyxel审计系列的第一篇文章,我们将为读者介绍如何提取ZyxelZyWALL统一安全网关(USG)设备的固件,并对加密的口令进行解密处理。
固件提取
首先,我们从官方网站下载了与客户部署的USG310设备相同的固件映像(4.10和4.70版本)。
我们开始研究4.10版的固件:文件410AAPJ2C0.bin实际上就是ZIP格式的固件映像。不幸的是,该ZIP归档是受口令保护的。在网上搜索一番之后,我们没有找到任何关于Zyxel公司使用的口令的信息。
然而,在随固件分发的PDF文件中,我们却找到了升级过程失败时的恢复方法。
如果上述方法不管用的话,还可以借助于一个较低级别的过程来实现恢复:
https://kb.zyxel.com/KB/searchArticle!gwsViewDetail.action?articleOid=006845&lang=EN
根据上述低级恢复指南中的介绍,我们可以看出系统是直接执行“.ri”文件来安装基本组件的,以便通过“.bin”文件来刷写固件。
于是,我们用binwalk提取了“410aapj2c0.ri”:
inode@stormbringer:w$ binwalk -e410AAPJ2C0.ri
DECIMAL HEXADECIMAL DEsc riptION
--------------------------------------------------------------------------------
512 0x200 uImage header, header size: 64bytes, header CRC: 0xF9EC0106, created: 2014-12-06 03:27:53, image size:4948010 bytes, Data Address: 0x5000000, Entry Point: 0x80101400, data CRC:0xD43F46E, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type:lzma, image name: "Linux Kernel Image"
576 0x240 LZMA compressed data, properties:0x5D, dictionary size: 8388608 bytes, uncompressed size: 16814648 bytes
inode@stormbringer:w$
其中包含下列文件:
inode@stormbringer:w/_410AAPJ2C0.ri.extracted$binwalk -e 240
DECIMAL HEXADECIMAL DEsc riptION
--------------------------------------------------------------------------------
0 0x0 ELF, 64-bit MSB MIPS32 rel2executable, MIPS, version 1 (SYSV)
5095576 0x4DC098 Linux kernel version 2.6.32
11385704 0xADBB68 gzip compressed data, maximumcompression, from Unix, last modified: 2014-12-06 01:57:21
11487616 0xAF4980 DES SP2, big endian
11488128 0xAF4B80 DES SP1, big endian
11511376 0xAFA650 CRC32 polynomial table, little endian
11910064 0xB5BBB0 Unix path: /usr/bin/magic-seed
11919441 0xB5E051 Unix path:/sys/module/perf_counters/parameters/counter
再次进行提取操作:
inode@stormbringer:w/_410AAPJ2C0.ri.extracted/_240.extracted$binwalk -e D65000
DECIMAL HEXADECIMAL DEsc riptION
--------------------------------------------------------------------------------
0 0x0 ASCII cpio archive (SVR4 with noCRC), file name: ".", file name length: "0x00000002", filesize: "0x00000000"
112 0x70 ASCII cpio archive (SVR4 with noCRC), file name: "zyinit", file name length: "0x00000007",file size: "0x00000000"
232 0xE8 ASCII cpio archive (SVR4 with noCRC), file name: "zyinit/zyinit", file name length:"0x0000000E", file size: "0x00040DEC"
266064 0x40F50 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/etc_inittab", file name length:"0x00000013", file size: "0x00000BB8"
269196 0x41B8C ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/e2fsck", file name length: "0x0000000E",file size: "0x0006B64C"
709204 0xAD254 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/zld_mrd.ko", file name length:"0x00000012", file size: "0x00001788"
715356 0xAEA5C ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/rw.zip", file name length: "0x0000000E",file size: "0x002106C8"
2879904 0x2BF1A0 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/mke2fs", file name length: "0x0000000E",file size: "0x0004239C"
3151288 0x3015B8 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/switchdev.ko", file name length:"0x00000014", file size: "0x0000AF10"
3196236 0x30C54C ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/zld_fsextract", file name length:"0x00000015", file size: "0x00017098"
3290728 0x323668 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/sw_cn60xx.ko", file name length: "0x00000014",file size: "0x00005418"
3312388 0x328B04 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/zld_udev", file name length:"0x00000010", file size: "0x0001347C"
3391488 0x33C000 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/db.zip", file name length: "0x0000000E",file size: "0x00001428"
3396772 0x33D4A4 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/zyinit_gpl", file name length:"0x00000012", file size: "0x00055128"
3745356 0x39264C ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/fwversion", file name length:"0x00000011", file size: "0x0000014A"
3745816 0x392818 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/switchdev_char.ko", file name length:"0x00000019", file size: "0x00005800"
3768480 0x3980A0 ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/unzip", file name length: "0x0000000D",file size: "0x0002B8B0"
3946956 0x3C39CC ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/lkm.lst", file name length:"0x0000000F", file size: "0x00000050"
3947164 0x3C3A9C ASCII cpio archive (SVR4 with no CRC),file name: "zyinit/platform_support.ko", file name length:"0x0000001B", file size: "0x00004CC8"
3966960 0x3C87F0 ASCII cpio archive (SVR4 with no CRC),file name: "init", file name length: "0x00000005", filesize: "0x0000000D"
3967092 0x3C8874 ASCII cpio archive (SVR4 with no CRC),file name: "TRAILER!!!", file name length: "0x0000000B",file size: "0x00000000"
inode@stormbringer:w/_410AAPJ2C0.ri.extracted/_240.extracted$
检查提取的文件,在zyinit二进制文件中找到了一些与固件有关的字符串。
通过分析,看看它是否启动了其他外部命令,特别是“zld_fsextract”命令:
在网上搜索这些可执行的二进制文件,只找到了一个有趣的URL(https://www.dslreports.com/forum/remark,26961186),其中含有一些关于ZIP口令的信息。
在“zld_fsextract”二进制文件中搜索口令,很可能找出一些有用的线索,比如:
这些选项由“unzip”二进制文件使用参数“-p”中定义的特定口令来解压缩文件。根据网上找到的信息,我们认为该二进制文件似乎是根据二进制文件的名称或二进制文件的内容,以某种方式来计算解压口令的。
提取文件的最简单方法,就是模拟MIPS处理器并执行该二进制文件。
为此,先启动一个Linux/MIPS虚拟机,具体命令如下所示:
> wgethttps://people.debian.org/~aurel32/qemu/mips/vmlinux-3.2.0-4-5kc-malta
> wgethttps://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2
> qemu-system-mips64.exe -M malta-kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mips_standard.qcow2-append "root=/dev/sda1 console=tty0" -net nic -netuser,hostfwd=tcp:127.0.0.1:2222-:22
然后,使用“zld_fsextract”二进制文件来获取固件映像信息,具体如下所示:
root@debian-mips:~# ./zld_fsextract410AAPJ2C0.bin -s list
name :kernel
scope :-f kernelusg310.bin -fkernelchecksum -D /
nc_scope :-f kernelusg310.bin
version :2.6.32
build_date :2014-12-06 11:27:35
checksum :6e4e1ad212be0a8a3ce89484f3c5dc1e
core_checksum :a028057f7c742ea52bd3d0408f38a673
name :code
scope :-f bmusg310.bin -f bmchecksum-f kernelusg310.bin -f kernelchecksum -d wtp_image -d db -i -D /rw
scope :-d db/etc/zyxel/ftp/conf -D /
nc_scope :-f fwversion -f filechecksum -fwtpinfo
version :4.10(AAPJ.2)
build_date :2014-12-09 08:51:18
checksum :899be95dac4a5bdcfd2f694035f16746
core_checksum :e25ab639d4ff432bb7ffdc8c1bd39be3
name :WTP_wtp_image/400AAS4C0.bin
scope :-f wtp_image/400AAS4C0.bin -D/db
nc_scope :
version :4.00(###.4)
build_date :2013-08-31 20:36:43
checksum :07773b3deb39a1dd9c2036201097529c
core_checksum :07773b3deb39a1dd9c2036201097529c
name :WTP_wtp_image/400AADG4C0.bin
scope :-f wtp_image/400AADG4C0.bin -D/db
nc_scope :
version :V4.00(###.4)
build_date :2013-08-31 20:35:40
checksum :aabb0606887cec28a07b8598e699f027
core_checksum :aabb0606887cec28a07b8598e699f027
root@debian-mips:~#
并使用zld_fsextract二进制文件提取固件,这里无需指定口令:
root@debian-mips:~# ./zld_fsextract410AAPJ2C0.bin ./unzip -s extract -e code
...
root@debian-mips:~#
root@debian-mips:~# ls -al /rw/
total 57192
drwxr-xr-x 3 root root 4096 Nov 23 12:56.
drwxr-xr-x 24 root root 4096 Nov 23 12:56 ..
-r--r--r-- 1 root root 58511360 Dec 6 2014 compress.img
drwxr-xr-x 5 root root 4096 Dec 9 2014etc_writable
-rw-r--r-- 1 root root 139 Dec 9 2014filechecksum
-rw-r--r-- 1 root root 21415 Dec 9 2014filelist
-rw-r--r-- 1 root root 326 Dec 9 2014fwversion
-rw-r--r-- 1 root root 1671 Dec 9 2014wtpinfo
root@debian-mips:~#
然后解压缩该新映像:
inode@stormbringer:w$ binwalk -ecompress.img
DECIMAL HEXADECIMAL DEsc riptION
--------------------------------------------------------------------------------
0 0x0 Squashfs filesystem, littleendian, version 4.0, compression:gzip, size: 58509745 bytes, 4958 inodes,blocksize: 131072 bytes, created: 2014-12-06 03:26:20
inode@stormbringer:w$
在看到上述流程可以在完全仿真的环境中正确工作后,我们决定让QEMU来仿真单个二进制文件。通过使用“strace”,我们可以快速检查“unzip”二进制文件是如何启动的;了解该过程有助于我们查找ZIP口令。
strace -f -s 199 qemu-mipsn32-static./zld_fsextract 410AAPJ2C0.bin ./unzip -s extract -e code
上述命令的输出结果如下所示:
这个计算ZIP口令的二进制文件是静态编译的,因此,要了解该口令是如何产生的并不容易,最重要的是我们很懒……
口令的加密算法
现在我们已经可以访问文件系统了,接下来,让我们开始寻找用于在配置文件中存储口令的算法的信息。在我们可用的配置中,所有的口令采用的格式都是4$,并在前面加上了下面的关键词:
- encrypted-key
- encrypted-keystring
- encrypted-password
- encrypted-presharekey
- password-encrypted
- wpa-psk-encrypted
第一步,确定这些关键字是由哪些可执行文件处理的:
inode@stormbringer:w$ grep"encrypted-" _compress.img.extracted/squashfs-root/bin/*
Binary file_compress.img.extracted/squashfs-root/bin/zysh matches
Binary file_compress.img.extracted/squashfs-root/bin/zyshd matches
inode@stormbringer:w$
在“zyshd”的代码中查找标识加密口令的字符串“$4$”:
然后,检查schrodinger例程,它用于生成盐:
加密是由以下代码负责:
最后,还会进行base64编码:
查看“rij_cbc_encrypt”函数内部,可以找到将我们引向AES算法的例程“rij_decrypt”。同时,函数的名称(RIJ=Rijndael)以及设置密钥使用的0xC0(即192)也证实了这一点:AES是少数支持192位密钥的加密算法之一。
查看加密例程内部,可以发现像Td0内存这样的AES参数;此外,我们也可以使用FindCrypt Ghidra插件来帮助识别相应的加密例程。
我们可以假设加密/解密函数的工作方式如下:
RIJ_cbc_encrypt(destination, source, len,unknown, IV, encryption/decryption byte)
接下来,我们只需要确定传递给函数的参数。
IV是:
加密密钥是:
所以:
aes_key ="001200054A1F23FB1F060A14CD0D018F5AC0001306F0121C"
aes_iv = "0006001C01F01FC0FFFFFFFFFFFFFFFF"
对于4.70版本的固件,也可以遵循相同的过程来识别$5$加密方案。
总而言之,对于以$4$开头的口令哈希值,算法具体如下:
iv = static_iv
key = static_key
salt = generate_8_bytes_from_random()
to_encrypt = salt + password (the passwordis repeated to reach the 80 bytes)
AES_SET_KEY(aes,key,192)
AES_DECRYPT(aes,cyphertext,to_encrypt,key,iv)
final_encrypted = base64(cyphertext)
final_string = "$4$" + salt +"$" + final_encrypted "$"
而以$5$开头的口令哈希值,算法如下:
iv = static_iv
key = static_key
salt = generate_8_bytes_from_random()
to_encrypt = salt + password (the passwordis repeated to reach the 80 bytes)
AES_SET_KEY(aes,key,192)
AES_DECRYPT(aes,cyphertext,to_encrypt,key,iv)
salt = generate_8_bytes_from_random()
to_encrypt = salt + password (the passwordis repeated to reach the 80 bytes)
AES_SET_KEY(aes,key,192)
AES_DECRYPT(aes,cyphertext,to_encrypt,key,iv)
step1 = base64(cyphertext)
salt1 = generate_8_bytes_from_random()
to_encrypt = salt1 + step1
AES_SET_KEY(aes,key,192)
AES_DECRYPT(aes,cyphertext,to_encrypt,key,iv)
final_encrypted = base64(cyphertext)
final_string = "$5$" + salt1 +"$" + salt2 + "$" + final_encrypted "$"
我们开发了一个解密工具,可以从以下网址下载:https://github.com/inode-/zyxel_password_decrypter。
对于这些算法,另一种逆向分析方法是进行动态分析,但QEMU似乎对Zyxel使用的SoC的支持并不完美,并在大多数可执行文件上都会出现非法指令错误。在https://github.com/amir-mehmood/qemu-octeon-mips64上,好像有一个有效的QEMU补丁程序,但我们尚未对其进行测试。
最后,当我们收到物理设备后,发现文件系统中的二进制文件/usr/sbin/zencrypt也可以用来解密这些口令:
root@https://security.humanativaspa.it/zyxel-firmware-extraction-and-password-analysis/USG20-VPN:~# zencrypt -k'E8AzAwOyYBwVHJJjtUTDqdAxLDPIr/ncDNV5HCfZFRJlqhYkSqbGvX9BP06YyBg5fGp2HQrwtHsp9mQLNAtBeEIPejrUv5jw/ZRmUx20nuwCNK0UZdeklaFWc945oR3zWVupR2i/KakLhb9d46q72geNfFnXggbJdsl7ObjGKUNLfRF6tMnvxGtPO54PJKpL8E9Af4LAqStAAEPu3S70og8h9PzUER9/Qq246kyTfjONyyVF49EX1l+vg6rD0y+5UnHamBedqvzr/e7az469oViXUIxfMnr/CpMgR5hgzKn3efqldNVSEqmS07UN6CkjHiWaQgnDTxhc4tjbZQjaT3CGVpghF7W3GzmYtMqQlYQ$'
pluto
感谢您的阅读,我们将在下一篇文章中继续为读者介绍有趣的漏洞!
本文由secM整理并翻译,不代表白帽汇任何观点和立场
原文地址:https://security.humanativaspa.it/zyxel-firmware-extraction-and-password-analysis/
最新评论