破解D-Link DIR3060固件加密-侦测-第1部分

2020-08-20 02:39:06

最近,我们发现D-Link路由器上的一些固件样本无法正确解包。幸运的是,我们得到了一种更老、更便宜但类似的设备(DIR882),我们能够更仔细地分析它。我们的目标是找到一种方法来减轻为防止篡改和静态分析而实施的固件加密。本系列重点介绍了编写自定义解密例程的结果和必要步骤,该例程实际上也适用于许多其他模型,但稍后会详细介绍。首先让我们来看看这个问题。

可从此处下载最新的D-Link 3060固件(撰写本文时)。我将检查10/22/19年10月22日发布的v1.02B03。简要的初步分析显示如下:

>;md5sum DIR-3060_RevA_Firmware111B01.bin86e3f7baebf4178920c767611ec2ba50 DIR3060A1_FW102B03.bin>;文件DIR-3060_RevA_Firmware111B01.binDIR3060A1_FW102B03.bin:数据>;Binwaldir-3060REVA_Firmware111B01.binDECIMAL十六进制DESCRIPTION---->;HD-n128DIR3060_REVA_Firmware111B01.bin00000000 53 48 52 53 01 13 1f 9e 01 13 1f a0 67 c6 69 73|shrs.g.is|00000010 51 ff 4a ec 29 cd ba ab f2 fb E3 46 2e 97 E7 b1|q.j.).F...|00000020 56 90 b9 16 f8 0C 77 b8 bf 13 17 46 7b E3 c5 9c|V.。13 65 04|9.Yku.(.3.E.|00000040 61 de 2d 56 6f 38 d7 EB 43 9d D9 10 EB 38 20 88|a.-vo8..c...8.|00000050 1f 21 0E 41 88 ff ee aa 85 46 0E ee d7 f6 23 04|.A.F...#.|00000060 fa 29 db 31 9c 5f 55 68 12 2e 32 c3 14 5c 0A 53|.)1_Uh..2.\。C0 de 1c f3 8b 67 1D e6 31 36|..$..Y.g.16|00000080。

因此,我们从file命令得到的只是手头有某种形式的(二进制)数据文件,这不是很有用。我们的GOTO初始侦察选择:binwalk也无法识别固件映像中的任何文件部分,甚至不能识别任何误报。最后,前128个字节的十六进制转储显示的似乎是从偏移量0x0开始的随机数据。所有这些都是加密图像的指示符,熵分析可以确认这一点:

熵曲线上没有一滴水,没有给我们留下任何空间来提取关于目标的任何类型的信息。

由于我们不愿以大约200美元的价格购买D-Link DIR 3060,我们检查了D-Link的类似型号,这些型号都是价格较低的,目的是找到至少一种部署了相同加密方案的替代方案。最后,我们遇到了D-Link DIR882,它的价格要便宜得多。

另外,即使当我们找不到类似的加密方案时,查看不同的固件标头也可以提供一些提示,说明它们的GOTO机制是什么样子的,它们的固件是什么样子的。

当我们偶然发现DIR 882时,我们检查了在02/20/20发布的固件v1.30B10,它显示出与老大哥DIR3060相同的行为,包括几乎为1的恒定熵。被投资的读者可能会注意到,开头的4字节序列是相同的";SHRS";。我们稍后会讲到那个。

>;md5sum DIR_882_FW120B06.BIN89a80526d68842531fe29170cbd596c3 DIR_882FW120B06.BIN>;文件DIR_882FW120B06.BINDIR_882FW120B06.BIN:DATA&gT;BINDECIMAL十六进制DESCRIPTION---->;HD-n128 DIR882_FW120B06.BIN00000000 53 48 52 53 00 d1 d9 a6 00 d1 d9 b0 67 c6 69 73|shrs.g.is|00000010 51 ff 4a ec 29 cd ba ab f2 fb e3 46 fd a7 4d 06|q.j.).f..m|00000020 a4 66 e6 ad bf c4 9d 13 f3 f7 d1 12 98 6b 2a 35|.f.k*5|。Fb|.m:*%Z...|00000040 2a 17 7a b2 99 04 60 66 EB c2 58 98 82 74 08 E3|*.z...`f..x..t..|00000050 54 1e e2 51 44 42 E8 d6 8e 46 6e 2c 16 57 d3 0b|T..qdb...fn,.w..|00000060 07 d7 7c 9e 11 EC 72 1D fb 87 a2 5b 18 EC 53 82|..|...r....[..S.|00000070 85 b9 84 39 b6 b4 dd 85 de f0 28 3D 36 0E be aa|...9.(=6...|00000080。

这个固件为我们证实的另一件事是,2020年初仍在使用相同的加密方案。

一旦我们获得了DIR882,我们就可以进入设备上的串行控制台,并查看文件系统,以寻找处理固件更新加/解密的任何线索和候选者(连接到UART控制台不在本文的讨论范围内,也不是特别有趣,因为除了连接4根电缆外,它不涉及任何硬件黑客攻击。)。我们很快就找到了合适的候选人:

>;文件imgdeccryptimgdeccrypt:ELF 32位LSB可执行文件,MIPS,MIPS32 rel2版本1(SYSV),动态链接,解释器/lib/ld-,剥离>;md5sum imgdeccrypta5474af860606f035e4b84bd31fc17a1 imgdeccrypt。

因为我们只对这个特殊的二进制文件感兴趣,所以我们以最残酷的方式将其转储:

在将输出复制到本地机器并将base64转换回二进制之后,我们可以开始仔细查看了!

我们在上面已经看到,我们正在处理一个用于MIPS的32位ELF二进制文件,它是动态链接(如预期的那样)并被剥离的。让我们看看古老的琴弦能为我们做些什么:

>;String-n 10 imgdeccrypt|uniq/lib/ld-uClibc.so.0[...]SHA512_InitSHA512_UpdateSHA512_FinalRSA_verifyAES_set_encrypt_keyAES_cbc_encryptAES_set_decrypt_keyPEM_write_RSAPublicKeyOPENSSL_add_all_algorithms_noconfPEM_read_RSAPublicKeyPEM_read_RSAPrivateKeyRSA_generate_keyEVP_aes_256_cbcPEM_write_RSAPrivateKeydecrypt_firmareencrypt_firmare[...]libcrypto.so.1.0.0[...]no映像马季奇找到检查SHA512POST失败在失败前检查SHA512%d%d检查SHA512供应商失败静态常量字符*PUBKEY_n=";%s";;静态常量char*pubkey_e=";%s";读取rsa私钥失败,可能密钥密码不正确/etc_ro/public.pem%s<;sourceFile>;/tmp/.firmware.orig0123456789ABCDEF%s源文件目标文件[...]

甜!。里面还有很多有用的东西。我刚刚删除了";[...]";指示的垃圾行。最值得注意的是以下几点:

到目前为止,我们已经学到了许多有趣的东西,这些东西应该会对我们以后的工作有所帮助!

(可以毫无问题地访问DIR 882上的UART串行控制台)。

接下来,我们将深入研究imgdeccrypt二进制文件的静态分析,以了解固件更新是如何控制的!但在此之前,对于那些对MIPS32汇编语言感到有点生疏/不熟悉的人,这里有一个简短的入门教程。

你们中的大多数人很可能熟悉x86/x86_64反汇编,所以这里有一些关于MIPS如何操作以及它与x86世界有何不同的一般规则。首先,有两个调用约定(O32与N32/N64)。我将讨论O32这款,因为它似乎是周围最常见的一款。深入讨论所有这些都超出了本文的范围!

在MIPS32中,您可以使用32个寄存器。O32调用约定将它们定义如下:

+---+|名称|编号|用法|+。-+|$0|$0|始终为0,对其的写入将被丢弃。|+----+|$at|$1|汇编程序临时寄存器(伪实例)|+。-+|$v0|+----+|$v1|$2─$3|函数返回/表达式求值$a0─$a3|$4─$7|函数参数,其余在堆栈中的是|+----+|$t0─$t7|$8─$15|临时寄存器|+。-+|$s0|+----─$s7|$16─$23|已保存的临时寄存器。+|$t8─$t9|$24─$25|临时寄存器|+----+|$k0─$k1|$26─$27|为内核保留|+。-+|$GP|$28|全局指针|+-。-+|$sp|$29|堆栈指针|+----+|$fP|$30|帧指针|+。-+|$ra|$31|回邮地址|+---+。

前四个函数参数移到$a0-$a3中,其余的放在堆栈顶部

函数返回放在$v0中,当有第二个返回值时,最终放在$v1中。

通过跳转与链接(JAL)或跳转与链接寄存器(JALR)执行函数调用时,返回地址存储在$ra寄存器中。

$sx寄存器在过程调用之间保留(子例程可以使用它们,但必须在返回之前恢复它们)。

$GP指向静态数据段中64k内存块的中间。

LEAF:不要调用任何其他子例程,也不要使用堆栈上的任何内存空间。因此,它们不会构建堆栈帧(因此不需要更改$sp)。

带数据的叶:与叶相同,但它们需要堆栈空间,例如:用于局部变量。它们将推送堆栈帧,但可以省略它们不需要的堆栈帧部分。

非叶:它们将调用其他子例程。这些设备很可能具有完整的堆栈框架。

在带有PIC$t9的Linux上,应该包含被调用函数的地址

++-++-+|+-++->;堆栈|FRAME|+-++|+-|本地数据x─1|+--+|+-+|+-|+-|+。|本地数据0|+-+||空||堆栈|+-+|增长||返回值||方向|+-|已保存的注册表─1|+。堆栈|+-+|框架||已保存的注册表0|+-+|arg n─1|+-+|+-+||arg 4。+-+||参数3|+-+|参数2|+-+|参数1|+-+||参数0||v。-++-+||v。

有很多非常常见的操作,如果你已经熟悉了其他汇编语言,你很快就会理解的。以下是精选的几个示例,为您提供本系列第2部分的先机:

+-+---+-+。-+|助记|全名|语法|操作|+-+---+-。----+---+|Add|Add(带溢出)|Add$a,亿美元,$c|$a=$b+$c|+---+-+---+-+。-+|ADDI|立即添加(带溢出)|ADDI$a,亿美元,IMM|$a=$b+IMM|+-+---+-+。-+|ADDIU|添加立即无签名(无溢出)|ADDU$a,亿美元,IMM|参见ADDI|+-+---+-+。-+|ADDU|添加无签名(无溢出)|ADDU$a,亿美元,$c|请参阅添加|+---+-+---+-+。-+|和*|按位AND|和$a,$b,$c|$a=$b&;$c|+-+---+-+。-+|B**|无条件偏移的分支|b偏移|转到偏移|+---+-+。-+-+---+|BEQ|EQUAL|BEQ$a上的分支,亿美元,偏移量|如果$a==$t转到偏移量|+---+-+---+-+。-+|BEQZ|BEQZ|分支开等于零|BEQZ$a,偏移量|如果$a==0转到偏移量|+---+-+---+-+。-+|bgez|分支大于或等于零|bgez$a,偏移|如果$a>;=0转到偏移量|+---+-+---+-+。-+|bgezal|BGEZAL|BRANCH ON大于或等于零并链接|bgezal$a,偏移|如果$a>;=0:$ra=PC+8和转到偏移量|+---+-+---+-+。-+|平衡|分支和链接|平衡偏移|$ra=PC+8和转到偏移|+-+。-+-+---+|bne|分支不等于|bne$a,亿美元,偏移量|if$a!=$b:转到偏移量|+---+-+---+-+。除法(U)|除(无符号)|---+|$a,$b|$Lo=$s/$t,$HI=$s%$t(LO/HI为特殊寄存器)|+-+---+-+。---+|J**|跳转|j目标|PC=目标|+---+-+。-+-+---+|Jr|跳转寄存器|Jr目标|pc=$REGISTER|+。-+---+-+。-+|JALR|跳转和链接寄存器|JALR TARGET|$ra=PC+8,PC=$REGISTER|+-+