起因
应ccadmin邀请,一起分析研究某厂商的虚拟机产品的注册逻辑和加固措施。
以下内容纯属做梦梦到,涉及到的名称如与现实世界雷同,纯属巧合。
下载完成后是个ova文件,使用7zip打开如下:
初见端倪
使用7zip继续打开vmdk文件,可以看到Properties中的Comment信息如下:
其中
ddb.virtualHWVersion = "7"信息表示这是一个传统的MBR引导类型镜像。具体文件如下:
很明显0.img为系统启动的
boot分区,大小为190Mb,符合boot分区的大小。其他分区文件直接打不开,使用
010Editor查看,发现为LUKS加密分区。
那么就从boot分区进行检查吧。
加密的boot分区?
之前遇到的LUKS分区加密都在vmlinuz或initrd.img中加载解密密钥,解密挂载硬盘,而这个却和常规做法不同。
分区文件:
脱出
vmlinuz和initrd.img文件进行分析,使用010Editor发现其都为高熵特征,无法识别原有的文件结构类型。
怀疑这些文件进行了某种加密/混淆处理,解密一定在系统进入BOOT启动阶段前(不然boot引导加密了,系统就无法执行下去了)。 正常系统加载流程如下:
flowchart TD
A[VMware虚拟机上电<br/>虚拟BIOS初始化硬件] --> B[BIOS读取vmdk磁盘0扇区 MBR 0x7C00]
B --> C[MBR引导代码:查找活动分区<br/>读取活动分区PBR扇区]
C --> D["GRUB stage1(PBR 512字节)"]
D --> E[加载MBR‑gap中的stage1.5<br/>获得ext4文件系统能力]
E --> F[读取/boot/grub/core.img]
F --> G[core.img加载grub.cfg,弹出GRUB菜单]
G --> H[准备读取 /boot/vmlinuz + initramfs]
这里就要去提取、分析vmdk中的引导代码了,但是这里有个取巧的方法。
数据取证?no,just the recovery key !
直接脱出0.img这个boot分区文件,使用autospy开源的数据取证软件进行加载。why? 因为要尝试恢复可能被删除的secret file。
发现
autospy部分文件显示0字节,无法有效进行提取。使用命令
sudo losetup -f -P --show 0.img 挂载该分区镜像,使用命令sudo extundelete /dev/loop0 --restore-all来尝试恢复所有的文件,其中/dev/loop0为分区镜像文件挂载后的路径。
这里只需要提取
*.mod文件,使用Ghidra对其进行分析。在
linux.mod和linux16.mod文件中找到了解密的办法。存在
rc4_setkey、rc4_encrypt_stream函数,大概可知是RC4 KSA相关加密。
可知,RC4的密钥为5位,在
DAT_0001131c中。
最终得到的
key为61 8a 63 d2 fb。解密脚本如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from pathlib import Path
# linux.mod / linux16.mod 中发现的 RC4 Key
KEY = bytes.fromhex("61 8A 63 D2 FB")
def rc4_init(key: bytes) -> list[int]:
"""
RC4 KSA
对应目标程序中的 rc4_setkey()
"""
if not key:
raise ValueError("RC4 key cannot be empty")
S = list(range(256))
j = 0
key_len = len(key)
for i in range(256):
j = (j + S[i] + key[i % key_len]) & 0xFF
S[i], S[j] = S[j], S[i]
return S
def rc4_crypt(data: bytes, key: bytes) -> bytes:
"""
RC4 PRGA + XOR
RC4 加密和解密使用完全相同的操作。
"""
S = rc4_init(key)
i = 0
j = 0
output = bytearray(len(data))
for n, byte in enumerate(data):
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) & 0xFF]
output[n] = byte ^ k
return bytes(output)
def decrypt_file(input_file: str, output_file: str):
input_path = Path(input_file)
output_path = Path(output_file)
if not input_path.is_file():
raise FileNotFoundError(f"输入文件不存在: {input_path}")
print(f"[+] Input : {input_path}")
print(f"[+] Output: {output_path}")
print(f"[+] Key : {KEY.hex(' ').upper()}")
print(f"[+] Size : {input_path.stat().st_size} bytes")
with input_path.open("rb") as f:
encrypted = f.read()
print("[+] RC4 decrypting...")
decrypted = rc4_crypt(encrypted, KEY)
with output_path.open("wb") as f:
f.write(decrypted)
print("[+] Done.")
print(f"[+] Written: {len(decrypted)} bytes")
def main():
if len(sys.argv) != 3:
print(
f"Usage:\n"
f" {sys.argv[0]} <input_file> <output_file>\n\n"
f"Example:\n"
f" {sys.argv[0]} encrypted.bin decrypted.bin"
)
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
try:
decrypt_file(input_file, output_file)
except Exception as e:
print(f"[-] Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
文章的部分内容被密码保护: