USG310 4.70 固件解密分析
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 解壓提取固件包的時候,顯示是需要密碼的。

但是在 db\etc\zyxel\ftp\conf\目錄中,看到了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 " 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_2\470AAPJ0C0.bin\db\etc\zyxel\ftp\conf" 目錄中的system-defalut.conf 文件

于是使用unzip解開db.zip 文件,很幸運,這個zip 并沒有同 470AAPJ0C0.bin 一樣被加密。

可以看到里面有一個 system-default.conf 文件,可以看到這個文件的大小和名稱和前面看到的一樣大。我認為這個文件和"firmware_USG310_4.70_2\470AAPJ0C0.bin\db\etc\zyxel\ftp\conf" 目錄中的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
[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
[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文件,進行解密,總的來說,在分析加密固件的時候,應該對那些伴隨加密固件一起發布出來的其他文件進行詳細的信息收集,以便了解更多與固件相關的信息,另外,加密固件在更新到設備上的時候,是需要解密后提取文件系統才能使用的,如果設備上原本是沒有密鑰或者生成密鑰的算法,那么解密的密鑰或者解密的方式大概率會和加密固件一起發布出來。