【技术分享】一种 SonicWall nsv 虚拟机的解包方法

admin 2022年2月10日01:06:58评论130 views字数 3969阅读13分13秒阅读模式

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

 最近 SonicWall 官方发布了影响 SonicOS 的数个缓冲区溢出漏洞,编号 CVE-2021-20048、CVE-2021-20046 等。在研究复现过程中发现针对此设备的解包思路比较有趣,所以在这里分享给大家。

【技术分享】一种 SonicWall nsv 虚拟机的解包方法
【技术分享】一种 SonicWall nsv 虚拟机的解包方法

固件获取


该设备固件前段时间可以从 mysonicwall 网站上获取,但目前 sonicwall 下架了 nsv200(SonicOS GEN6) 相关固件,所以这里提供旧版固件的网盘链接(提取码:1a30),感兴趣的朋友可以自行下载。

 

【技术分享】一种 SonicWall nsv 虚拟机的解包方法
【技术分享】一种 SonicWall nsv 虚拟机的解包方法

安装和配置


用 VMWare 打开 ova 文件,一路确定并等待开机完成。虚拟机会从 DHCP 自动获取到 IP 地址,访问可以看到登录界面:

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

默认用户名和密码:admin:password

 

【技术分享】一种 SonicWall nsv 虚拟机的解包方法
【技术分享】一种 SonicWall nsv 虚拟机的解包方法

解包


启动分析

sonicwall nsv 的默认终端是一个自定义 CLI 程序,不提供 Linux root shell,所以想要得到关键文件,必须解包固件。

查看虚拟机的启动过程,输出很少的信息之后就会出现 sonicwall logo

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

厂商使用这种方式遮蔽了系统启动时输出的信息,只能直接从固件下手。

找到虚拟机的 vmdk 磁盘文件,使用 7-zip 打开,可以看到内部有数个分区文件

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

这里也可尝试直接将磁盘挂载到已有的 Linux 虚拟机上,虚拟机设置 -> 添加 -> 硬盘 -> SCSI -> 使用现有虚拟磁盘 -> 选择 sonicwall vmdk 文件 -> 保持现有格式

挂载之后分区状态如下

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

系统识别到 4 个分区,但是全部加密,猜测文件系统就位于其中一个分区内。

ls 查看 /dev/ 目录下信息,发现还有 sdb1、2 等没有在文件管理器显示出来,尝试手动挂载

sudo mount /dev/sdb1 ./test

第一个分区存放了 BOOT 相关文件,并有一个 coreos 目录,可能是基于开源项目制作的。在 boot 分区中我们发现虚拟机使用了 GRUB 引导系统启动,GRUB 是一个常见的启动引导程序,它具有命令行功能。根据官方文档,当系统引导失败时会自动进入 GRUB 救援模式,可以执行一些和文件系统相关的操作。

加密方式

明确了后续思路,我们还需要知道分区的加密方式是什么。

用 7-zip 解压出其中一个较小的加密分区 OEM.img,然后加载到 16 进制编辑器看到文件开头是 LUKS

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

LUKS 是一种磁盘加密标准,nsv 设备使用 LUKS 实现了全盘加密。

网络上存在一些 LUKS 全盘加密的解密方法,可以进入 GRUB 的救援模式命令行并使用 cryptomount 命令&密码解密。但目前还不知道加密密码是什么,需要研究一下 LUKS 的加密逻辑。

既然系统可以正常启动,说明在引导阶段就通过某种方式解密了各个分区。参考分区的加密方式,密码应该被保存在 luks 相关的模块中。

关于 LUKS 解密的具体逻辑可参考它的项目源码,解密关键函数是 grub_crypto_pbkdf2,原型如下

grub_crypto_pbkdf2 (const struct gcry_md_spec *md,
const grub_uint8_t *P, grub_size_t Plen,
const grub_uint8_t *S, grub_size_t Slen,
unsigned int c,
grub_uint8_t *DK, grub_size_t dkLen)

其中第二个参数是用于解密分区的密钥。

在 BOOT 分区搜索正好可以找到 luks.mod 模块,将它解压,然后用 IDA 分析,通过交叉引用找到解密相关代码

grub_real_dprintf("disk/luks.c", 246LL, "luks", "Trying keyslot %dn", k);
v32 = grub_crypto_pbkdf2(*(a2 + 88), a4, v43, v31, 32LL, _byteswap_ulong(*(v31 - 1)), v51, v38);

我们看到解密时会输出 log 信息 “Trying keyslot xx”,而变量 a4 应该就是 key。

提取密钥

为了解密分区,需要通过某种手段获取模块中的密钥,而密钥又保存在 luks 模块中,所以具体思路是进入 GRUB 命令行,然后加载和解密相关的各个模块,接着利用调试器附加到 luks.mod 上,尝试中断在 grub_crypto_pbkdf2 函数,并提取其参数。

为了能完成上述操作,首先我们需要一种能够在启动虚拟机后从外部修改文件的方式。这一点可利用 qemu-nbd 实现。

其次还要支持调试器,用于调试 luks 相关模块。可以利用 Linux 下虚拟化平台 QEMU/KVM 虚拟机实现。

工具配置

安装 qemu-utils 和 KVM & VMM

sudo apt install qemu-utils
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager

转换镜像

vmware 使用的是 vmdk 虚拟磁盘镜像,为了使用 qemu-nbd 修改镜像,首先要把 vmdk 转换成 qcow2 格式。

qemu-img convert -f vmdk ./image.vmdk -O qcow2 sonicwall.qcow2

配置虚拟机

注:虚拟机需要开启 Intel-VTx 或 AMD-V 支持。

将转换之后的镜像使用 qemu-nbd 挂载到本地

sudo modprobe nbd max_part=8 # 加载 nbd 模块
sudo qemu-nbd --connect=/dev/nbd0 ./sonicwall.qcow2 # 挂载镜像
sudo fdisk /dev/nbd0 -l # 查看分区信息

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

此时已经成功挂载。

然后导入挂载的镜像到 KVM 虚拟机,在 VMM 中添加一个虚拟机,导入现有磁盘映像 -> 存储路径:/dev/nbd0

配置调试接口

和 vmware 一样,KVM 也支持对虚拟机添加调试接口,在终端输入命令

virsh edit <虚拟机名称>

进入命令行模式,随意选择一个编辑器修改配置文件。

修改 <domain type=’kvm’> 为

<domain type='kvm'
xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0' >
<qemu:commandline>
<qemu:arg value='-gdb'/>
<qemu:arg value='tcp::12345'/>
</qemu:commandline>

这样当虚拟机启动之后就可以用 gdb 附加到 localhost:12345 对其进行调试了。

进入救援模式

我们重命名 boot 分区中的一些文件,当 GRUB 引导时就会由于找不到关键文件而进入救援模式。

首先挂载 nbd 中的 BOOT 分区到本地目录。

sudo mount /dev/nbd0p1 ./test

然后进入 /coreos/grub 目录,并重命名其中的三个目录

mv i386-pc i386-pc-bak
mv x86_64-efi x86_64-efi-bak
mv x86_64-xen x86_64-xen-bak
sync
sync

启动虚拟机,此时系统会自动进入救援模式。

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

这里可用 TAB 键看看有哪些可用的命令。

ls 命令显示分区列表

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

使用解密功能

解密分区命令为 cryptomount,不过在使用之前需要恢复 BOOT 分区中的目录名。

mv ./i386-pc-bak i386-pc
mv x86_64-efi-bak x86_64-efi
mv x86_64-xen-bak x86_64-xen
sync
sync

使用 cryptomount 尝试解密 (hd0,gpt3) 报错

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

这是因为相关模块没加载,需要手动将部分模块加载一遍,经过测试,至少要加载 gcry_rijndael、luks、gcry_sha256 三个模块

insmod luks
insmod gcry_rijndael
insmod gcry_sha256

然后再对 gpt3 分区进行解密

cryptomount (hd0,gpt3)

这样解密成功,还可以加载 ext2 模块并用 ls 命令查看文件系统结构

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

调试 luks 模块

在救援模式下已经成功解密分区,但无法将文件提取出来,所以需要调试 LUKS 模块取得相关密钥,再挂载镜像到普通的 Linux 系统解密。

重新启动虚拟机,然后另开一个终端,启动 gdb,并附加到当前虚拟机

target remote 0:12345
continue

加载 gcry_rijndael 等模块,之后通过搜索 LUKS.mod 中特殊字符串来定位关键代码

find 0,0x8000000,"Trying keyslot %dn"

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

这里找到两个结果,依次从每个地址附近查找关键代码,例如进行以下搜索

x /30i 0x7f56fb7 - 0xbf0

继续向下找到关键函数开头

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

根据指令偏移量找到关键位置

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

这些指令序列和 IDA 对应位置相同

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

在 call 指令下断点,然后让系统继续运行,接着在命令行中解密 gpt3 分区,gdb 将会断下

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

此时 rcx 寄存器值表示密钥的长度,而在 rdx 寄存器指向的地址中就能找到解密分区使用的密钥

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

【技术分享】一种 SonicWall nsv 虚拟机的解包方法
【技术分享】一种 SonicWall nsv 虚拟机的解包方法

解密文件


我们已经获取解密 gpt3 分区的密钥,接下来可以在本地 Linux 系统中尝试解密分区。

将 vmdk 重新挂载到本地 Linux 系统,并把调试得到的密钥保存到一个文件中,然后执行以下命令尝试解密。

cat key_p3 | sudo cryptsetup luksOpen /dev/sdb3 p3 # 解密分区
dd if=/dev/mapper/p3 of=./part3 # 某些情况下挂载分区会报错,我们将分区拷贝到 Windows 下解压

将生成的 part3 分区文件解压即可得到解密后的文件系统

【技术分享】一种 SonicWall nsv 虚拟机的解包方法

同理,对于剩下的几个加密分区也能通过同样的方式实现解密。

 

【技术分享】一种 SonicWall nsv 虚拟机的解包方法
【技术分享】一种 SonicWall nsv 虚拟机的解包方法

参考文章


qemu-nbd技术分析

Decrypt and mount LUKS volume in GRUB rescue mode

Linux的磁盘加密技术LUKS

原文始发于微信公众号(安全客):【技术分享】一种 SonicWall nsv 虚拟机的解包方法

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年2月10日01:06:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【技术分享】一种 SonicWall nsv 虚拟机的解包方法http://cn-sec.com/archives/768684.html

发表评论

匿名网友 填写信息