今天繼續往上層結構看,試著寫寫看 bootloader。從Linux userspace 到 Bootloader 理解系統在 Kernel 啟動前到底做了什麼。
回顧一下Day2 架構中提及的介紹,bootloader 是在進到kernel前的程式,負責硬體初始化完成後,從 Flash、eMMC、SD、USB 或網路載入 Kernel / DTB / initramfs,並把控制權交給 Linux。
e.g., linux embed最常見的U-boot、CFE、甚至是廠商自定義的 bootloader 也有
對漏洞研究來說,它同時提供 Environment、boot command、recovery、network boot 與 image verification,因此也是很重要的攻擊面。
備註: RTOS 系統的話通常 bootloader 結束會直接接到啟動 RTOS firmware,甚至是直接從 Boot ROM 直接接。
常見 embedded Linux 的開機流程大概長這樣:
┌──────────────────────────────────────────────────────────────┐
│ Boot Chain │
├──────────────────────────────────────────────────────────────┤
│ │
│ Power on │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Boot ROM │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ SPL / SBL │ 初始化 DRAM / flash │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ U-Boot / CFE │ env / bootargs / recovery │
│ └──────┬───────┘ │
│ │ │
│ ├──────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌────────────────────┐ │
│ │ Signed FIT │ │ Recovery / TFTP / │ │
│ │ kernel + dtb │ │ USB / altbootcmd │ │
│ └──────┬───────┘ └─────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Linux Kernel + DTB + initramfs / RootFS │ │
│ └────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Boot ROM 通常燒在 SoC 裡。SPL / SBL 負責早期初始化,例如 DRAM 和 flash。真正常見、也最容易和 Secure Boot 討論接在一起的,是 U-Boot、CFE 或 vendor 自己做的 bootloader。
| 階段 | 來源 | 主要攻擊面 |
|---|---|---|
| Boot ROM | SoC 內建 ROM | boot mode、fuse、晶片缺陷 |
| SPL / SBL | flash / eMMC | early loader、DRAM init、下一階段載入 |
| U-Boot / CFE | flash / eMMC | env、command、image loading、recovery |
| Kernel + DTB | flash / FIT image | bootargs、device tree、initramfs |
| RootFS | flash partition | /sbin/init、service、設定檔 |
U-Boot 的一些參數簡介
bootcmd:自動開機時要執行的指令序列
bootcmd 決定正常開機路徑。若是改寫 bootcmd,就能把開機流程導向攻擊者控制的 image。bootargs:傳給 Linux kernel 的命令列參數
bootargs 則是直接餵給 kernel 的參數。有一些案例中也會把 /bin/sh 當成第一個 userspace processbootdelay:自動開機前等待幾秒
bootdelay 決定能不能中斷自動開機,比如接上UART debug 的時候就可以跟蹤bootup 流程,進入 U-Boot prompt,下一些觀察指令 e.g., printenv、setenv、bootm、tftpboot。(具體下一個case,大概後天會具體 demo 一下)serverip / ipaddr:TFTP / NFS 相關bootpart:從哪個分區開機altbootcmd:fallback / recovery 時執行什麼stdin / stdout:console 輸入輸出位置討論到 bootloader 就會順便提到另一個功能(攻擊面),Secure Boot
理想的驗證流程大概是這樣:
Boot ROM
↓ verify
SPL / First-stage Bootloader
↓ verify
U-Boot / Second-stage Bootloader
↓ verify
kernel / DTB / initramfs
↓
rootfs / init
不過實際設備的驗證鏈不一定長這樣。有些 SoC 會由 Boot ROM 直接驗證完整 Boot Image;有些則由 U-Boot 驗證 FIT Image,將 kernel、DTB 與 initramfs 一起納入簽章範圍。
分析方式通常是去驗證每一層有沒有被確實地檢驗,還有加密與驗證是否都有使用安全的元件/實作,以及在這個流程之中是否有潛在的攻擊面,或是沒有處理到的風險。e.g., DTB、initramfs、configuration 是否也包含在簽章範圍?rootfs 是否另外受到 dm-verity、fs-verity 或其他完整性機制保護?Public key 是否寫死在 Bootloader?
還有後續的fallback tracing 也可以去看看
bootargs(例如 init=/bin/sh)(大概舉幾個例子,明天的復現再詳細展開解說)
現代 U-Boot 常見的 verified boot 實作是 FIT(Flattened Image Tree),結構大致如下:
FIT image
├─ images
│ ├─ kernel-1
│ │ ├─ data
│ │ └─ hash-1
│ ├─ fdt-1
│ │ ├─ data
│ │ └─ hash-1
│ └─ ramdisk-1
│ ├─ data
│ └─ hash-1
├─ configurations
│ └─ conf-1
│ ├─ kernel = kernel-1
│ ├─ fdt = fdt-1
│ └─ ramdisk = ramdisk-1
└─ signatures
└─ signature-1
├─ algo
├─ key-name-hint
└─ value
┌──── env
│
├──── bootargs
Attacker input ──┼──── DTB
│
├──── recovery image
│
└──── network boot
│
▼
Bootloader
│
verification
│
▼
Kernel
這邊可以去看看 kernel / dtb / ramdisk / configuration 哪些真的被 hash / signature 保護,哪些只是被引用但沒有納入簽章。
通常有幾種來源:
Loading kernel from FIT Image at ...,可反推出 FIT 在哪個 partition / offset寫得有點拉,但希望明天的復現可以給出更好的案例分享不屯稿小丑完全作死
[1] https://zhuanlan.zhihu.com/p/615517515
[2] https://blog.csdn.net/michaelwubo/article/details/47418639