0x01 前言
关于Zyxel 固件的解密和提取的分析,最近的这篇文章给了我一个很好的idea,感兴趣的可以去看一下。
文章连接:https://security.humanativaspa.it/zyxel-firmware-extraction-and-password-analysis/
对固件进行漏洞挖掘的第一步,就是从固件中提取我们需要的一些文件和组件,从而进行漏洞分析,但是如果解开固件包的过程中,显示固件已被加密,并且无法确定如何进行解密,那么是特别难受的。从一些常见的固件解密的方式来看,有的固件是压缩的时候进行加密的,那么解密程序或者生成解密密钥的二进制文件有可能会存在和固件包一起的打包出来,那么可以根据确定一些与解密有关的关键信息,和逆向加密算法对加密方式进行对应的破解,但是,i am lazy 🙂 。所以通过收集固件的信息,了解其中的加密的一些命令和方式,比如zyxel固件解密所描述,根据一些特殊的组件猜测其解密的方式,尝试去解密固件,虽然这个过程也很艰难,但相对去逆向晦涩难懂的加密算法而言,难度就小很多了。
另外也可以根据linux中常见的解密工具,比如unzip 来确定固件中的解密方式,如下图所示的这个案例,通过检索unzip信息,获取到了解密的方式和解密密钥。
接下来我来讲述一下关于Zyxel 固件的另一种固件解密的idea, 虽然在固件解密这一块,也并不怎么擅长,但仍希望能给大家带来不一样的想法。
0x02 固件解包 1
固件版本:USG310 4.70
固件下载连接:https://firmware.cdn.cloud.zyxel.com/firmware/AAPJ/4.72(AAPJ.0)/firmware.zip
固件指令架构:ELF 32-bit MSB executable, MIPS, N32 MIPS64 rel2 version 1 (SYSV), statically linked, stripped
这里我使用的是 firmware_USG310_4.70(APPJ.0) 固件包, 下载下来之后,这是一个zip 压缩的压缩文件,解压之后的目录如下所示
后的目录如下所示
我们要解开的固件是 470AAPJ0C0.bin , 使用zip 查看 470AAPJ0C0.bin。
同时使用zip 解压提取固件包的时候,显示是需要密码的。
但是在 dbetczyxelftpconf目录中,看到了system-defalut.conf 文件,这个文件的大小为 72871和 470AAJ0C0.conf 文件 (72870) 的大小只相差1字节。
如此的巧合让我猜测这两个文件应该是同一个文件,我尝试使用pkcrack 明文攻击来破解,很遗憾,并没有达到我要的破解效果,甚至我在 470AAJ0C0.conf 文件中增加了一个字节,让这两个文件的大小相同,同样没有达到效果。
于是根据https://security.humanativaspa.it/zyxel-firmware-extraction-and-password-analysis/,我使用binwalk 解开470AAJ0C0.ri 文件。
接着使用binwalk 解开 _470AAPJ0C0 文件中的240 文件。
tigerortiger@ubuntu ~/i/g/z/firmware_USG310_4.70> cd _470AAPJ0C0.ri.extracted/
tigerortiger@ubuntu ~/i/g/z/f/_470AAPJ0C0.ri.extracted> ls
240 240.7z
tigerortiger@ubuntu ~/i/g/z/f/_470AAPJ0C0.ri.extracted> binwalk -e 240
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 ELF, 64-bit MSB MIPS32 rel2 executable, MIPS, version 1 (SYSV)
5107864 0x4DF098 Linux kernel version 3.10.8
5179656 0x4F0908 gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
5196240 0x4F49D0 Unix path: /home/sdd1/buildbot/470-share1100-k3-slave/470-share1100-k3/build/share1100-r102127-k3/share1100/src/kernel
5284016 0x50A0B0 DES SP2, big endian
5284528 0x50A2B0 DES SP1, big endian
5824472 0x58DFD8 Unix path: /usr/bin/magic-seed
5853384 0x5950C8 Unix path: /home/sdd1/buildbot/470-share1100-k3-slave/470-share1100-k3/build/share1100-r102127-k3/share1100/src/kernel/arch/mips/include/as
5939112 0x5A9FA8 xz compressed data
6040223 0x5C2A9F Copyright string: "Copyright 2005-2007 Rodolfo Giometti <[email protected]>"
6092400 0x5CF670 Neighborly text, "NeighborSolicits"
6092424 0x5CF688 Neighborly text, "NeighborAdvertisementsorts"
6097330 0x5D09B2 Neighborly text, "neighbor %.2x%.2x.%pM lost rename link %s to %s"
6463872 0x62A180 CRC32 polynomial table, little endian
6643248 0x655E30 Unix path: /usr/local/zld_udev/sbin/uevent_helper.sh
7060288 0x6BBB40 Flattened device tree, size: 7847 bytes, version: 17
7068160 0x6BDA00 Flattened device tree, size: 12032 bytes, version: 17
7085520 0x6C1DD0 ASCII cpio archive (SVR4 with no CRC), file name: ".", file name length: "0x00000002", file size: "0x00000000"
7085632 0x6C1E40 ASCII cpio archive (SVR4 with no CRC), file name: "init", file name length: "0x00000005", file size: "0x0000000D"
7085764 0x6C1EC4 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit", file name length: "0x00000007", file size: "0x00000000"
7085884 0x6C1F3C ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/zld_fsextract", file name length: "0x00000015", file size: "0x00017098"
7180376 0x6D9058 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/etc_inittab", file name length: "0x00000013", file size: "0x00000BB8"
7183508 0x6D9C94 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/rw.zip", file name length: "0x0000000E", file size: "0x001330BD"
8441296 0x80CDD0 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/zyinit_gpl", file name length: "0x00000012", file size: "0x00055128"
8789880 0x861F78 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/sw_cn60xx.ko", file name length: "0x00000014", file size: "0x000073E0"
8819676 0x8693DC ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/e2fsck", file name length: "0x0000000E", file size: "0x0006B64C"
9259684 0x8D4AA4 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/zld_udev", file name length: "0x00000010", file size: "0x0008B460"
9830276 0x95FF84 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/zld_mrd.ko", file name length: "0x00000012", file size: "0x000016D0"
9836244 0x9616D4 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/lkm.lst", file name length: "0x0000000F", file size: "0x00000050"
9836452 0x9617A4 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/db.zip", file name length: "0x0000000E", file size: "0x00003829"
9850956 0x96504C ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/mke2fs", file name length: "0x0000000E", file size: "0x0004239C"
10122340 0x9A7464 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/unzip", file name length: "0x0000000D", file size: "0x0002B8B0"
10300816 0x9D2D90 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/platform_support.ko", file name length: "0x0000001B", file size: "0x000048C0"
10319580 0x9D76DC ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/switchdev_char.ko", file name length: "0x00000019", file size: "0x00004DE8"
10339660 0x9DC54C ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/switchdev.ko", file name length: "0x00000014", file size: "0x00009A90"
10379360 0x9E6060 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/fwversion", file name length: "0x00000011", file size: "0x00000158"
10379832 0x9E6238 ASCII cpio archive (SVR4 with no CRC), file name: "zyinit/zyinit", file name length: "0x0000000E", file size: "0x000C0AEC"
11169184 0xAA6DA0 ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
解开240 如下的文件目录包含一下内容,我看到了db.zip。这让我联想到了 “firmware_USG310_4.70_2470AAPJ0C0.bindbetczyxelftpconf” 目录中的system-defalut.conf 文件
于是使用unzip解开db.zip 文件,很幸运,这个zip 并没有同 470AAPJ0C0.bin 一样被加密。
可以看到里面有一个 system-default.conf 文件,可以看到这个文件的大小和名称和前面看到的一样大。我认为这个文件和”firmware_USG310_4.70_2470AAPJ0C0.bindbetczyxelftpconf” 目录中的system-defalut.conf 文件是同一个文件。这样我又可以使用 pkcrack 进行破解了。
在使用pkcrack 明文攻击进行破解之前,简单的描述一下pkcrack 的攻击原理。
0x03 pkcrack 明文攻击
明文攻击 该攻击是已知的纯文本攻击,这意味着您必须知道部分加密数据才能破解密码。比如:加密压缩包中有10张图片,其中1张图片你有未加密的源文件。常用工具: ARCHPR 4.53、pkcrack 原理:明文攻击是一种较为高效的攻击手段,大致原理是当你不知道一个zip的密码,但是你有zip中的一个已知文件(文件大小要大于12Byte)或者已经通过其他手段知道zip加密文件中的某些内容时,因为同一个zip压缩包里的所有文件都是使用同一个加密密钥来加密的,所以可以用已知文件来找加密密钥,利用密钥来解锁其他加密文件 速度:非常快
pkcark 进行明文破解 使用pkcrack进行明文破解需要注意一点:压缩包中可能包含多个加密文件,但我们只要持有其中一个文件即可,该文件必须和压缩包中的某个文件一模一样。在破解前,需要先把明文文件进行压缩。如果使用zip命令直接压缩可能会出现压缩率的问题。windows下可以使用7-zip一类的桌面应用进行压缩。使用7-zip进行压缩时,会有一个压缩率的选项,可以调整word size的大小,如果这个压缩率和加密文件的压缩率不匹配,破解时可能会出现文件长度不匹配问题 查看压缩率可以借助 zipdetails、360压缩、7zip等查看。https://github.com/keyunluo/pkcrack
pkcrack 破解需要两个文件
-
需要解密的ZIP文件 -
另一个zip文件,其中至少包含未加密的加密存档红的一个文件。必须使用与加密文件相同的压缩方法压缩该文件。
pkcrack -C encrypted-ZIP -c ciphertextname -P plaintext-ZIP -p plaintextname -d decrypted_file -a
encrypted-ZIP:是加密的ZIP归档文件的名称(和路径) ciphertextname:是存档中文件的名称,对于该文件,您具有-纯文本 plaintext-ZIP:是包含压缩明文的ZIP归档文件的名称(和路径) plaintextname:是归档文件中包含已知明文的文件名 unlocked_file:是解密档案将被写入的文件的名称
示例
demo ├── demo.zip # encrypted-ZIP ├── pkcrack ├── pkcrack.exe ├── README.txt # plaintext └── README.zip # plaintext-ZIP
用shell 命令用于破解
../bin/pkcrack -C demo.zip -c README.txt -P README.zip -p README.txt -d cracked.zip -a
0x04 固件解包2
使用pkcrack 解开固件
tigerortiger@ubuntu ~/i/g/z/firmware_USG310_4.70>
zip -9 470AAPJ0C0.zip system-default.conf
adding: system-default.conf (deflated 84%)
tigerortiger@ubuntu ~/i/g/z/firmware_USG310_4.70>
/home/tigerortiger/tools/pkcrack/bin/pkcrack -C 470AAPJ0C0.bin -c db/etc/zyxel/ftp/conf/system-default.conf -P 470AAPJ0C0.zip -p system-default.conf -d cracked.zip -a
Generating 1st generation of possible key2_11532 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Lowest number: 989 values at offset 6926
Lowest number: 954 values at offset 6924
Lowest number: 898 values at offset 5324
Done. Left with 898 possible Values. bestOffset is 5324.
Stage 1 completed. Starting stage 2 on
Ta-daaaaa! key0=184108c9, key1=a102a710, key2=8f550772
Probabilistic test succeeded for 6213 bytes.
Ta-daaaaa! key0=184108c9, key1=a102a710, key2=8f550772
Probabilistic test succeeded for 6213 bytes.
Stage 2 completed. Starting zipdecrypt on
Decrypting compress.img (624a9fbe5fcf76c1bdf8eb86)... OK!
Decrypting db/ (863c636d34664e0e43c3858b)... OK!
Decrypting db/etc/ (9438acbd8959952d160c858b)... OK!
Decrypting db/etc/zyxel/ (51b87c5ec94917155458858b)... OK!
Decrypting db/etc/zyxel/ftp/ (b28c0fca7cc7f78ffa8e858b)... OK!
Decrypting db/etc/zyxel/ftp/conf/ (2525dc17bac05f9a4da7858b)... OK!
Decrypting db/etc/zyxel/ftp/conf/system-default.conf (c4c22bea727241c6f6816779)... OK!
...
Decrypting etc_writable/zyxel/conf/__system_default_device_ha.xml (c83a53734113602259af6779)... OK!
Decrypting etc_writable/zyxel/conf/__system_default_dynamic_guest_log.xml (2061195d4ef1dfcb2b2d6779)... OK!
Decrypting etc_writable/zyxel/conf/__wantrunk_default.xml (d06c63f2a332c4dc718dc085)... OK!
Decrypting etc_writable/zyxel/conf/__zwo.xml (5d594289e3ff7f8d5bebcf86)... OK!
Decrypting etc_writable/zyxel/conf/dynamic_guest_log.xml (f66649f9e313ccd24136868b)... OK!
Decrypting etc_writable/zyxel/coredump_script/ (ae137544f7ad54f9eebd868b)... OK!
Decrypting etc_writable/zyxel/coredump_script/common.sh (dfcc6000cb3ca1609dec1786)... OK!
Decrypting etc_writable/zyxel/coredump_script/samples.sh (5fb2e2a1cae7bbddfe761786)... OK!
Decrypting etc_writable/zyxel/coredump_script/sdwan_common.sh (5160662af82248b81a751786)... OK!
Decrypting etc_writable/zyxel/printer/ (76acf8b17a486b36cfbb868b)... OK!
Decrypting etc_writable/zyxel/printer/SP350E.dat (d8b9df979471623787e18e81)... OK!
Decrypting etc_writable/zyxel/printer/double_print.sh (9f01ea1939359e15be448e81)... OK!
Decrypting etc_writable/zyxel/secuextender/ (0e53686443987b96c859868b)... OK!
Decrypting etc_writable/zyxel/secuextender/applet.html (9df9661a81cfa97ff5ba2e82)... OK!
Decrypting etc_writable/zyxel/secuextender/sslapp.jar (f407182e1b0743d27b67a982)... OK!
Decrypting etc_writable/zyxel/selector/ (296a84395ffc890845b1437f)... OK!
Decrypting filechecksum (b48c0dec79379e4cac83898b)... OK!
Decrypting filelist (878dcff5a799dfe9ef6c8a8b)... OK!
Decrypting fwversion (e223b319d7586e29573e848b)... OK!
Decrypting kernelchecksum (8d701dd63dd56aa385810987)... OK!
Decrypting kernelshare1100.bin (0ef2d6fb1ee26b107de40987)... OK!
Decrypting wtp_image/ (2423c9bb7b21b8c347cb898b)... OK!
Decrypting wtp_image/cloud_checksum (5dd7bf2962fa3637a954898b)... OK!
Decrypting wtp_image/nwa5123-ac (3d4ca00fc39c85554711878b)... OK!
Decrypting wtp_image/nwa5123-ac-hd (fb86ad6cc45e1e0a12e9888b)... OK!
Decrypting wtp_image/wtpinfo (298480fe98ba3898b2b3888b)... OK!
Decrypting wtpinfo (9deeef6c027ef4bd1420888b)... OK
成功解开zip 文件。其实为什么470AAJ0C0.conf 文件不能爆破,我解开之后,大概的看了一下system-default.conf文件,这两个文件的内容是大概是一模一样的,没仔细找,那多出的一个字节是啥。
0x05 提取文件系统
查看compress.img 的文件格式,可以看到这是Squashfs
解开之后整个文件目录结构
├── _470AAPJ0C0.ri.extracted
│ └── _240.extracted
│ ├── cpio-root
│ └── etc
├── _compress.img.extracted
│ └── squashfs-root
│ ├── bin
│ ├── dev
│ ├── etc
│ ├── lib
│ ├── lib32 -> lib/
│ ├── lib64
│ ├── sbin
│ ├── usr
│ ├── util
│ └── var
├── db
│ └── etc
│ └── zyxel
├── etc_writable
│ ├── budget
│ ├── ModemManager
│ ├── usb_modeswitch
│ └── zyxel
│ ├── conf
│ ├── coredump_script
│ ├── printer
│ ├── secuextender
│ └── selector
└── wtp_image
0x06 zld_fsextract 解开固件
关于 zld_fsextract 的讲解,原文讲的很详细了,这里就复现一下
https://security.humanativaspa.it/zyxel-firmware-extraction-and-password-analysis/
tigerortiger@ubuntu ~/i/g/z/f/_470AAPJ0C0.ri.extracted [1]> chmod -R 777 ./_240.extracted/
tigerortiger@ubuntu ~/i/g/z/f/_/_240.extracted> file zld_fsextract
zld_fsextract: ELF 32-bit MSB executable, MIPS, N32 MIPS64 rel2 version 1 (SYSV), statically linked, stripped
tigerortiger@ubuntu ~/i/g/z/f/_/_240.extracted> which qemu-mipsn32-static
/usr/bin/qemu-mipsn32-static
tigerortiger@ubuntu ~/i/g/z/f/_/_240.extracted> cp /usr/bin/qemu-mipsn32-static ./
tigerortiger@ubuntu ~/i/g/z/f/_/_240.extracted> chmod 777 qemu-mipsn32-static
tigerortiger@ubuntu ~/i/g/z/f/_/_240.extracted>
sudo strace -f -s 199 qemu-mipsn32-static ./zld_fsextract ../../470AAPJ0C0.bin ./unzip -s extract -e code
[sudo] password for tigerortiger:
execve("/usr/bin/qemu-mipsn32-static", ["qemu-mipsn32-static", "./zld_fsextract", "../../470AAPJ0C0.bin", "./unzip", "-s", "extract", "-e", "code"], 0x7fffa6cc8d20 /* 17 vars */) = 0
brk(NULL) = 0x62813000
brk(0x62814280) = 0x62814280
arch_prctl(ARCH_SET_FS, 0x62813940) = 0
uname({sysname="Linux", nodename="ubuntu", ...}) = 0
set_tid_address(0x62813c10) = 64399
set_robust_list(0x62813c20, 24) = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x6014ccc0, sa_mask=[], s
...
[pid 3040] wait4(-1, strace: Process 3048 attached
<unfinished ...>
[pid 3048] set_robust_list(0x64384c20, 24) = 0
[pid 3048] rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], ~[KILL STOP RTMIN RT_1], 8) = 0
[pid 3048] clone(child_stack=0x7fd85d2dcdb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7fd85d2dd9d0, tls=0x7fd85d2dd700, child_tidptr=0x7fd85d2dd9d0) = 3049
[pid 3048] rt_sigprocmask(SIG_SETMASK, ~[KILL STOP RTMIN RT_1], NULL, 8) = 0
[pid 3048] rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], NULL, 8) = 0
[pid 3048] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 3048] execve("./unzip", ["./unzip", "-o", "-q", "-P", "RvP5AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXFmhjdq.rqDqX.cPD9BG3eq/Y", "../../470AAPJ0C0.bin", "-d", "/", "db/etc/zyxel/ftp/conf/", "db/etc/zyxel/ftp/conf/system-default.conf"], 0x643d4b50 /* 16 vars */strace: Process 3049 attached
<unfinished ...>
[pid 3049] +++ exited with 0 +++
[pid 3048] <... execve resumed> ) = 0
[pid 3048] brk(NULL) = 0x64372000
[pid 3048] brk(0x64373280) = 0x64373280
[pid 3048] arch_prctl(ARCH_SET_FS, 0x64372940) = 0
[pid 3048] uname({sysname="Linux", nodename="ubuntu", ...}) = 0
[pid 3048] set_tid_address(0x64372c10) = 3048
[pid 3048] set_robust_list(0x64372c20, 24) = 0
[pid 3048] rt_sigaction(SIGRTMIN, {sa_handler=0x60142520, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x6014a9b0}, NULL, 8) = 0
[pid 3048] rt_sigaction(SIGRT_1, {sa_handler=0x601425c0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x6014a9b0}, NULL, 8) = 0
[pid 3048] rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
[pid 3048] prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
[pid 3048] readlink("/proc/self/exe", "/usr/bin/qemu-mips-static", 4096) = 25
[pid 3048] brk(0x64394280) = 0x64394280
主要是查看这一句,密钥有一部分我就不公布了。
[pid 3048] execve("./unzip", ["./unzip", "-o", "-q", "-P", "RvP5AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXFmhjdq.rqDqX.cPD9BG3eq/Y", "../../470AAPJ0C0.bin", "-d", "/", "db/etc/zyxel/ftp/conf/", "db/etc/zyxel/ftp/conf/system-default.conf"],
最终用整个密钥也可以解开470AAPJ0C0.bin
0x07 总结
这个固件解密的方式主要用的方式pkcrack,利用的就是一个未加密的conf文件,进行解密,总的来说,在分析加密固件的时候,应该对那些伴随加密固件一起发布出来的其他文件进行详细的信息收集,以便了解更多与固件相关的信息,另外,加密固件在更新到设备上的时候,是需要解密后提取文件系统才能使用的,如果设备上原本是没有密钥或者生成密钥的算法,那么解密的密钥或者解密的方式大概率会和加密固件一起发布出来。
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):USG310 4.70 固件解密分析