原文地址
https://therealunicornsecurity.github.io/Powervision-1/
正文
黑进Harley的调谐器
找到我的方法,著名的Harley调谐器的固件.
注意:所有加密密钥和密码都是伪造的,用于撰写此帖子。
第 1 部分:什么是调谐器
调谐器是一种应该插在摩托车上的小装置。它的目的是配置车载计算机,以优化燃料空气比和其他参数。众所周知,它被用来取消发动机功率限制,或优化燃油消耗。不过,我敢肯定,一个真正的摩托车手会用更好的词语来描述它是如何被使用的。
这里研究的模型是戴诺杰(Dynojet)为哈雷·戴维森(Harley Davidson)设计的Power Vision。硬件是由德鲁技术公司(Drew Technologies)推出的山猫D版(PCB上有一个打字错误,实际上写的是德鲁技术公司)。
第 2 部分:获取固件
2.1:分析工具
调谐器应在连接到计算机时进行配置。它在CAN总线旁边有一个迷你USB接口(这个接口应该连接到自行车的车载计算机上)。用来配置它的工具可以在Dynojet的网站上免费下载。
已安装的Windows工具包含以下二进制文件:
* WinPV.exe:带有图形用户界面的主软件。
* PVUpdateClient.exe:软件更新程序,它的工作是下载新的固件并通过USB链接复制它们。
* RecoveryTool.exe:由PVUpdateClient专门调用,用于更新固件的恢复部分。
* PVLink.dll:负责串口通信,非常重要
现在我们的目标是得到固件,这样我们就可以开始逆向分析了。在YouTube教程和TheWaybackMachine上查看,我们可以看到固件曾经可以直接在Dynojet的网站上,在固件部分,现在是空的。我们通过同时运行PVUpdateCLient.exe、Wireshark发现了一条有趣的线索。
Wireshark捕获显示纯文本HTTP将dynojetpowervision.com网站以及检查可用的固件文件。这里没有真正的保护,只是您应该使用的用户代理是“PVUpdateClient”,否则文件仍然是隐藏的。
使用curl命令,我们可以得到我们要查找的文件名:
```
curl -v -A PVUpdateClient http://dynojetpowervision.com/downloads/PowerVisionVersions.xml
* Trying 52.183.62.164:80...
* TCP_NODELAY set
* Connected to dynojetpowervision.com (52.183.62.164) port 80 (#0)
GET /downloads/PowerVisionVersions.xml HTTP/1.1
Host: dynojetpowervision.com
User-Agent: PVUpdateClient
Accept: /
- Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/xml
< Last-Modified: Mon, 09 Nov 2020 18:35:22 GMT
< Accept-Ranges: bytes
...
...
我们开始关注固件和SYSTEM_UPGRADE文件,因为它们最有可能包含我们感兴趣的内容。我们使用wget下载它们,麻烦从这里开始:
mishellcode@unisec:~/powervision$ unzip PV_SYSTEMUPGRADE-1.0.1-631.pvrArchive: PV_SYSTEMUPGRADE-1.0.1-631.pvr
[PV_SYSTEMUPGRADE-1.0.1-631.pvr] PVRecoveryInfo.xml password:
mishellcode@unisec:~/powervision$ unzip PV_FIRMWARE-2.0.47-1613.pvuArchive: PV_FIRMWARE-2.0.47-1613.pvu
extracting: PVU_TYPE
extracting: PVU_CERT
inflating: PVU_FILE
mishellcode@unisec:~/powervision$ file PVU_FILE
PVU_FILE: openssl enc'd data with salted password
```
SYSTEM_RECOVERY,也就是我们所说的 "PVR文件 "是一个受密码保护的存档,而名为 "PVU_FILE "的FIRMWARE文件实际上是使用Openssl加密的。另外,PVU_CERT文件表示可能会对PVU_FILE进行完整性检查。
我将跳过细节,但由于PVR文件是以明文形式写在设备上的,很明显,其中一个工具(在本例中是RecoveryTool.exe)在某个地方有密码。稍后通过逆向工程,我们得到一个密码 "POWERVISION_RECOVER_3456789Z"。虽然在这里我不会解释整个事情的来龙去脉,但是这个密码其实是有一定的隐蔽性的。它并没有硬编码,而是通过一些循环去整数值生成密码的结尾模式,然后再给它连接一个大写字母。有明显的意图是为了隐藏这一点,防止有不良意图的用户,这通常是我们走对了路的标志。
恢复文件内容
nandflash_bobcat.bin: data
PVRecoveryInfo.xml: exported SGML document, ASCII text, with CRLF line terminators
u-boot.bin: data
uImage: u-boot legacy uImage, Bobcat-577, Linux/ARM, OS Kernel Image (Not compressed), 828996 bytes, Thu Feb 3 14:44:52 2011, Load Address: 0x20008000, Entry Point: 0x20008040, Header CRC: 0x5EDBBE36, Data CRC: 0xD026D2D5
恢复文件是一个u-boot镜像与Kernel镜像。文件的熵表明其中有一部分是加密的。另外,at91bootstrap文件的存在表明我们是存在一个SAM AT 91板,它可以使用安全启动。该死的。不过我们可以从这些文件中得到一个信息:处理器类型是SAM926X。
浏览互联网,我们还可以找到以下论坛帖子,其中有一位Drew Technoligies的员工询问关于这个相同系列的处理器(特别是SAM9260-EK)的信息:https://lists.denx.de/pipermail/u-boot/2011-June/093651.html
更新文件
由于更新文件是加密的,我们可以提出两个假设:
* 固件是加密存储的,在运行时解密。这可能会很慢,但由于板子支持安全启动,这是一个可行的假设。
* 固件是不加密存储的,只有更新是加密的。更新过程会解密PVU_FILE,并替换运行中的固件,这样就好了,不是吗?
2.2: 物理构造
快刀斩乱麻的胜法总是拆开内存芯片来获取固件。但这样的话,就比较复杂了。整个PCB是用塑料保护成型的,可能是为了密封防潮。
我必须把它切开才能看到实际的PCB:
内存芯片似乎被焊接在PCB的另一侧。这是一个相当糟糕的消息,因为它在屏幕下方,如果试图从物理上得到这个,可能会破坏设备。另外,在这个阶段,我们也不知道拿到芯片是否能帮助我们。它可能只是包含一个Openssl加密文件,在启动过程中会被解密。所以我们从其他地方寻找。
通过仔细观察,我们可以发现有4个引脚上面写着DEBUG!
所以我们用一个UART转USB的适配器连接到它,然后启动minicom。
ROMBoot
Welcome to bobcat
bocat login:
问题是这个shell是有密码保护的,即使经过几天的强制执行(使用这个工具),也没有找到密码。另外,U-Boot被设置为静音,从这个shell没有办法与启动序列进行交互。
有一条轨迹仍然没有被探索出来:RECOVERY MODE。
恢复模式
摆弄一下RecoveryTool.exe,我们发现有一个设备的恢复模式。它是通过按电源按钮,同时插入USB链接激活.
现在,这个模式的有趣之处在于,它切换了USB Link端口的通信模式。事实上,在名义上的工作模式下,这个端口使用了一个专有的协议,限制了很多操作(我将在专门的部分详细介绍),而在恢复模式下,这个端口会暴露出一个U-Boot shell!
```
U-Boot> printenv
bootargs=console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 quiet
bootcmd=nboot kernel;bootm;
bootdelay=0
baudrate=115200
mtdids=nand0=atmel_nand
mtdparts=mtdparts=atmel_nand:128k@0x0(at91bootstrap)ro,1m(u-boot)ro,2m(kernel),-(linux)
silent=yes
ver=U-BootVersion:1.0.1
stdout=usbser
stdin=usbser
stderr=usbser
Environment size: 316/131068 bytes
我们可以修改引导参数,以绕过内部UART调试端口的认证:
U-Boot> setenv bootargs "console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single"
U-Boot> setenv silent no
U-Boot> setenv bootdelay 3
U-Boot> printenv
bootcmd=nboot kernel;bootm;
baudrate=115200
mtdids=nand0=atmel_nand
mtdparts=mtdparts=atmel_nand:128k@0x0(at91bootstrap)ro,1m(u-boot)ro,2m(kernel),-(linux)
ver=U-BootVersion:1.0.1
stdout=usbser
stdin=usbser
stderr=usbser
bootargs="console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single"
silent=no
bootdelay=3
Environment size: 319/131068 bytes
```
我们用single代替quiet,以便停用认证,添加一个延迟,以便我们有足够的时间进入UART shell,并将silent设置为 "no",以确保我们在UART shell上有一个启动跟踪。
要做到这一点,我们需要同时连接到我们配置新参数的USB链接和内部UART调试端口,在那里shell应该弹出。
设置好所有参数后,用U-Boot在USB Link上运行启动命令,将触发单用户恢复模式启动:
```
U-Boot> boot
Loading from nand0, offset 0x120000
Image Name: Bobcat-577
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 828996 Bytes = 809.6 KiB
Load Address: 20008000
Entry Point: 20008040
Booting kernel from Legacy Image at 20008000 ...
Image Name: Bobcat-577
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 828996 Bytes = 809.6 KiB
Load Address: 20008000
Entry Point: 20008040
Verifying Checksum ... OK
XIP Kernel Image ... OK
OK
同时,在UART调试连接上:
[ 0.000000] Linux version 2.6.30 (joey@superserver) (gcc version 4.3.3 (GCC) ) #32 PREEMPT Thu Feb 3 09:43:13 E1
[ 0.000000] CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00053177
[ 0.000000] CPU: VIVT data cache, VIVT instruction cache
[ 0.000000] Machine: Dynojet Power Vision
[ 0.000000] Memory policy: ECC disabled, Data cache writeback
[ 0.000000] Clocks: CPU 192 MHz, master 96 MHz, main 16.000 MHz
[ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16002
[ 0.000000] Kernel command line: console=ttyS0,115200 ubi.mtd=linux root=31:4 lpj=598016 single
[ 0.000000] NR_IRQS:192
[ 0.000000] AT91: 96 gpio irqs in 3 banks
...
[ 0.930000] UBI: background thread "ubi_bgt0d" started, PID 97
[ 0.950000] VFS: Mounted root (squashfs filesystem) readonly on device 31:4.
[ 0.960000] Freeing init memory: 64K
/
id
uid=0(root) gid=0(root)
ggwp
```
左边是U-Boot shell,右边是显示启动顺序的UART shell
2.3 恢复Mode Shell
我们得到的shell是在一个特定的模式下设置的,其中只有部分固件被安装。我们现在需要完整的固件。一种方法是找到Openssl加密密码,并解密PVU_FILE。但我们先从另一个方法开始。
在2.1的时候,我们怀疑固件可能是未加密存储的,只有更新文件是加密存储的。这是正确的答案。
[ 0.410000] UBI: attaching mtd3 to ubi0
[ 0.410000] UBI: physical eraseblock size: 131072 bytes (128 KiB)
[ 0.420000] UBI: logical eraseblock size: 129024 bytes
[ 0.430000] UBI: smallest flash I/O unit: 2048
[ 0.430000] UBI: sub-page size: 512
[ 0.430000] UBI: VID header offset: 512 (aligned 512)
[ 0.440000] UBI: data offset: 2048
[ 0.870000] UBI: attached mtd3 to ubi0
[ 0.880000] UBI: MTD device name: "linux"
[ 0.880000] UBI: MTD device size: 252 MiB
[ 0.890000] UBI: number of good PEBs: 2023
[ 0.890000] UBI: number of bad PEBs: 0
[ 0.900000] UBI: max. allowed volumes: 128
[ 0.900000] UBI: wear-leveling threshold: 4096
[ 0.910000] UBI: number of internal volumes: 1
[ 0.910000] UBI: number of user volumes: 2
[ 0.910000] UBI: available PEBs: 0
[ 0.920000] UBI: total number of reserved PEBs: 2023
[ 0.920000] UBI: number of PEBs reserved for bad PEB handling: 20
[ 0.930000] UBI: max/mean erase counter: 110/68
[ 0.930000] UBI: background thread "ubi_bgt0d" started, PID 97
在启动序列中,我们可以看到从MTD设备中挂载了一个UBI文件系统。使用我们现在拥有的root shell,我们发现2个有趣的设备。UBI0_0和UBI0_1. 为了直接从它们那里读取数据,我们使用dd和uencode:
dd if=/dev/ubi0X of=stdout bs=SIZE count=COUNT 2&>/dev/null |uuencode -m ubi00
而我们从minicom日志中提取base64编码的数据。我们从PVU_FILE中知道固件的大小(约11MB),从恢复文件中的u-boot数据知道内存芯片的大小(128MB)
```
$ binwalk ubi00
DECIMAL HEXADECIMAL DESCRIPTION
0 0x0 Squashfs filesystem, little endian, version 4.0, compression:gzip, size: 10473396 bytes, 455 inodes, blocksize: 131072 bytes, created: 2019-09-04 20:48:04
$ binwalk ubi01
DECIMAL HEXADECIMAL DESCRIPTION
0 0x0 UBIFS filesystem superblock node, CRC: 0x8254BB9D, flags: 0x0, min I/O unit size: 2048, erase block size: 129024, erase block count: 1917, max erase blocks: 2039, format version: 4, compression type: lzo
129024 0x1F800 UBIFS filesystem master node, CRC: 0xBA73BA7B, highest inode: 2248, commit number: 132173
131072 0x20000 UBIFS filesystem master node, CRC: 0xF9347A80, highest inode: 2248, commit number: 132174
133120 0x20800 UBIFS filesystem master node, CRC: 0xB9DAE7BF, highest inode: 2248, commit number: 132175
258048 0x3F000 UBIFS filesystem master node, CRC: 0xB6434F66, highest inode: 2248, commit number: 132173
260096 0x3F800 UBIFS filesystem master node, CRC: 0xF5048F9D, highest inode: 2248, commit number: 132174
262144 0x40000 UBIFS filesystem master node, CRC: 0xB5EA12A2, highest inode: 2248, commit number: 132175
5062704 0x4D4030 Zip archive data, at least v1.0 to extract, compressed size: 11, uncompressed size: 11, name: PVU_TYPE
5062781 0x4D407D Zip archive data, at least v1.0 to extract, compressed size: 128, uncompressed size: 128, name: PVU_CERT
5062975 0x4D413F Zip archive data, at least v2.0 to extract, compressed size: 11295533, uncompressed size: 11293808, name: PVU_FILE
...
```
好了,整个固件都在这里了:
- UBI0_0: 固件的只读部分,也就是二进制文件,布局和设备的一切必需品。
- UBI0_1: 读/写的部分,所以许可证,用户文件,新的更新等
好了,我们可以逆向分析了
参考文献
这个帖子让我想到了在启动参数中切换到单用户模式
https://blog.senr.io/blog/jtag-explained
在下一集里
固件逆向工程和仿真(着重于USB链路专有协议)
Loot(密码、加密密钥…)
缓冲区溢出
我代表Unicorn Security祝大家新年快乐!请继续关注下一部分。
写于2021年1月1日
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论