固件解密分析:DrayTek Vigor3910 固件

** **DrayTek Vigor3910 固件下载

固件信息:

Draytek Vigor 3910是Draytek旗下的一款多WAN安全路由器。** *厂商:Draytek* *型号:Vigor 3910* **固件版本:3971、4325

使用 binwalk 进行初步分析

为了了解固件的结构和加密状态,我们可以使用 binwalk 工具进行分析。以下是具体步骤和发现:

熵值分析

通过以下命令查看固件的熵值分布:

1
binwalk -E firmware.bin

![image-

结果:分析显示固件的熵值接近 1

熵值含义

  • 熵值(entropy)是衡量数据随机性的指标,范围从 0 到 1。
  • 熵值接近 0 表示数据高度有序(如纯文本或重复模式)。
  • 熵值接近 1 表示数据高度随机,通常是压缩或加密的特征。

结论:固件的熵值接近 1,表明其内容并非明文或简单压缩,而是经过加密处理。

中间过渡版本分析

为了寻找未加密的中间过渡件,我们回溯历史版本,下载 V3.9.7.1:

  • 文件:v3910_3972.all(假设为 V3.9.7.1)。
  • 提取尝试
1
binwalk -Me v3910_3972.all

熵值检查

1
binwalk -E v3910_3972.all

结果:熵值较低,表明 V3.9.7.1 未加密。

稍微分析一下

固件基本结构

固件通常包含以下部分:

  1. Bootloader:硬件初始化和内核加载,位于固件开头。
  2. Kernel:操作系统核心,通常压缩(如 LZMA、gzip)。
  3. Rootfs:用户空间文件系统,包含脚本、二进制和多媒体内容,常以压缩格式存储(如 LZ4、gzip)。

未加密固件分析 (V3.9.7.1)

  • 结构分析
    1
    binwalk v3910_3972.all

结果:偏移 0xEE3C48(十进制 15613000)标记为 LZ4 compressed data, legacy,确定为文件系统起点。

依据

  • LZ4 压缩常见于 initramfs 或 rootfs。
  • 后续包含脚本、图像和路径,符合文件系统特征。
  • 位于内核之后,符合固件布局。

使用dd命令提取这部分的文件系统

1
dd if=v3910_3972.all of=gizp.bin bs=1 skip=15613000 

这里为了方便就直接从这里开始提取后面的全部

binwalk并不支持lz4解压缩,故我们下载liblze4-tool,并在binwalk的配置文件中添加规则,(路径看shell图片的路径)

1
^lz4 compressed data:lz4:lz4 -d '%e' '%e.bin'
1
sudo apt-get install liblz4-tool

们再次使用binwalk -Me固件,到这里就得到了最开始没有加密的固件系统,

从未加密到加密的过渡

V3.9.7.1 未加密,而 V4.4.3.1 已加密,说明固件升级引入了加密机制。我们在 V3.9.7.1 的文件系统中搜索升级逻辑:

  • 搜索关键字
1
2
grep -r "upgrade" 
grep -r "firmware"

发现:fw_upload 文件包含 “upgrade” 和 “firmware”,为固件升级脚本。

解密逻辑

    • ChaCha20 简介

      • 全称:ChaCha20 是 ChaCha 加密算法的变种,使用 20 轮迭代。
      • 类型:流加密算法(Stream Cipher)。
      • 核心组件
        • 密钥(Key):256 位(32 字节)。
        • Nonce:96 位(12 字节)或 64 位(8 字节)。
        • 计数器(Counter):32 位或 64 位。
        • 常量(Constants):如 “expand 32-byte k”。
      • 用途:保护固件内容,广泛用于 TLS/SSL、VPN 等。

对此二进制文件进行一些基础的信息收集,可以知道其为arm64的小端序

准备 QEMU:通过cp把qemu-aarch64-static移动到当前目录下,去运行此程序

1
cp $(which qemu-aarch64-static) ./

报错:Bad input data in argv,缺少输入输出文件。

1
2
3
sudo chroot . ./qemu-aarch64-static ./sbin/chacha20
Bad input data in argv
Using: ./main input.file output.file

仍报错:参数格式可能不正确。

1
2
3
4
sudo chroot . ./qemu-aarch64-static ./sbin/chacha20 v3910_4326.all
decrypt.all
Bad input data in argv
Using: ./main input.file output.file

逆向分析 chacha20

  • 工具:IDA Pro。
  • 通过IDA对此二进制程序进行逆向分析发现里面会存在key

这里找到key了我们还需要他的nonce随机数,

在加密的固件头信息中发现了这个值,

要知道加密的固件是从enc_Image之后开始的,所以我们要提取被加密的部分,

这里的 enc_Image 指的是固件中某个标记或区域的名称,通常在固件解包后会生成类似 enc_Image 的文件,表示加密的镜像部分。

注意这里怎么去提,image后面的一个byte是是enc固定的大小,这里是小端序所以就是034B1A00,这个转成10进制就是55253504,这个就是被加密的数据的长度或者大小,(个人理解)

所以我们应该是从243开始提,216+25+3

这里就是被加密的数据,所以我们使用dd命令去提

1
dd if=v3910_4325.all of=test skip=243 count=55187968 bs=1

那么我们知道的是,chacha20已经被集成在许多语言中的库里了,故我们用python去写一个

解密的脚本即可(因为原程序参数我们不知道该放什么故想着写脚本去解密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from Crypto.Cipher import ChaCha20

def do_decrypt(enc_image):
   nonce = b"UODAjyXZOzH0"
   key = b"DraytekKd5Eason3DraytekKd5Eason"
   with open(enc_image, "rb") as f:
       enc_data = f.read()
   cipher = ChaCha20.new(key=key, nonce=nonce)
   dec_data = cipher.decrypt(enc_data)
   with open(f"{enc_image}_decrypt", "wb") as f:
       f.write(dec_data)
   print("解密完成")

if __name__ == "__main__":
   filename = "test" ##文件名
   do_decrypt(filename)

解密成功,生成 test_decrypt。

参考链接

1、https://www.hexacon.fr/slides/hexacon_draytek_2022_final.pdf