Dumping and extracting the SpaceX Starlink User Terminal firmware

IoT 2周前 admin
89 0 0
Dumping and extracting the SpaceX Starlink User Terminal firmware

Towards the end of May 2021 Starlink launched in Belgium so we were finally able to get our hands on a Dishy McFlatface. In this blog post we will cover some initial exploration of the hardware and we will explain how we dumped and extracted the firmware.
2021 年 5 月底,Starlink 在比利时推出,因此我们终于能够获得 Dishy McFlatface。在这篇博文中,我们将介绍硬件的一些初步探索,并解释我们如何转储和提取固件。

Note that this blog post does not discuss any specific vulnerabilities, we merely document techniques that can be used by others to research the Starlink User Terminal (UT). Towards the end of this blog post we will include some interesting findings from the firmware. Note that SpaceX actively encourages people to find and report security issues through their bug bounty program: https://bugcrowd.com/spacex
请注意,这篇博文没有讨论任何特定的漏洞,我们只是记录了其他人可用于研究 Starlink 用户终端 (UT) 的技术。在这篇博文的最后,我们将包括一些来自固件的有趣发现。请注意,SpaceX积极鼓励人们通过其漏洞赏金计划查找和报告安全问题: https://bugcrowd.com/spacex

We first set up our UT on a flat section of our university building’s roof and played around with it for a few hours, giving the UT and router the chance to perform a firmware update. We did run a few mandatory speed tests and were seeing as much as 268 Mbps download and 49 Mbps upload.
我们首先在大学大楼屋顶的平坦部分设置了UT,并玩了几个小时,让UT和路由器有机会执行固件更新。我们确实进行了一些强制性的速度测试,并看到了高达 268 Mbps 的下载速度和 49 Mbps 的上传速度。

Dumping and extracting the SpaceX Starlink User Terminal firmware

Teardown: Level 1 拆解: 级别 1

After a few hours of playing around it was time to get our hands dirty and disassemble the UT.
经过几个小时的玩耍,是时候弄脏我们的手并拆卸 UT 了。

There have been a few teardown videos of the UT but none of them went into the details we were interested in: the main SoC and firmware. Nevertheless, these prior teardowns ([1, 2, 3]) of the dish contain a lot of useful information that allowed us to disassemble our dish without too much damage.
有一些关于UT的拆解视频,但没有一个涉及我们感兴趣的细节:主SoC和固件。尽管如此,这些先前对盘子的拆解([1,2,3])包含许多有用的信息,使我们能够在不造成太大损坏的情况下拆卸盘子。

It appears that there a few hardware revisions of the UT out there by now, certain parts of the teardown process can differ depending on the revision, something we learned the hard way. One of the aforementioned teardown videos shows the Ethernet and motor control cables to be detached from the main board before the white plastic cover is removed. On our UT, a tug on the motor control cables pulled the entire connector from the PCB; luckily it appears we can repair the damage. In other words, do not pull on those cables but first remove the back plastic cover, for those of you in the same boat: JST BM05B-ZESS-TBT.
到目前为止,UT似乎已经有一些硬件版本,拆解过程的某些部分可能会因版本而异,这是我们通过艰难的方式学到的。上述拆解视频之一显示,在取下白色塑料盖之前,要从主板上拆下以太网和电机控制电缆。在我们的UT上,电机控制电缆上的拉扯将整个连接器从PCB上拉出;幸运的是,我们似乎可以修复损坏。换句话说,不要拉扯这些电缆,而要先取下后塑料盖,对于那些在同一条船上的人:JST BM05B-ZESS-TBT。

After removing the back plastic cover we can see a metal shield covering the PCB, with the exception of a small cut-out containing the connectors for the Ethernet cable and motor control cable. There is one additional, unpopulated connector (4 pin JST SH 1.0mm), that we assumed would contain a UART debug interface as was shown in [4]. Note that the early teardown videos had an additional connector that is no longer present on our UT.
取下背面塑料盖后,我们可以看到覆盖 PCB 的金属屏蔽层,除了一个包含以太网电缆和电机控制电缆连接器的小切口。还有一个额外的、未填充的连接器(4 针 JST SH 1.0mm),我们假设它包含一个 UART 调试接口,如 [4] 所示。请注意,早期的拆解视频有一个额外的连接器,我们的 UT 上不再存在。

The UART interface UART 接口

After hooking up a USB to serial converter we could get some information on the UT’s boot process.
连接USB到串行转换器后,我们可以获得有关UT启动过程的一些信息。

The output contains information regarding the early stage bootloaders before showing the following output.
在显示以下输出之前,输出包含有关早期引导加载程序的信息。

We can see that the UT is using the U-Boot bootloader, and that typing ‘falcon’ may interrupt the boot process. While this may give us access to a U-Boot CLI we can also see that the serial input is configured as ‘nulldev’. Unsurprising, spamming the serial interface with ‘falcon’ during boot did not yield any result.
我们可以看到 UT 正在使用 U-Boot 引导加载程序,并且键入“falcon”可能会中断引导过程。虽然这可以让我们访问 U-Boot CLI,但我们也可以看到串行输入配置为“nulldev”。不出所料,在启动过程中用“falcon”向串行接口发送垃圾邮件没有产生任何结果。

U-Boot 2020.04-gddb7afb (Apr 16 2021 - 21:10:45 +0000)
Model: Catson
DRAM:  1004 MiB
MMC:   Fast boot:eMMC: 8xbit - div2
stm-sdhci0: 0
In:    nulldev
Out:   serial
Err:   serial
CPU ID: 0x00020100 0x87082425 0xb9ca4b91
Detected Board rev: #rev2_proto2
sdhci_set_clock: Timeout to wait cmd & data inhibit
FIP1: 3 FIP2: 3
BOOT SLOT B
Net:   Net Initialization Skipped
No ethernet found.
                                                          *
                                                 +        
                                       +    +             
                                +     +                   
                           +      +                       
+ + + + +              +     +                            
  +        +       +     +                                
     +       + +      +                                   
        +   +      +                                      
          +      + +                                      
      +      +        +                                   
   +       +    +        +                                
 +       +         +        +                             
+ + + + +             + + + + +                           
Board: SPACEX CATSON UTERM
======================================
= Type 'falcon' to stop boot process =
======================================

Continuing through the boot process we can see that U-Boot loads a kernel, ramdisk and Flattened Device Tree (FDT) from a Flattened uImage Tree (FIT) image that is stored on an embedded MultiMediaCard (eMMC).
继续引导过程,我们可以看到 U-Boot 从存储在嵌入式多媒体卡 (eMMC) 上的扁平化 uImage 树 (FIT) 映像加载内核、ramdisk 和扁平化设备树 (FDT)。

We can also see that the integrity (SHA256) and authenticity (RSA 2048) of the kernel, ramdisk and FDT is being checked. While we would have to perform some more tests it appears that a full trusted boot chain (TF-A) is implemented from the early stage ROM bootloader all the way down to the Linux operating system.
我们还可以看到,正在检查内核、虚拟硬盘和 FDT 的完整性 (SHA256) 和真实性 (RSA 2048)。虽然我们必须执行更多的测试,但似乎从早期ROM引导加载程序一直到Linux操作系统都实现了完全可信的引导链(TF-A)。

switch to partitions #0, OK
mmc0(part 0) is current device
MMC read: dev # 0, block # 98304, count 49152 ... 49152 blocks read: OK
## Loading kernel from FIT Image at a2000000 ...
   Using 'rev2_proto2@1' configuration
   Verifying Hash Integrity ... sha256,rsa2048:dev+ OK
   Trying 'kernel@1' kernel subimage
     Description:  compressed kernel
     Created:      2021-04-16  21:10:45 UTC
     Type:         Kernel Image
     Compression:  lzma compressed
     Data Start:   0xa20000dc
     Data Size:    3520634 Bytes = 3.4 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0x80080000
     Load Size:    unavailable
     Entry Point:  0x80080000
     Hash algo:    sha256
     Hash value:   5efc55925a69298638157156bf118357e01435c9f9299743954af25a2638adc2
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at a2000000 ...
   Using 'rev2_proto2@1' configuration
   Verifying Hash Integrity ... sha256,rsa2048:dev+ OK
   Trying 'ramdisk@1' ramdisk subimage
     Description:  compressed ramdisk
     Created:      2021-04-16  21:10:45 UTC
     Type:         RAMDisk Image
     Compression:  lzma compressed
     Data Start:   0xa2427f38
     Data Size:    8093203 Bytes = 7.7 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0xb0000000
     Load Size:    unavailable
     Entry Point:  0xb0000000
     Hash algo:    sha256
     Hash value:   57020a8dbff20b861a4623cd73ac881e852d257b7dda3fc29ea8d795fac722aa
   Verifying Hash Integrity ... sha256+ OK
   Loading ramdisk from 0xa2427f38 to 0xb0000000
WARNING: 'compression' nodes for ramdisks are deprecated, please fix your .its file!
## Loading fdt from FIT Image at a2000000 ...
   Using 'rev2_proto2@1' configuration
   Verifying Hash Integrity ... sha256,rsa2048:dev+ OK
   Trying 'rev2_proto2_fdt@1' fdt subimage
     Description:  rev2 proto 2 device tree
     Created:      2021-04-16  21:10:45 UTC
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0xa23fc674
     Data Size:    59720 Bytes = 58.3 KiB
     Architecture: AArch64
     Load Address: 0x8f000000
     Hash algo:    sha256
     Hash value:   cca3af2e3bbaa1ef915d474eb9034a770b01d780ace925c6e82efa579334dea8
   Verifying Hash Integrity ... sha256+ OK
   Loading fdt from 0xa23fc674 to 0x8f000000
   Booting using the fdt blob at 0x8f000000
   Uncompressing Kernel Image
   Loading Ramdisk to 8f848000, end 8ffffe13 ... OK
ERROR: reserving fdt memory region failed (addr=b0000000 size=10000000)
   Loading Device Tree to 000000008f836000, end 000000008f847947 ... OK
WARNING: ethact is not set. Not including ethprime in /chosen.
Starting kernel ...

The remainder of the boot process contains some other interesting pieces of information.
启动过程的其余部分包含一些其他有趣的信息。

For example, we can see the kernel command line arguments and with that the starting addresses and lengths of some partitions. Additionally, we can see that the SoC contains 4 CPU cores.
例如,我们可以看到内核命令行参数,以及一些分区的起始地址和长度。此外,我们可以看到 SoC 包含 4 个 CPU 内核。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[    0.000000] 000: Detected VIPT I-cache on CPU0
[    0.000000] 000: Built 1 zonelists, mobility grouping on.  Total pages: 193536
[    0.000000] 000: Kernel command line: rdinit=/usr/sbin/sxruntime_start mtdoops.mtddev=mtdoops console=ttyAS0,115200 quiet alloc_snapshot trace_buf_size=5M rcutree.kthread_prio=80 earlycon=stasc,mmio32,0x8850000,115200n8 uio_pdrv_genirq.of_id=generic-uio audit=1 SXRUNTIME_EXPECT_SUCCESS=true blkdevparts=mmcblk0:0x00100000@0x00000000(BOOTFIP_0),0x00100000@0x00100000(BOOTFIP_1),0x00100000@0x00200000(BOOTFIP_2),0x00100000@0x00300000(BOOTFIP_3),0x00080000@0x00400000(BOOTTERM1),0x00080000@0x00500000(BOOTTERM2),0x00100000@0x00600000(BOOT_A_0),0x00100000@0x00700000(BOOT_B_0),0x00100000@0x00800000(BOOT_A_1),0x00100000@0x00900000(BOOT_B_1),0x00100000@0x00A00000(UBOOT_TERM1),0x00100000@0x00B00000(UBOOT_TERM2),0x00050000@0x00FB0000(SXID),0x01800000@0x01000000(KERNEL_A),0x00800000@0x02800000(CONFIG_A),0x01800000@0x03000000(KERNEL_B),0x00800000@0x04800000(CONFIG_B),0x01800000@0x05000000(SX_A),0x01800000@0x06800000(SX_B),0x00020000@0x00F30000(VERSION_INFO_A),0x00020000@0x00F50000(VERSION_INFO_B),0x00020000
[    0.000000] 000: audit: enabled (after initialization)
[    0.000000] 000: Dentry cache hash table entries: 131072 (order: 9, 2097152 bytes, linear)
[    0.000000] 000: Inode-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.000000] 000: mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] 000: Memory: 746884K/786432K available (6718K kernel code, 854K rwdata, 1648K rodata, 704K init, 329K bss, 39548K reserved, 0K cma-reserved)
[    0.000000] 000: SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[    0.000000] 000: ftrace: allocating 23664 entries in 93 pages
[    0.000000] 000: rcu: Preemptible hierarchical RCU implementation.
[    0.000000] 000: rcu:        RCU event tracing is enabled.
[    0.000000] 000: rcu:        RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=4.
[    0.000000] 000: rcu:        RCU priority boosting: priority 80 delay 500 ms.
[    0.000000] 000: rcu:        RCU_SOFTIRQ processing moved to rcuc kthreads.
[    0.000000] 000:     No expedited grace period (rcu_normal_after_boot).
[    0.000000] 000:     Tasks RCU enabled.
[    0.000000] 000: rcu: RCU calculated value of scheduler-enlistment delay is 100 jiffies.
[    0.000000] 000: rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
[    0.000000] 000: NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] 000: random: get_random_bytes called from start_kernel+0x33c/0x4b0 with crng_init=0
[    0.000000] 000: arch_timer: cp15 timer(s) running at 60.00MHz (virt).
[    0.000000] 000: clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x1bacf917bf, max_idle_ns: 881590412290 ns
[    0.000000] 000: sched_clock: 56 bits at 60MHz, resolution 16ns, wraps every 4398046511098ns
[    0.008552] 000: Calibrating delay loop (skipped), value calculated using timer frequency..
[    0.016871] 000: 120.00 BogoMIPS (lpj=60000)
[    0.021129] 000: pid_max: default: 32768 minimum: 301
[    0.026307] 000: Mount-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.034005] 000: Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.048359] 000: ASID allocator initialised with 32768 entries
[    0.050341] 000: rcu: Hierarchical SRCU implementation.
[    0.061390] 000: smp: Bringing up secondary CPUs ...
[    0.078677] 001: Detected VIPT I-cache on CPU1
[    0.078755] 001: CPU1: Booted secondary processor 0x0000000001 [0x410fd034]
[    0.095799] 002: Detected VIPT I-cache on CPU2
[    0.095858] 002: CPU2: Booted secondary processor 0x0000000002 [0x410fd034]
[    0.112970] 003: Detected VIPT I-cache on CPU3
[    0.113025] 003: CPU3: Booted secondary processor 0x0000000003 [0x410fd034]
[    0.113160] 000: smp: Brought up 1 node, 4 CPUs
[    0.113184] 000: SMP: Total of 4 processors activated.

Finally, when the UT completes its boot process we are greeted with a login prompt:
最后,当UT完成其启动过程时,我们会收到一个登录提示:

Development login enabled: no
SpaceX User Terminal.
user1 login:

While making a few attempts at guessing valid login credentials we started realising that this UART interface would be unlikely to result in an easy win. We had to go deeper.
在尝试猜测有效的登录凭据时,我们开始意识到这个 UART 接口不太可能轻松获胜。我们必须更深入。

Teardown: Level 2 拆解: 级别 2

The back metal cover of the UT is glued to the assembly around the outer edge and additional glue is applied between the ribs in the metal cover and the underlying PCB. To loosen the glue at the edge of the metal cover we used a heat gun, prying tools, isopropyl alcohol and a lot of patience. Specifically, we first applied heat to a small section, used a prying tool to loosen that section, added IPA to help dissolve the glue and another round of the prying tool. Having removed the metal cover we are greeted by an enormous PCB measuring approximately 55 cm in diameter.
UT 的背面金属盖粘在外边缘周围的组件上,并在金属盖的肋条和下面的 PCB 之间施加额外的胶水。为了松开金属盖边缘的胶水,我们使用了热风枪、撬动工具、异丙醇和大量的耐心。具体来说,我们首先对一小部分施加热量,使用撬动工具松开该部分,添加 IPA 以帮助溶解胶水和另一轮撬动工具。取下金属盖后,迎接我们的是一个直径约为 55 厘米的巨大 PCB。

Dumping and extracting the SpaceX Starlink User Terminal firmware

The parts of interest to us are shown in the picture below. The flip-chip BGA package with the metal lid is the main SoC on this board (marking: ST GLLCCOCA6BF). Unsurprisingly the SoC is connected to some volatile DRAM storage and non-volatile flash storage in the form of an eMMC chip.
我们感兴趣的部分如下图所示。带金属盖的倒装芯片 BGA 封装是该板上的主要 SoC(标记:ST GLLCCOCA6BF)。不出所料,SoC 以 eMMC 芯片的形式连接到一些易失性 DRAM 存储和非易失性闪存。

Dumping and extracting the SpaceX Starlink User Terminal firmware

Identifying eMMC test points
识别 eMMC 测试点

An embedded MultiMediaCard (eMMC) contains flash storage and a controller and is quite similar to an SD-card. The UT contains a Micron eMMC chip with package marking JY976, Micron offers a convenient tool to decode these package markings to the actual part number: https://www.micron.com/support/tools-and-utilities/fbga. The eMMC chip in question has part number MTFC4GACAJCN-1M and contains 4GB of flash storage in a BGA-153 package. In most scenarios we would desolder such an eMMC chip, reball it and dump it using a BGA socket. However, in this case we first attempted to dump the eMMC in-circuit to minimize the odds of damaging our UT and the eMMC chip.
嵌入式多媒体卡 (eMMC) 包含闪存和控制器,与 SD 卡非常相似。UT 包含一个封装标记为 JY976 的美光 eMMC 芯片,美光提供了一个方便的工具,可以将这些封装标记解码为实际部件号:https://www.micron.com/support/tools-and-utilities/fbga。有问题的 eMMC 芯片的部件号为 MTFC4GACAJCN-1M,包含 BGA-153 封装的 4GB 闪存。在大多数情况下,我们会拆焊这样的eMMC芯片,将其重新熔接并使用BGA插座转储。然而,在这种情况下,我们首先尝试转储 eMMC 在线,以尽量减少损坏我们的 UT 和 eMMC 芯片的几率。

eMMC chips are similar to SD cards in that they share a similar interface; the eMMC chip does support up to 8 data lines whereas SD-cards support up to 4 data lines. Both eMMC chips and SD-Cards support the use of only a single data line at the cost of lower read/write speeds.
eMMC 芯片与 SD 卡相似,因为它们共享相似的接口;eMMC 芯片最多支持 8 条数据线,而 SD 卡最多支持 4 条数据线。eMMC芯片和SD卡都支持仅使用一条数据线,但读/写速度较低。

To read the eMMC chip in-circuit we have to identify the clock (CLK), command (CMD) and data 0 (D0) signals. The 10 test points above the main SoC drew our attention, as 10 test points could be a CMD, CLK and 8 data lines. Additionally, all of these test points have a 30 Ohm series resistor connected to them which is relatively common for eMMC connections. We soldered a short wire to each test point, allowing us to create a logic analyser capture during the UT boot process. Using such a capture it is relatively straightforward to identify the required signals. The CLK signal will be the only repetitive signal, CMD is the signal that is first active after the clock starts toggling and D0 is the first data line to send out data. Determining the remaining 7 data lines is luckily unnecessary to dump the eMMC contents.
为了在线读取eMMC芯片,我们必须识别时钟(CLK)、命令(CMD)和数据0(D0)信号。主 SoC 上方的 10 个测试点引起了我们的注意,因为 10 个测试点可能是 CMD、CLK 和 8 条数据线。此外,所有这些测试点都连接了一个 30 欧姆串联电阻器,这对于 eMMC 连接来说相对常见。我们在每个测试点上焊接了一根短线,使我们能够在UT启动过程中创建逻辑分析器捕获。使用这种捕获方式,识别所需的信号相对简单。CLK 信号是唯一的重复信号,CMD 是时钟开始切换后首先激活的信号,D0 是发送数据的第一条数据线。幸运的是,确定剩余的 7 条数据线对于转储 eMMC 内容是不必要的。

Dumping the eMMC in-circuit
转储 eMMC 在线

To dump the eMMC chip we can connect a reader (that supports 1.8V IO) to the identified test points. Commercial readers that are mostly aimed at phone repair exist and should work well for this purpose (e.g. easy-JTAG and Medusa Pro). Alternatively you can use a regular USB SD-card reader (one that supports 1-bit mode) with an SD-card breakout with integrated level-shifters (e.g. https://shop.exploitee.rs/shop/p/low-voltage-emmc-adapter ). You can also whip something up yourself if you have some parts laying around. The picture below shows a standard USB SD-card reader connected to a TI TXS0202EVM level-shifter breakout board.
为了转储eMMC芯片,我们可以将读卡器(支持1.8V IO)连接到确定的测试点。存在主要针对手机维修的商业阅读器,并且应该可以很好地用于此目的(例如easy-JTAG和Medusa Pro)。或者,您可以使用普通的 USB SD 卡读卡器(支持 1 位模式的读卡器)和带有集成电平转换器的 SD 卡分支(例如 https://shop.exploitee.rs/shop/p/low-voltage-emmc-adapter )。如果你有一些零件,你也可以自己鞭打一些东西。下图显示了连接到 TI TXS0202EVM 电平转换器分线板的标准 USB SD 卡读卡器。


We only provide power to the eMMC to prevent the main SoC from interfering. The eMMC can be powered through two nearby decoupling capacitors, 3.3V is provided by the SD card reader and 1.8V is provided using a lab power supply. Once everything is hooked up properly we can create a disk image for later analysis.
我们只为 eMMC 供电,以防止主 SoC 干扰。eMMC可通过两个附近的去耦电容供电,3.3V由SD读卡器提供,1.8V由实验室电源提供。一旦一切都正确连接,我们就可以创建一个磁盘映像以供以后分析。


Note that reading eMMC in circuit is not always an easy task; wires that are slightly too long can already prevent reading from succeeding. In this case it was rather straightforward and the system appears to function normally even with these relatively long wires attached.
请注意,在电路中读取eMMC并不总是一件容易的事;稍微太长的电线已经会阻止读取成功。在这种情况下,它相当简单,即使连接了这些相对较长的电线,系统似乎也能正常运行。

Unpacking the raw eMMC dump
解压缩原始 eMMC 转储

Unfortunately, Binwalk was not able to extract the full filesystem hence a manual analysis was required.
不幸的是,Binwalk 无法提取完整的文件系统,因此需要手动分析。

From the boot log it was clear that U-Boot was loading 49152 blocks of data starting at block 98304. Meaning that U-Boot is reading 0x1800000 bytes (blocksize of 512 (0x200) bytes) starting from address 0x3000000. We also know from the U-Boot output that this chunk of data is a FIT image. However, when trying to read the FIT image header information using the dumpimage tool (part of the u-boot-tools package) we weren’t getting any useful information.
从引导日志中可以清楚地看出,U-Boot从98304块开始加载了49152个数据块。这意味着 U-Boot 从地址 0x3000000 开始读取 0x1800000 字节(块大小为 512 (0x200) 字节)。我们还从 U-Boot 输出中知道,此数据块是 FIT 映像。但是,当尝试使用 dumpimage 工具(u-boot-tools 包的一部分)读取 FIT 映像标头信息时,我们没有得到任何有用的信息。

Luckily SpaceX released their modifications to U-Boot on Github for GPL compliance: https://github.com/SpaceExplorationTechnologies
幸运的是,SpaceX 在 Github 上发布了他们对 U-Boot 的修改,以符合 GPL 标准:https://github.com/SpaceExplorationTechnologies

By looking at this code it became clear that certain parts of the firmware are stored in a custom format that contains Error Correcting Code (ECC) data.
通过查看此代码,可以清楚地看出固件的某些部分以包含纠错码 (ECC) 数据的自定义格式存储。

Stripping Reed-Solomon ECC words
剥离 Reed-Solomon ECC 单词

The file spacex_catson_boot.h contains interesting information related to how the device boots.
该文件 spacex_catson_boot.h 包含与设备如何启动相关的有趣信息。

The following snippets show how data is being read from eMMC (mmc read8) and the definition for startkernel.
以下代码片段显示了如何从 eMMC ( mmc read8 ) 读取数据以及 的定义 startkernel 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define SPACEX_CATSON_COMMON_BOOT_SETTINGS \
    "kernel_boot_addr=" __stringify(CATS_KERNEL_BOOT_ADDR) "\0" \
    "kernel_load_addr=" __stringify(CATS_KERNEL_LOAD_ADDR) "\0" \
    "kernel_offset_a=" __stringify(CATS_KERNEL_A_OFFSET) "\0" \
    "kernel_offset_b=" __stringify(CATS_KERNEL_B_OFFSET) "\0" \
    "kernel_size=" __stringify(CATS_KERNEL_A_SIZE) "\0" \
    "setup_burn_memory=mw.q " __stringify(CATS_TERM_SCRATCH_ADDR) " 0x12345678aa640001 && " \
        "mw.l " __stringify(CATS_TERM_LOAD_ADDR) " 0xffffffff " __stringify(CATS_BOOTTERM1_SIZE) " && " \
        "mw.l " __stringify(CATS_TERM_TOC_SER_ADDR) " " __stringify(CATS_TERM_TOC_SER_VAL) "\0" \
    "startkernel=unecc $kernel_load_addr $kernel_boot_addr && bootm $kernel_boot_addr${boot_type}\0" \
    "stdin=nulldev\0"
#define SPACEX_CATSON_BOOT_SETTINGS \
    SPACEX_CATSON_COMMON_BOOT_SETTINGS \
    "_emmcboot=mmc dev " __stringify(CATS_MMC_BOOT_DEV) " " __stringify(CATS_MMC_BOOT_PART) " && " \
        "mmc read8 $kernel_load_addr ${_kernel_offset} $kernel_size && " \
        "run startkernel\0" \
    "emmcboot_a=setenv _kernel_offset $kernel_offset_a && run _emmcboot\0" \
    "emmcboot_b=setenv _kernel_offset $kernel_offset_b && run _emmcboot\0"

The definition of startkernel is particularly interesting as it shows how the address where the kernel was loaded is being passed to a command called unecc. From the unecc command definition it is quite clear that this functionality is performing error correction on the data read from the eMMC.
的定义 startkernel 特别有趣,因为它显示了加载内核的地址如何传递给名为 unecc 的命令。从 unecc 命令定义中可以清楚地看出,此功能是对从 eMMC 读取的数据执行纠错。

1
2
3
4
5
6
7
8
9
10
U_BOOT_CMD(
    unecc, 3, 0, do_unecc,
    "Unpacks an ECC volume; increments internal ECC error counter on error",
    "<source> <target>\n"
    "\tReturns successfully if the given source was successfully\n"
    "\tunpacked to the target. This will fail if the given source\n"
    "\tis not an ECC volume. It will succeed if bit errors were\n"
    "\tsuccessfully fixed.\n"
    "\t<source> and <target> should both be in hexadecimal.\n"
);

The unecc command calls the do_unecc function implemented in unecc.c. Eventually this will result in calling the ecc_decode_one_pass function defined in ecc.c.
该 unecc 命令调用 中 unecc.c 实现的 do_unecc 函数。最终,这将导致调用 中 ecc.c 定义的 ecc_decode_one_pass 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * Decodes an ECC protected block of memory. If the enable_correction
 * parameter is zero, it will use the MD5 checksum to detect errors and will
 * ignore the ECC bits. Otherwise, it will use the ECC bits to correct any
 * errors and still use the MD5 checksum to detect remaining problems.
 *
 * @data:       Pointer to the input data.
 * @size:       The length of the input data, or 0 to read until the
 *          end of the ECC stream.
 * @dest:       The destination for the decoded data.
 * @decoded[out]:   An optional pointer to store the length of the decoded
 *          data.
 * @silent:     Whether to call print routines or not.
 * @enable_correction:  Indicates that the ECC data should be used
 *          to correct errors. Otherwise the MD5 checksum
 *          will be used to check for an error.
 * @error_count[out]:   Pointer to an integer that will be incremented
 *          by the number of errors found. May be NULL.
 *          Unused if !enable_correction.
 *
 * Return: 1 if the block was successfully decoded, 0 if we had a
 * failure, -1 if the very first block didn't decode (i.e. probably
 * not an ECC file)
 */
static int ecc_decode_one_pass(const void *data, unsigned long size, void *dest,
                   unsigned long *decoded, int silent,
                   int enable_correction, unsigned int *error_count)

ecc.h contains several relevant definitions:
ecc.h 包含几个相关定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#else /* !NPAR */
#define NPAR            32
#endif /* NPAR */
/*
 * These options must be synchronized with the userspace "ecc"
 * utility's configuration options. See ecc/trunk/include/ecc.h in the
 * "util" submodule of the platform.
 */
#define ECC_BLOCK_SIZE      255
#define ECC_MD5_LEN     16
#define ECC_EXTENSION       "ecc"
#define ECC_FILE_MAGIC      "SXECCv"
#define ECC_FILE_VERSION    '1'
#define ECC_FILE_MAGIC_LEN  (sizeof(ECC_FILE_MAGIC) - 1)
#define ECC_FILE_FOOTER_LEN sizeof(file_footer_t)
#define ECC_DAT_SIZE        (ECC_BLOCK_SIZE - NPAR - 1)
#define ECC_BLOCK_TYPE_DATA '*'
#define ECC_BLOCK_TYPE_LAST '$'
#define ECC_BLOCK_TYPE_FOOTER   '!'

In the end what it boils down to is that, in this implementation, an ECC protected block of memory starts with the magic header value SXECCv followed by a version byte (1). This magic value marks the start of the ECC protected data, but also the start of the header block. The header block itself contains (in addition to the magic value and version byte), 215 bytes of data, an asterisk (*), and 32 bytes of ECC code words.
最后,它归结为,在此实现中,受 ECC 保护的内存块以魔术标头值 SXECCv 开头,后跟版本字节 ( 1 )。此幻值标志着受 ECC 保护的数据的开始,同时也是标头块的开始。标头块本身包含(除了幻值和版本字节之外)、215 字节的数据、一个星号 ( * ) 和 32 个字节的 ECC 代码字。

The header block is followed by multiple data blocks. Each of these data blocks is 255 bytes long and contains 222 bytes of data followed by an asterisk symbol (*) and 32 bytes of ECC code words. The last data block contains a dollar sign ($) instead of the asterisk and is followed by a final footer block. This footer block starts with and exclamation mark (!) that is followed by the number of data bytes in the ECC protected block of memory (4-bytes) and MD5 digest over those data bytes.
标头块后面跟着多个数据块。每个数据块的长度为 255 字节,包含 222 字节的数据,后跟星号 ( * ) 和 32 字节的 ECC 码字。最后一个数据块包含美元符号 ( $ ) 而不是星号,后面是最后一个页脚块。此页脚块以 和 感叹号 ( ! ) 开头,后跟受 ECC 保护的内存块中的数据字节数(4 字节)和这些数据字节的 MD5 摘要。

At this point it should be clear why Binwalk did not succeed in extracting the kernel, initramfs and FDT. Binwalk is able to pick up on the magic values that indicate the start of a particular file, but each block of the file had additional data that prevented Binwalk from extracting it. We used a simple Python scrip to remove the extra ECC data before using Binwalk to extract the image. Similarly, we can now also use dumpimage to get more information on the FIT image.
在这一点上,应该很清楚为什么 Binwalk 没有成功提取内核、initramfs 和 FDT。Binwalk 能够获取指示特定文件开头的魔术值,但文件的每个块都有额外的数据,阻止 Binwalk 提取它。在使用 Binwalk 提取图像之前,我们使用一个简单的 Python 脚本来删除额外的 ECC 数据。同样,我们现在也可以使用 dumpimage 来获取有关 FIT 映像的更多信息。

The FIT image and board revisions
FIT映像和电路板修订版

The following snippet contains some of the dumpimage output. The FIT image contains 13 boot configurations, all configurations use the same kernel and initramfs images but a different Flattened Device Tree (FDT).
以下代码片段包含一些转储图像输出。FIT 映像包含 13 个引导配置,所有配置都使用相同的内核和 initramfs 映像,但使用不同的扁平化设备树 (FDT)。

FIT description: Signed dev image for catson platforms
Created:         Fri Apr 16 23:10:45 2021
 Image 0 (kernel@1)
  Description:  compressed kernel
  Created:      Fri Apr 16 23:10:45 2021
  Type:         Kernel Image
  Compression:  lzma compressed
  Data Size:    3520634 Bytes = 3438.12 KiB = 3.36 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0x80080000
  Entry Point:  0x80080000
  Hash algo:    sha256
  Hash value:   5efc55925a69298638157156bf118357e01435c9f9299743954af25a2638adc2
Image 12 (rev2_proto2_fdt@1)
  Description:  rev2 proto 2 device tree
  Created:      Fri Apr 16 23:10:45 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    59720 Bytes = 58.32 KiB = 0.06 MiB
  Architecture: AArch64
  Load Address: 0x8f000000
  Hash algo:    sha256
  Hash value:   cca3af2e3bbaa1ef915d474eb9034a770b01d780ace925c6e82efa579334dea8
 Image 15 (ramdisk@1)
  Description:  compressed ramdisk
  Created:      Fri Apr 16 23:10:45 2021
  Type:         RAMDisk Image
  Compression:  lzma compressed
  Data Size:    8093203 Bytes = 7903.52 KiB = 7.72 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0xb0000000
  Entry Point:  0xb0000000
  Hash algo:    sha256
  Hash value:   57020a8dbff20b861a4623cd73ac881e852d257b7dda3fc29ea8d795fac722aa
 
Default Configuration: 'rev2_proto2@1'
 Configuration 0 (utdev@1)
  Description:  default
  Kernel:       kernel@1
  Init Ramdisk: ramdisk@1
  FDT:          utdev3@1
  Sign algo:    sha256,rsa2048:dev
  Sign value:   bb34cc2512d5cd3b5ffeb5acace0c1b3dd4d960be3839c88df57c7aeb793ad73a74e87006efece4e9f1e31edbb671e2c63dc4cdcb1a2f55388d83a11f1074f21a1e48d81884a288909eb0c9015054213e5e74cbcc6a6d2617a720949dcac3166f1d01e3c2465d8e7461d14288f1a0abef22f80e2745e7f8499af46e8c007b825d72ab494f104df57433850f381be793bfe06302473269d2f45ce2ff2e8e4439017c0a94c5e7c6981b126a2768da555c86b2be136d4f5785b83193d39c9469bd24177be6ed3450b62d891a30e96d86eee33c2cbfc549d3826e6add36843f0933ced7c8e23085ee6106e3cc2af1e04d2153af5f371712854e91c8f33a4ea434269

From the U-Boot code (spacex_catson_uterm.c) it becomes clear that the boot configuration is decided based on the state of 5 GPIO pins.
从U-Boot代码( spacex_catson_uterm.c )可以清楚地看出,启动配置是根据5个GPIO引脚的状态决定的。

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  /**
   * Check board ID GPIOs to find board revision.
   * The board IDs are mapped as follows
   * id_b0:pio12[2]
   * id_b1:pio12[3]
   * id_b2:pio12[0]
   * id_b3:pio12[1]
   * id_b4:pio20[4]
   */
  u32 pio12 = readl(BACKBONE_PIO_A_PIO2_PIN);
  u32 pio20 = readl(BACKBONE_PIO_B_PIO0_PIN);
  u32 board_id = (((pio12 >> 2) & 1) << 0) |
             (((pio12 >> 3) & 1) << 1) |
         (((pio12 >> 0) & 1) << 2) |
         (((pio12 >> 1) & 1) << 3) |
         (((pio20 >> 4) & 1) << 4);
  /*
   * https://confluence/display/satellites/User+Terminal%3A+Catson+ID+Bits
   */
  switch (board_id)
  {
    case 0b11111:
      board_rev_string = BOARD_REV_1_1P3;
      break;
    case 0b11100:
      board_rev_string = BOARD_REV_1_2P1;
      break;
    case 0b11000:
      board_rev_string = BOARD_REV_1_2P2;
      break;
    case 0b10100:
      board_rev_string = BOARD_REV_1_3P0;
      break;
    case 0b10000: /* rev1 pre-production */
      board_rev_string = BOARD_REV_1_PRE_PROD;
      break;
    case 0b11110: /* rev1 production */
      board_rev_string = BOARD_REV_1_PROD;
      break;
    case 0b00001:
      board_rev_string = BOARD_REV_2_0P0;
      break;
    case 0b00010:
      board_rev_string = BOARD_REV_2_1P0;
      break;
    case 0b00011:
      board_rev_string = BOARD_REV_2_2P0;
      break;
  }
}
printf("Detected Board rev: %s\n", board_rev_string);

The following picture shows where these pins are being pulled high/low to indicate the board revision. Note from the earlier serial bootlog that our UT boots using the rev2_proto2 configuration (case 0b00011). In a recent video Colin O’Flynn pulled some of these pins high/low and could observe that the UT tried booting using a different FIT configuration and thus different devicetree [5]. We compared some of the FDTs but did not spot any differences that would be interesting from a security perspective.
下图显示了这些引脚被拉高/低的位置,以指示电路板修订。请注意,在前面的串行引导日志中,我们的 UT 使用 rev2_proto2 配置 ( case 0b00011 ) 引导。在最近的一段视频中,Colin O’Flynn 将其中一些引脚拉高/低,并观察到 UT 尝试使用不同的 FIT 配置启动,从而使用不同的设备树 [5]。我们比较了一些FDT,但没有发现任何从安全角度来看有趣的差异。

Dumping and extracting the SpaceX Starlink User Terminal firmware

A first look at the firmware
固件初探

The login prompt 登录提示

Recall that after the boot process has completed we are greeted with a login prompt. For further research it would be useful to gain the ability to log in, allowing us to interact with the live system. However, by looking at the shadow file it becomes clear that none of the users are allowed to log in.
回想一下,启动过程完成后,我们会收到登录提示。对于进一步的研究,获得登录能力将很有用,使我们能够与实时系统进行交互。但是,通过查看影子文件,可以清楚地看到任何用户都不允许登录。

During boot the UT does read a fuse to determine if it is development hardware or not. If the UT is unfused it will set a password for the root user, allowing log in.
在启动过程中,UT会读取保险丝,以确定它是否是开发硬件。如果 UT 未融合,它将为 root 用户设置密码,允许登录。

Starlink UTs that are sold to consumers are of course production fused, disabling the login prompt.
出售给消费者的 Starlink UT 当然是生产融合的,禁用登录提示。

1
2
3
4
5
6
7
8
9
10
root:*:10933:0:99999:7:::
bin:*:10933:0:99999:7:::
daemon:*:10933:0:99999:7:::
sync:*:10933:0:99999:7:::
halt:*:10933:0:99999:7:::
uucp:*:10933:0:99999:7:::
operator:*:10933:0:99999:7:::
ftp:*:10933:0:99999:7:::
nobody:*:10933:0:99999:7:::
sshd:*:::::::

Development hardware 开发硬件

Development hardware often finds its way into the wrong hands [6, 7]. The engineers at SpaceX considered this scenario and appear to try to actively detect unfused development hardware that is no longer under their control. Development hardware is geofenced to only work in certain predefined areas, most of which are clearly SpaceX locations. SpaceX is likely notified if development hardware is used outside these predefined geofences.
开发硬件经常落入坏人之手[6,7]。SpaceX的工程师考虑了这种情况,并试图主动检测不再受其控制的未融合开发硬件。开发硬件被地理围栏限制为仅在某些预定义区域工作,其中大部分显然是SpaceX的位置。如果在这些预定义的地理围栏之外使用开发硬件,SpaceX可能会收到通知。

Interestingly, some of these geofences do not seem to have a clear connection to SpaceX. While we will not disclose these locations here, I will say that the SNOW_RANCH looks like a nice location to play with development hardware.
有趣的是,其中一些地理围栏似乎与SpaceX没有明确的联系。虽然我们不会在这里透露这些位置,但我会说,这 SNOW_RANCH 看起来是一个使用开发硬件的好地方。

Secure element 安全元件

From references in the firmware it became clear that (our revision of) the UT contains a STMicroelectronics STSAFE secure element. The purpose of the secure element is not entirely clear yet, but it may be used to remotely authenticate the UT.
从固件中的参考资料可以清楚地看出,(我们对)UT的修订版包含STMicroelectronics STSAFE安全元件。安全元件的用途尚不完全清楚,但它可用于远程验证 UT。

Dumping and extracting the SpaceX Starlink User Terminal firmware

The SoC

Some people have asked which processor is being used: the answer is a Quad-Core Cortex-A53 and each core has been assigned a specific task.
有人问使用哪种处理器:答案是四核 Cortex-A53,每个内核都被分配了特定的任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
############################
# System Information
############################
#
# The user terminal phased-array computers are Catson SoCs with a quad-core
# Cortex-A53.
#
# We dedicate one core to control, while leaving the other three to handle
# interrupts and auxiliary processes.
#
#   CPU 0: Control process.
#   CPU 1: Lower-MAC RX process.
#   CPU 2: Lower-MAC TX process.
#   CPU 3: PhyFW and utility core - interrupts, auxiliary processes, miscellaneous

What’s next 后续步骤

That’s it for now. We will likely continue looking into the Starlink UT and provide more details in future blog posts if there is interest. At the time of writing we were able to obtain a root shell on the UT, but it’s too early to publicly share more information on that matter.
现在就是这样。如果有兴趣,我们可能会继续研究 Starlink UT,并在以后的博客文章中提供更多详细信息。在撰写本文时,我们能够获得UT的根shell,但现在公开分享有关此事的更多信息还为时过早。

References 引用

[1] MikeOnSpace – Starlink Dish TEARDOWN! – Part 1 – https://youtu.be/QudtSo5tpLk
[1] MikeOnSpace – Starlink Dish 拆解!– 第 1 部分 – https://youtu.be/QudtSo5tpLk

[2] Ken Keiter – Starlink Teardown: DISHY DESTROYED! – https://youtu.be/iOmdQnIlnRo
[2] 肯·凯特 – Starlink 拆解:DISHY 被摧毁!– https://youtu.be/iOmdQnIlnRo

[3] The Signal Path – Starlink Dish Phased Array Design, Architecture & RF In-depth Analysis – https://youtu.be/h6MfM8EFkGg
[3] 信号路径 – Starlink 碟形相控阵设计、架构和射频深入分析 – https://youtu.be/h6MfM8EFkGg

[4] MikeOnSpace – Starlink Dish TEARDOWN! – Part 2 – https://youtu.be/38_KTq8j0Nw
[4] MikeOnSpace – Starlink Dish 拆解!– 第 2 部分 – https://youtu.be/38_KTq8j0Nw

[5] Colin O’Flynn – Starlink Dishy (Rev2 HW) Teardown Part 1 – UART, Reset, Boot Glitches – https://youtu.be/omScudUro3s
[5] Colin O’Flynn – Starlink Dishy (Rev2 HW) 拆解第 1 部分 – UART、重置、启动故障 – https://youtu.be/omScudUro3s

[6] Brendan I. Koerner – The Teens Who Hacked Microsoft’s Xbox Empire – https://www.wired.com/story/xbox-underground-videogame-hackers/
[6] Brendan I. Koerner – 黑客入侵Microsoft Xbox 帝国的青少年 – https://www.wired.com/story/xbox-underground-videogame-hackers/

[7] Jack Rhysider – Darknet Diaries EP 45: XBOX UNDERGROUND (PART 1) – https://darknetdiaries.com/episode/45/
[7] 杰克·里西德 – 暗网日记 EP 45:XBOX UNDERGROUND(第 1 部分)– https://darknetdiaries.com/episode/45/

原文始发于Bluesky @cosic.bsky.social:Dumping and extracting the SpaceX Starlink User Terminal firmware

版权声明:admin 发表于 2024年4月30日 下午6:36。
转载请注明:Dumping and extracting the SpaceX Starlink User Terminal firmware | CTF导航

相关文章