譯者:知道創宇404實驗室翻譯組
原文鏈接:https://0x434b.dev/breaking-the-d-link-dir3060-firmware-encryption-recon-part-1/

前言

最近,我們發現了一些無法解壓的D-Link路由器的固件樣本。通過分析類似的更舊、更便宜的設備(DIR882),我們可以找到一種破解固件加密的方法,以防止篡改和靜態分析。本系列文章重點介紹了編寫自定義解密例程的結果和必要步驟,該例程也可用于其他模型,后續會對此進行更多介紹。

問題

此處可下載最新版D-Link 3060固件(截至撰寫本文時),我將研究于19年10月22日發行的v1.02B03版本,初步分析結果如下:

> md5sum DIR-3060_RevA_Firmware111B01.bin
86e3f7baebf4178920c767611ec2ba50  DIR3060A1_FW102B03.bin

> file DIR-3060_RevA_Firmware111B01.bin
DIR3060A1_FW102B03.bin: data

> binwalk DIR-3060_RevA_Firmware111B01.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------

> hd -n 128 DIR-3060_RevA_Firmware111B01.bin
00000000  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.....w....F{...|
00000030  39 b5 59 6b 75 8d b8 b0  a3 1d 28 84 33 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..\.S|
00000070  ed 18 24 d0 a6 59 c0 de  1c f3 8b 67 1d e6 31 36  |..$..Y.....g..16|
00000080

從文件命令中得到的某種形式的二進制數據文件并不是很有用,最初的偵察選擇是:binwalk也無法識別固件鏡像中的文件部分,前128個字節的十六進制轉儲顯示了從偏移量0x0開始的看似隨機的數據。這些都是加密圖像的指標,通過熵值分析法可以確認:

> binwalk -E DIR-3060_RevA_Firmware111B01.bin

DECIMAL       HEXADECIMAL     ENTROPY
--------------------------------------------------------------------------------
0             0x0             Rising entropy edge (0.978280)

init_Entropy

曲線沒有任何下降,我們無法提取有關目標的任何信息...

嘗試

我們購買了比200美元的D-Link DIR 3060更便宜但又極其類似的D-Link DIR 882,旨在找到至少一個部署相同加密方案的替代產品。

附帶說明:即使我們無法找到類似的加密方案,不同的固件標頭也可能提供一些提示,進而說明其“保護”固件的流程。

當我們偶然發現DIR 882時,我們檢查了2020年2月20日發行的固件v1.30B10,它顯示出與DIR3060相同的特征,包括接近1的常量熵。大家可能會注意到開始的“ SHRS”處是相同的4字節序列,這個我們稍后再討論。

> md5sum DIR_882_FW120B06.BIN
89a80526d68842531fe29170cbd596c3  DIR_882_FW120B06.BIN

> file DIR_882_FW120B06.BIN
DIR_882_FW120B06.BIN: data

> binwalk DIR_882_FW120B06.BIN

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------

> hd -n 128 DIR_882_FW120B06.BIN
00000000  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|
00000030  1d 0e 90 85 b7 83 f7 4d  3a 2a 25 5a b8 13 0c 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

dir882_entropy

截止2020年初,該固件仍使用相同的加密方案。

解決方案

一旦獲得DIR882,我們就可以在設備上輸入一個串行控制臺,并在文件系統中尋找處理固件更新的加密/解密的線索和對象。(UART控制臺不在本文的討論范圍之內,因為它除了連接4條電纜外,沒有涉及“硬件黑客”活動)很快就能找出相關信息:

> file imgdecrypt
imgdecrypt: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, stripped

> md5sum imgdecrypt
a5474af860606f035e4b84bd31fc17a1  imgdecrypt
> base64 < imgdecrypt

將輸出復制到本地計算機并將base64轉換回二進制文件后,開始仔細分析:

二進制偵察

上文可見,我們正處理用于MIPS的32位ELF二進制文件,該二進制文件是動態鏈接(按預期方式)和剝離的,讓我們看看strings能做些什么:

> strings -n 10 imgdecrypt | uniq
/lib/ld-uClibc.so.0
[...]
SHA512_Init
SHA512_Update
SHA512_Final
RSA_verify
AES_set_encrypt_key
AES_cbc_encrypt
AES_set_decrypt_key
PEM_write_RSAPublicKey
OPENSSL_add_all_algorithms_noconf
PEM_read_RSAPublicKey
PEM_read_RSAPrivateKey
RSA_generate_key
EVP_aes_256_cbc
PEM_write_RSAPrivateKey
decrypt_firmare
encrypt_firmare
[...]
libcrypto.so.1.0.0
[...]
no image matic found
check SHA512 post failed
check SHA512 before failed %d %d
check SHA512 vendor failed
static const char *pubkey_n = "%s";
static const char *pubkey_e = "%s";
Read RSA private key failed, maybe the key password is incorrect
/etc_ro/public.pem
%s <sourceFile>
/tmp/.firmware.orig
0123456789ABCDEF
%s sourceFile destFile
[...]

這里有很多有用的東西,我只是刪除了“[…]”所指示的行,最值得注意的是以下幾點:

  • 使用uClibc和libcrypto
  • 計算/檢查SHA512 hash摘要
  • 使用AES_CBC模式進行加密/解密
  • 進行RSA證書檢查,并將證書路徑固定到/etc_ro/public.pem
  • RSA私鑰受密碼保護
  • /tmp/.firmware.orig可能暗示了事物被臨時解密到的位置
  • imgdecrypt二進制文件的一般用法

小結

1.D-Link可能會在多個設備上重復使用相同的加密方案

2.設備基于MIPS32架構

3.可訪問DIR 882上的UART串行控制臺

4.鏈接到uClibc和libcrypto

可能使用AES,RSA和SHA512例程

5.二進制似乎同時負責加密和解密

6.有公共證書

7.imgdecrypt的用法似乎是./imgdecrypt myInFile

8.使用/ tmp /路徑存儲結果?

接下來,我們將深入研究imgdecrypt二進制文件,以了解如何控制固件更新!對于那些對MIPS32匯編語言有點生疏的人來說,這里是簡短的入門。

MIPS32拆卸的初級讀本

大多數人可能都很熟悉x86/x86_64反匯編,所以這里涉及MIPS如何運行以及它與x86有何不同的一般規則。接下來我將探討最常見的O32:

寄存器

在MIPS32中,可使用32個寄存器,O32對其定義如下:

+---------+-----------+------------------------------------------------+
|   Name  |   Number  |                  Usage                         |
+----------------------------------------------------------------------+
|  $zero  |  $0       |  Is always 0, writes to it are discarded.      |
+----------------------------------------------------------------------+
|  $at    |  $1       |  Assembler temporary register (pseudo instr.)  |
+----------------------------------------------------------------------+
| $v0─$v1 |  $2─$3    |  Function returns/expression evaluation        |
+----------------------------------------------------------------------+
| $a0─$a3 |  $4─$7    |  Function arguments, remaining are in stack    |
+----------------------------------------------------------------------+
| $t0─$t7 |  $8─$15   |  Temporary registers                           |
+----------------------------------------------------------------------+
| $s0─$s7 |  $16─$23  |  Saved temporary registers                     |
+----------------------------------------------------------------------+
| $t8─$t9 |  $24─$25  |  Temporary registers                           |
+----------------------------------------------------------------------+
| $k0─$k1 |  $26─$27  |  Reserved for kernel                           |
+----------------------------------------------------------------------+
|  $gp    |  $28      |  Global pointer                                |
+----------------------------------------------------------------------+
|  $sp    |  $29      |  Stack pointer                                 |
+----------------------------------------------------------------------+
|  $fp    |  $30      |  Frame pointer                                 |
+----------------------------------------------------------------------+
|  $ra    |  $31      |  Return address                                |
+---------+-----------+------------------------------------------------+

最重要的是:

  • 前四個函數參數移入$a0 - $a3,其余參數置于堆棧頂部;
  • 函數返回被放置,$v0并最終在$v1第二個返回值存在時被放置;
  • $ra通過跳轉和鏈接(JAL)或跳轉和鏈接寄存器(JALR)執行功能調用時,返回地址存儲在寄存器中;
  • $sX 寄存器在過程調用之間保留(子例程可以使用它們,但必須在返回之前將其還原);
  • $gp 指向靜態數據段中64k內存塊的中間;
  • $sp 指向堆棧的最后一個位置;
  • 葉子非葉子子例程之間的區別:
  • 葉子:請勿調用任何其他子例程,也不要使用堆棧上的內存空間。沒有建立堆棧框架,因此不需要更改$sp
  • 帶數據的葉:與葉相同,但它們需要堆棧空間,例如:用于局部變量,將推動堆棧框架,可省略不需要的堆棧框架部分。
  • 非葉程序:將調用其他子例程,很可能具有完整的堆棧框架。
  • 在有PIC的Linux上,$t9應該包含被調用函數的地址。
              +                 +-------------------+  +-+
              |                 |                   |    |
              |                 +-------------------+    |
              |                 |                   |    |   Previous
              |                 +-------------------+    +-> Stack
              |                 |                   |    |   Frame
              |                 +-------------------+    |
              |                 |                   |    |
              |                 +-------------------+  +-+
              |                 |  local data x─1   |  +-+
              |                 +-------------------+    |
              |                 |                   |    |
              |                 +-------------------+    |
              |                 |  local data 0     |    |
              |                 +-------------------+    |
              |                 |  empty            |    |
    Stack     |                 +-------------------+    |
    Growth    |                 |  return value     |    |
    Direction |                 +-------------------+    |
              |                 |  saved reg k─1    |    |
              |                 +-------------------+    |   Current
              |                 |                   |    +-> Stack
              |                 +-------------------+    |   Frame
              |                 |  saved reg 0      |    |
              |                 +-------------------+    |
              |                 |  arg n─1          |    |
              |                 +-------------------+    |
              |                 |                   |    |
              |                 +-------------------+    |
              |                 |  arg 4            |    |
              |                 +-------------------+    |
              |                 |  arg 3            |    |
              |                 +-------------------+    |
              |                 |  arg 2            |    |
              |                 +-------------------+    |
              |                 |  arg 1            |    |
              |                 +-------------------+    |
              |                 |  arg 0            |    |
              v                 +-------------------+  +-+
                                          |
                                          |
                                          v

常用操作

熟悉其他匯編語言的人將很快上手,以下為可快速入門本系列第2部分的精選內容:

+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  Mnemonic        |  Full name                                         |  Syntax                 |  Operation                                               |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  ADD             |  Add (with overflow)                               |  add $a, $b, $c         |  $a = $b + $c                                            |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  ADDI        |  Add immediate (with overflow)                     |  addi $a, $b, imm       |  $a = $b + imm                                           |
    +--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  ADDIU       |  Add immediate unsigned (no overflow)              |  addiu $a, $b, imm      |  see ADDI                                                |
    +--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  ADDU        |  Add unsigned (no overflow)                        |  addu $a, $b, $c        |  see ADD                                                 |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  AND*            |  Bitwise and                                       |  and $a, $b, $c         |  $a = $b & $c                                            |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  B**             |  Branch to offset unconditionally                  |  b offset               |  goto offset                                             |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  BEQ         |  Branch on equal                                   |  beq $a, $b, offset     |  if $a == $t goto offset                                 |
    +---+----------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
        |  BEQZ    |  Branch on equal to zero                           |  beqz $a, offset        |  if $a == 0 goto offset                                  |
    +---+----------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  BGEZ        |  Branch on greater than or equal to zero           |  bgez $a, offset        |  if $a >= 0 goto offset                                  |
    +---+----------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
        |  BGEZAL  |  Branch on greater than or equal to zero and link  |  bgezal $a, offset      |  if $a >= 0: $ra = PC+8 and goto offset                  |
    +---+----------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  BAL         |  Branch and link                                   |  bal offset             |  $ra=PC+8 and goto offset                                |
    +--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  BNE         |   Branch on not equal                              |  bne $a, $b, offset     |  if $a != $b: goto offset                                |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  DIV(U)          |  Divide (unsigned)                                 |  div $a, $b             |  $LO = $s/$t, $HI = $s%$t (LO/HI are special registers)  |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  J**             |  Jump                                              |  j target               |  PC=target                                               |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  JR          |  Jump register                                     |  jr target              |  PC=$register                                            |
    +--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  JALR        |  Jump and link register                            |  jalr target            |  $ra=PC+8, PC=$register                                  |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  L(B/W)          |   Load (byte/word)                                 |  l(b/w) $a, offset($b)  |  $a = memory[$b + offset]                                |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  LWL         |  Load word left                                    |  lwl $a, offset(base)   |                                                          |
    +--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
    |  LWR         |  Load word right                                   |  lwr $a, offset(base)   |                                                          |
+---+--------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  OR*             |  Bitewise or                                       |  or $a, $b, $c          |  $a = $b|$c                                              |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  S(B/W)          |  Store (byte/word)                                 |  s(w/b) $a, offset($b)  |  memory[$b + offset] = $a                                |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  SLL**           |  Shift left logical                                |  sll $a, $b, h          |  $a = $b << h                                            |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  SRL**           |  Shift right logical                               |   srl $a, $b, h         |  $a = $b >> h                                            |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
| SYSCALL          |  System call                                       |  syscall                |  PC+=4                                                   |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+
|  XOR*            |  Bitwise exclusive or                              |  xor $a, $b, $c         |  $a = $b^$c                                              |
+------------------+----------------------------------------------------+-------------------------+----------------------------------------------------------+

注意

1.未明確聲明PC更改的人可以假定在執行時PC + = 4;
2.標有星號的標記至少具有一個即時版本;
3.帶有雙星號的標記還有許多其他變體;
4.ADD變體只能將SUB(U)作為對應物;
5.DIV變體有一個MULT(U)對應物;
6.jb指令之間的一般區別是分支使用PC相對位移,而跳轉使用絕對地址,當考慮使用PIC時,這一點非常重要。

最初的偵查階段就到這里了,上面的MIPS32匯編表只是所有可用指令的超集,即使不熟悉MIPS組裝,以上表格也足以應對第二部分的內容學習了! 本系列的第二部分,我們將深入研究IDA中的imgdecrypt二進制文件,敬請關注!

參考


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1523/