DIR-882路由固件加解密
VSole2021-10-17 06:53:38
固件下载
下载固件https://support.dlink.com/resource/PRODUCTS/DIR-882/REVA/DIR-882_REVA_FIRMWARE_v1.10B02.zip,这里下载的版本为DIR-882
解压缩可以看到给了两个版本一个为加密版,一个为未加密版。
加密版
未加密版
固件解包
对未加密的版本进行解包
binwalk -Me DIR882A1_FW104B02_Middle_FW_Unencrypt.bin
在_DIR882A1_FW104B02_Middle_FW_Unencrypt.bin.extracted/_A0.extracted/_8AB758.extracted/cpio-root
目录下可以看到目录结构
$ tree -d . . ├── bin ├── dev │ └── pts ├── etc ├── etc_ro │ ├── l7-protocols │ │ ├── not_commit │ │ └── untested │ ├── lighttpd │ │ ├── lib │ │ └── www │ │ └── web │ │ ├── captcha │ │ ├── config │ │ │ └── deviceinfo │ │ ├── css │ │ ├── hnap │ │ ├── HNAP1 │ │ ├── image │ │ ├── info │ │ └── js │ │ ├── bootstrap │ │ │ ├── css │ │ │ └── js │ │ ├── ClientList │ │ ├── configuration │ │ ├── ipv6 │ │ ├── localization │ │ ├── SOAP │ │ └── wizard │ ├── linuxigd │ ├── onetouch │ │ └── scripts │ ├── ppp │ │ ├── 3g │ │ ├── peers │ │ └── plugins │ ├── usb │ ├── web │ ├── wifi │ ├── Wireless │ │ ├── iNIC │ │ ├── RT2860AP │ │ ├── RT2870STA │ │ ├── RT61AP │ │ └── WIFI3 │ ├── wlan │ └── xml ├── home ├── lib │ ├── firmware │ ├── ipsec │ └── modules │ └── 3.10.14+ │ └── kernel │ └── net │ ├── ipv4 │ │ └── netfilter │ ├── nat │ │ └── hw_nat │ └── netfilter ├── media ├── mnt ├── private ├── proc ├── sbin ├── share │ ├── man │ │ ├── man1 │ │ ├── man5 │ │ └── man8 │ └── udhcpc ├── sys ├── tmp ├── usr │ ├── bin │ ├── codepages │ ├── lib │ │ └── pppd │ │ └── 2.4.5 │ ├── local │ │ └── ssl │ └── sbin ├── var └── www ├── rsstest ├── rssui │ ├── img │ └── public └── tmp -> /var/tmp 90 directories
寻找解密点
- 尝试在后端
lighttpd
中寻找相关字符串。
$ sudo find . -name "*httpd*" | xargs strings | grep "firm" strings: Warning: './etc_ro/lighttpd' is a directory
- 没有找到。
- 尝试在
CGI
中寻找相关字符串。
$ sudo find . -name "*cgi*" | xargs strings | grep "firm" /tmp/firmware.img /bin/imgdecrypt /tmp/firmware.img /tmp/firmware.img mtd_write -r -w write /tmp/firmware.img Kernel & rm /tmp/firmware.img rm /tmp/.firmware.orig wget -O %s "http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s" rm -f /tmp/firmware.img & http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s /tmp/firmware.img /bin/imgdecrypt /tmp/firmware.img /tmp/firmware.img mtd_write -r -w write /tmp/firmware.img Kernel & rm /tmp/firmware.img rm /tmp/.firmware.orig wget -O %s "http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s" rm -f /tmp/firmware.img & http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s
- 可以看到之中存在
/bin/imgdecrypt
,疑似解密的文件。
分析程序
$ file imgdecrypt imgdecrypt: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
可以看出此程序解密程序
int __fastcall decrypt_firmare(int a1, int a2) { int result; // $v0 const char *v3; // [sp+18h] [-1Ch] int i; // [sp+1Ch] [-18h] int aes_key[5]; // [sp+20h] [-14h] BYREF qmemcpy(aes_key, "0123456789ABCDEF", 16); // aes_en_key v3 = "/etc_ro/public.pem"; i = -1; if ( a1 >= 2 ) { if ( a1 >= 3 ) v3 = *(const char **)(a2 + 8); if ( sub_40215C((int)v3, 0) ) // 检查证书 { result = -1; } else { sub_402554((int)aes_key); // aes加密 printf("key:"); for ( i = 0; i < 16; ++i ) printf("%02X", *((unsigned __int8 *)aes_key + i));// C05FBF1936C99429CE2A0781F08D6AD8 puts("\r"); i = sub_401780(*(_DWORD *)(a2 + 4), (int)"/tmp/.firmware.orig", (int)aes_key);// 解密函数 if ( !i ) { unlink(*(_DWORD *)(a2 + 4)); rename("/tmp/.firmware.orig", *(_DWORD *)(a2 + 4)); } RSA_free(dword_4131C0); // RSA 对象地址 result = i; } } else { printf("%s \r", *(const char **)a2); result = -1; } return result; }
sub_402554函数分析
int __fastcall sub_40108C(int a1, int a2, int a3, int *a4, int a5) { int v5; // $a0 int v6; // $v1 int v7; // $v0 char v9[244]; // [sp+24h] [-108h] BYREF int v10[5]; // [sp+118h] [-14h] BYREF int v12; // [sp+134h] [+8h] v12 = a2 + (1 - (a2 & 0xF)) * ((a2 & 0xF) != 0); AES_set_decrypt_key(a3, 128, v9); v5 = a4[1]; v6 = a4[2]; v7 = a4[3]; v10[0] = *a4; v10[1] = v5; v10[2] = v6; v10[3] = v7; AES_cbc_encrypt(a1, a5, v12, v9, v10, 0); return 0; }
sub_401780函数分析
int __fastcall sub_401780(int file, int a2, int aes_key) { int file_fp; // [sp+20h] [-108h] int v5; // [sp+24h] [-104h] _DWORD *v6; // [sp+28h] [-100h] int v7; // [sp+2Ch] [-FCh] int file_size; // [sp+30h] [-F8h] int v9; // [sp+34h] [-F4h] int i; // [sp+38h] [-F0h] int v11; // [sp+3Ch] [-ECh] int v12; // [sp+40h] [-E8h] int v13; // [sp+44h] [-E4h] _DWORD *firmware_fp; // [sp+48h] [-E0h] char firmware_de[68]; // [sp+4Ch] [-DCh] BYREF int file_buf[38]; // [sp+90h] [-98h] BYREF file_fp = -1; v11 = -1; v5 = -1; v6 = 0; v7 = 0; file_size = -1; v9 = -1; memset(firmware_de, 0, 64); v12 = 0; v13 = 0; memset(file_buf, 0, sizeof(file_buf)); firmware_fp = 0; if ( !stat(file, file_buf) ) { file_size = file_buf[13]; file_fp = open(file, 0); if ( file_fp >= 0 ) { v6 = (_DWORD *)mmap(0, file_size, 1, 1, file_fp, 0);// 固件映射到内存 if ( v6 ) { v11 = open(a2, 0x102); if ( v11 >= 0 ) { v9 = file_size; if ( file_size - 1 == lseek(v11, file_size - 1, 0) )// 比较写入内存文件和固件文件的大小 { write(v11, &unk_402E8C, 1); close(v11); v11 = open(a2, 0x102); v7 = mmap(0, v9, 3, 1, v11, 0); // 以加密固件大小将待解密的固件映射到内存 if ( v7 ) { firmware_fp = v6; if ( sub_400C40((int)v6) ) // 判断固件开头是否为'SHRS' { v12 = htonl(firmware_fp[2]); // 13265600 v13 = htonl(firmware_fp[1]); // 13265595 sub_400C94((int)(firmware_fp + 0x1B7), v12, (int)firmware_de);// 从偏移0x6DC计算长度为1326560的SHA512,这里反编译错误($v0+0x6dc) if ( !memcmp(firmware_de, firmware_fp + 0x27, 64) )// 比较消息摘要,这里反编译错误($v0+0x9c) { aes((int)(firmware_fp + 0x1B7), v12, aes_key, firmware_fp + 3, v7);// aes 解密,key为偏移0xC处,这里反编译错误($v0+0xc) sub_400C94(v7, v13, (int)firmware_de);// 计算解密后的消息摘要 if ( !memcmp(firmware_de, firmware_fp + 0x17, 64) )// 这里反编译错误($v0+0x5c) { sub_400D34(v7, v13, aes_key, (int)firmware_de);// 计算解密固件+aes_key的SHA512 if ( !memcmp(firmware_de, firmware_fp + 7, 0x40) )// 这里反编译错误($v0+0x1c) { // rsa验证摘要 if ( sub_400E88((int)(firmware_fp + 0x17), 64, (int)(firmware_fp + 0xB7), 0x200) == 1 )// 这里反编译错误($v0+0x2dc) { if ( sub_400E88((int)(firmware_fp + 0x27), 64, (int)(firmware_fp + 0x137), 0x200) == 1 )// 这里反编译错误($v0+0x4dc) v5 = 0; else v5 = -1; } else { v5 = -1; } } else { puts("check sha512 vendor failed\r"); } } else { printf("check sha512 before failed %d %d\r", v13, v12); for ( i = 0; i < 64; ++i ) printf("%02X", (unsigned __int8)firmware_de[i]); puts("\r"); for ( i = 0; i < 64; ++i ) printf("%02X", *((unsigned __int8 *)firmware_fp + i + 92)); puts("\r"); } } else { puts("check sha512 post failed\r"); } } else { puts("no image matic found\r"); } } } } } } } if ( v6 ) munmap(v6, file_size); if ( v7 ) munmap(v7, v9); if ( file_fp >= 0 ) close(file_fp); if ( file_fp >= 0 ) close(file_fp); return v5; }
反编译错误分析,函数sub_400C94,其余函数同理
.text:00401AF4 lw $gp, 0x128+var_110($sp) .text:00401AF8 sw $v0, 0x128+var_E4($sp) .text:00401AFC lw $v0, 0x128+firmware_fp($sp) .text:00401B00 nop .text:00401B04 addiu $v1, $v0, 0x6DC ; $v1为参数,$v0存放firmware_fp,所以应为(firmware_fp+0x6DC) .text:00401B08 addiu $v0, $sp, 0x128+firmware_de
解密
对程序进行仿真运行
$ cp /usr/bin/qemu-mipsel-static . $ sudo chroot . ./qemu-mipsel-static /bin/sh
运行解密程序
# ./bin/imgdecrypt /home/DIR882A1_FW110B02.bin
查看解密效果
$ sudo binwalk DIR882A1_FW110B02.bin
可以看到成功解密

VSole
网络安全专家