时间:2021年7月27日
binwalk -Me
一把梭哈,却发现,固件被加密了,惊不惊喜,刺不刺激。判断固件是否已经被加密
$ tree -L 1
.
├── DIR3040A1_FW112B01_middle.bin
├── DIR3040A1_FW113B03.bin
└── DIR-3040_REVA_RELEASE_NOTES_v1.13B03.pdf
$ binwalk GS108Tv3_GS110TPv3_GS110TPP_V7.0.6.3.bix
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
64 0x40 LZMA compressed data, properties: 0x5D, dictionary size: 67108864 bytes, uncompressed size: -1 bytes
$ binwalk DIR3040A1_FW113B03.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
binwalk -E
查看一个未加密固件(RAX200)和加密固件(DIR 3040)。可以看到,RAX200和DIR 3040相对比,不像后者那样直接全部是接近1了。
找到负责解密的可执行文件 仿真并解密固件 加解密固件分析(重点)
$ find . -name "*htm*" | grep -i "firmware"
./etc_ro/lighttpd/www/web/MobileUpdateFirmware.html
./etc_ro/lighttpd/www/web/UpdateFirmware.html
./etc_ro/lighttpd/www/web/UpdateFirmware_e.html
./etc_ro/lighttpd/www/web/UpdateFirmware_Multi.html
./etc_ro/lighttpd/www/web/UpdateFirmware_Simple.html
$ find . -name "*httpd*" | xargs strings | grep "firm"
strings: Warning: './etc_ro/lighttpd' is a directory
"*cgi*" | xargs strings | grep -i "firm"/bin/imgdecrypt /tmp/firmware.img find . -name
$ file bin/imgdecrypt
bin/imgdecrypt: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
$ cp $(which qemu-mipsel-static) ./usr/bin
$ sudo mount -t proc /proc proc/
$ sudo mount --rbind /sys sys/
$ sudo mount --rbind /dev/ dev/
$ sudo chroot . qemu-mipsel-static /bin/sh
BusyBox v1.22.1 (2020-05-09 10:44:01 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
/ # /bin/imgdecrypt tmp/DIR3040A1_FW113B03.bin
key:C05FBF1936C99429CE2A0781F08D6AD8
/ # ls -a tmp/
.. .firmware.orig . DIR3040A1_FW113B03.bin
/ #
type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"160 0xA0 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 23083456 bytes1810550 0x1BA076 PGP RSA encrypted session key - keyid: 12A6E329 67B9887A RSA (Encrypt or Sign) 1024b14275307 0xD9D2EB Cisco IOS microcode, for "z" binwalk .firmware.origDECIMAL HEXADECIMAL DESCRIPTION--------------------------------------------------------------------------------0 0x0 uImage header, header size: 64 bytes, header CRC: 0x7EA490A0, created: 2020-08-14 10:42:39, image size: 17648005 bytes, Data Address: 0x81001000, Entry Point: 0x81637600, data CRC: 0xAEF2B79F, OS: Linux, CPU: MIPS, image
关于固件安全开发到发布的一般流程
加解密逻辑分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // $v0
if ( strstr(*argv, "decrypt", envp) )
result = decrypt_firmare(argc, (int)argv);
else
result = encrypt_firmare(argc, argv);
return result;
}
int __fastcall decrypt_firmare(int argc, int argv)
{
int result; // $v0
const char *pubkey_loc; // [sp+18h] [-1Ch]
int i; // [sp+1Ch] [-18h]
int aes_key[5]; // [sp+20h] [-14h] BYREF
qmemcpy(aes_key, "0123456789ABCDEF", 16);
pubkey_loc = "/etc_ro/public.pem";
i = -1;
if ( argc >= 2 )
{
if ( argc >= 3 )
pubkey_loc = *(const char **)(argv + 8);
if ( check_rsa_cert((int)pubkey_loc, 0) ) // 读取公钥并进行保存RSA对象到dword_413220中
{
result = -1;
}
else
{
aes_cbc_crypt((int)aes_key); // 生成aes_key
printf("key:");
for ( i = 0; i < 16; ++i )
printf("%02X", *((unsigned __int8 *)aes_key + i));// 打印出key
puts("r");
i = actual_decrypt(*(_DWORD *)(argv + 4), (int)"/tmp/.firmware.orig", (int)aes_key);
if ( !i )
{
unlink(*(_DWORD *)(argv + 4));
rename("/tmp/.firmware.orig", *(_DWORD *)(argv + 4));
}
RSA_free(dword_413220);
result = i;
}
}
else
{
printf("%s <sourceFile>rn", *(const char **)argv);
result = -1;
}
return result;
}
int __fastcall actual_decrypt(int img_loc, int out_image_loc, int aes_key)
{
int image_fp; // [sp+20h] [-108h]
int v5; // [sp+24h] [-104h]
_DWORD *MEM; // [sp+28h] [-100h]
int OUT_MEM; // [sp+2Ch] [-FCh]
int file_blocks; // [sp+30h] [-F8h]
int v9; // [sp+34h] [-F4h]
int i; // [sp+38h] [-F0h]
int out_image_fp; // [sp+3Ch] [-ECh]
int data1_len; // [sp+40h] [-E8h]
int data2_len; // [sp+44h] [-E4h]
_DWORD *IN_MEM; // [sp+48h] [-E0h]
char hash_buf[68]; // [sp+4Ch] [-DCh] BYREF
int image_info[38]; // [sp+90h] [-98h] BYREF
image_fp = -1;
out_image_fp = -1;
v5 = -1;
MEM = 0;
OUT_MEM = 0;
file_blocks = -1;
v9 = -1;
// 这个hashbuf用于存储SHA512的计算结果,在后面比较会一直被使用到
memset(hash_buf, 0, 64);
data1_len = 0;
data2_len = 0;
memset(image_info, 0, sizeof(image_info));
IN_MEM = 0;
// 通过stat函数读取加密固件的相关信息写入结构体到image_info,最重要的是文件大小
if ( !stat(img_loc, image_info) )
{
// 获取文件大小
file_blocks = image_info[13];
// 以只读打开加密固件
image_fp = open(img_loc, 0);
if ( image_fp >= 0 )
{
// 将加密固件映射到内存中
MEM = (_DWORD *)mmap(0, file_blocks, 1, 1, image_fp, 0);
if ( MEM )
{
// 以O_RDWR | O_NOCTTY获得解密后固件应该存放的文件描述符
out_image_fp = open(out_image_loc, 258);
if ( out_image_fp >= 0 )
{
v9 = file_blocks;
// 比较写入到内存的大小和固件的真实大小是否相同
if ( file_blocks - 1 == lseek(out_image_fp, file_blocks - 1, 0) )
{
write(out_image_fp, &unk_402EDC, 1);
close(out_image_fp);
out_image_fp = open(out_image_loc, 258);
// 以加密固件的文件大小,将待解密的固件映射到内存中,返回内存地址OUT_MEM
OUT_MEM = mmap(0, v9, 3, 1, out_image_fp, 0);
if ( OUT_MEM )
{
IN_MEM = MEM; // 重新赋值指针
// 检查固件的Magic,通过查看HEX可以看到加密固件的开头有SHRS魔数
if ( check_magic((int)MEM) ) // 比较读取到的固件信息中含有SHRS
{
// 获得解密后固件的大小
data1_len = htonl(IN_MEM[2]);
data2_len = htonl(IN_MEM[1]);
// 从加密固件的1756地址起,计算data1_len个字节的SHA512,也就是解密后固件大小的消息摘要,并保存到hash_buf
sub_400C84((int)(IN_MEM + 0x6dc), data1_len, (int)hash_buf);
// 比较原始固件从156地址起,64个字节大小,和hash_buf中的值进行比较,也就是和加密固件头中预保存的真实加密固件大小的消息摘要比较
if ( !memcmp(hash_buf, IN_MEM + 0x9c, 64) )
{
// AES对加密固件进行解密,并输出到OUT_MEM中
// 这个地方也可以看出从加密固件的1756地址起就是真正被加密的固件数据,前面都是一些头部信息
// 函数逻辑比较简单,就是AES加解密相关,从保存在固件头IN_MEM + 0xc获取解密密钥
sub_40107C((int)(IN_MEM + 0x6dc), data1_len, aes_key, IN_MEM + 0xc, OUT_MEM);
// 计算解密后固件的SHA_512消息摘要
sub_400C84(OUT_MEM, data2_len, (int)hash_buf);
// 和存储在原始加密固件头,从92地址开始、64字节的SHA512进行比较
if ( !memcmp(hash_buf, IN_MEM + 0x5c, 64) )
{
// 获取解密固件+aes_key的SHA512
sub_400D24(OUT_MEM, data2_len, aes_key, (int)hash_buf);
// 和存储在原始固件头,从28地址开始、64字节的SHA512进行比较
if ( !memcmp(hash_buf, IN_MEM + 0x1c, 64) )
{
// 使用当前文件系统内的公钥,通过RSA验证消息摘要和签名是否匹配
if ( sub_400E78((int)(IN_MEM + 0x5c), 64, (int)(IN_MEM + 0x2dc), 0x200) == 1 )
{
if ( sub_400E78((int)(IN_MEM + 0x9c), 64, (int)(IN_MEM + 0x4dc), 0x200) == 1 )
v5 = 0;
else
v5 = -1;
}
else
{
v5 = -1;
}
}
else
{
puts("check sha512 vendor failedr");
}
}
else
{
printf("check sha512 before failed %d %drn", data2_len, data1_len);
for ( i = 0; i < 64; ++i )
printf("%02X", (unsigned __int8)hash_buf[i]);
puts("r");
for ( i = 0; i < 64; ++i )
printf("%02X", *((unsigned __int8 *)IN_MEM + i + 92));
puts("r");
}
}
else
{
puts("check sha512 post failedr");
}
}
else
{
puts("no image matic foundr");
}
}
}
}
}
}
}
if ( MEM )
munmap(MEM, file_blocks);
if ( OUT_MEM )
munmap(OUT_MEM, v9);
if ( image_fp >= 0 )
close(image_fp);
if ( image_fp >= 0 )
close(image_fp);
return v5;
}
概述DIR 3040的固件组成以及解密验证逻辑
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
小 结
参考链接
-
Breaking the D-Link DIR3060 Firmware Encryption
https://0x00sec.org/t/breaking-the-d-link-dir3060-firmware-encryption-recon-part-1/21943
-
D-Link DIR 3040从信息泄露到RCE
https://genteeldevil.github.io/2021/07/23/D-Link%20DIR%203040%E4%BB%8E%E4%BF%A1%E6%81%AF%E6%B3%84%E9%9C%B2%E5%88%B0RCE/
往 期 热 门
(点击图片跳转)
D-Link DIR 3040 从信息泄露到 RCE
VPN 原理以及实现
CVE-2021-33514:Netgear 多款交换机命令注入漏洞
本文始发于微信公众号(Seebug漏洞平台):加密固件之依据老固件进行解密
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论