Compare commits

..

No commits in common. "5d6204efa307db6c5e405f34df06203742def1ac" and "11bd8dd512d4bcbcedd0c7c46c6eff0ece23ece9" have entirely different histories.

12 changed files with 108 additions and 223 deletions

1
.gitignore vendored
View File

@ -1,6 +1,5 @@
dump/ dump/
*.bin *.bin
*.a *.a
venv/
reven/ reven/
!dump/exynos-usbdl/ !dump/exynos-usbdl/

View File

@ -1,8 +1,6 @@
.. _boot-chain-label:
======= =======
Booting Booting
======= =======
After exploitation the goal is to fully boot the device. After exploitation the goal is to fully boot the device.
Current boot chain: Current boot chain:

View File

@ -15,40 +15,12 @@ Samsung Firmware
---------------- ----------------
Samsung releases firmware files for their devices. These files contain the bootloader, modem, and other firmware files. Samsung releases firmware files for their devices. These files contain the bootloader, modem, and other firmware files.
To see how the ROM works we are interested in the sboot firmware, which contains multiple stages of the bootloader. To see how the ROM works we are interested in the sboot firmware, which contains multiple stages of the bootloader.
To extract the sboot.bin file from a samsung firmware file: To extract the sboot.bin file from a samsung firmware file:
.. code-block:: bash .. code-block:: bash
$ unzip -p firmware.zip 'BL_*.tar.md5' | tar -Oxf - 'sboot.bin.lz4' | lz4 -d - sboot.bin $ unzip -p firmware.zip 'BL_*.tar.md5' | tar -Oxf - 'sboot.bin.lz4' | lz4 -d - sboot.bin
Frederic has also written a payload to extract the sboot.bin file from a connected samsung device (See: :ref:`boot-chain-label`). The extracted boots can be split up in different stages. We're provied with sboot 1-4.bin. Running strings then provides us with some information about each stage.
.. code-block:: bash
$ strings -n4 sboot.bin.1.bin
was
.. list-table:: bootrom stages
:header-rows: 1
* - File
- Strings output
- Likely boot stage?
* - sboot.bin.1.bin
- Exynos BL1
- BL1
* - sboot.bin.2.bin
- BL31 %s
- BL31
* - sboot.bin.3.bin
- Unsure. Contains strings like: TOP_DIV_ACLK_MFC_600 and APOLLO_DIV_APOLLO_RUN_MONITOR
- BL2?
* - sboot.bin.4.bin
- Contains more textual information, and references to post BL2 boot, and android information
- Kernel boot/BL33?
Memory Layout Memory Layout
------------- -------------
TODO make memory layout of ROM, IMEM and some devices @JONHE TODO make memory layout of ROM, IMEM and some devices @JONHE

View File

@ -1,9 +1,9 @@
#Ghidra Lock File #Ghidra Lock File
#Fri Aug 09 11:27:43 CEST 2024 #Tue Aug 06 19:30:30 CEST 2024
OS\ Name=Linux OS\ Name=Linux
OS\ Version=6.5.0-44-generic OS\ Version=6.5.0-44-generic
Username=eljakim Username=eljakim
Hostname=levith Hostname=levith
<META>\ Supports\ File\ Channel\ Locking=Channel Lock <META>\ Supports\ File\ Channel\ Locking=Channel Lock
OS\ Architecture=amd64 OS\ Architecture=amd64
Timestamp=8/9/24, 11\:27 AM Timestamp=8/6/24, 7\:30 PM

View File

@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"program": "exploit.py", "program": "exploit.py",
"console": "integratedTerminal", "console": "integratedTerminal",
"args": ["--debug"] "args": ["--usb-debug"]
}, },
{ {
"name": "Run boot chain", "name": "Run boot chain",
@ -19,6 +19,15 @@
"program": "exploit.py", "program": "exploit.py",
"console": "integratedTerminal", "console": "integratedTerminal",
"justMyCode": false, "justMyCode": false,
"args": ["--run-boot-chain"]
},
{
"name": "Debug on device",
"type": "debugpy",
"request": "launch",
"program": "exploit.py",
"console": "integratedTerminal",
"justMyCode": false,
"args": [] "args": []
}, },
{ {

View File

@ -5,7 +5,7 @@ from ghidra_assistant.utils.utils import *
from ghidra_assistant.concrete_device import * from ghidra_assistant.concrete_device import *
from ghidra_assistant.utils.debugger.debugger_archs.ga_arm64 import GA_arm64_debugger from ghidra_assistant.utils.debugger.debugger_archs.ga_arm64 import GA_arm64_debugger
from qiling.const import QL_ARCH from qiling.const import QL_ARCH
import os, tqdm, datetime import os
def p32(x): def p32(x):
return struct.pack("<I", x) return struct.pack("<I", x)
@ -56,6 +56,7 @@ class ExynosDevice():
self.target = "8890" # TODO auto detect device self.target = "8890" # TODO auto detect device
self.connect_device() self.connect_device()
def connect_device(self): def connect_device(self):
"""Setup proper connection, and ensure the connection is alive""" """Setup proper connection, and ensure the connection is alive"""
self.context = usb1.USBContext() self.context = usb1.USBContext()
@ -80,11 +81,13 @@ class ExynosDevice():
raise e raise e
print(f"Connected device! {hex(self.idVendor)} {hex(self.idProduct)}") print(f"Connected device! {hex(self.idVendor)} {hex(self.idProduct)}")
def write(self, data): def write(self, data):
transferred = ctypes.c_int() transferred = ctypes.c_int()
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, data, len(data), ctypes.byref(transferred), 0) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, data, len(data), ctypes.byref(transferred), 0)
assert(res == 0), "Could not perform bulk transfer" assert(res == 0), "Could not perform bulk transfer"
return res return res
def send_empty_transfer(self): def send_empty_transfer(self):
transferred = ctypes.c_int() transferred = ctypes.c_int()
@ -92,6 +95,7 @@ class ExynosDevice():
assert(res == 0) assert(res == 0)
return transferred.value return transferred.value
def test_bug_2(self): def test_bug_2(self):
"""Interger overflow in last packet if reamining size is 1.""" """Interger overflow in last packet if reamining size is 1."""
transferred = ctypes.c_int() transferred = ctypes.c_int()
@ -109,6 +113,7 @@ class ExynosDevice():
while True: while True:
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10) res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
def test_bug(self): def test_bug(self):
# Start by sending a valid packet # Start by sending a valid packet
# Integer overflow in the size field # Integer overflow in the size field
@ -136,6 +141,7 @@ class ExynosDevice():
assert res == 0, "Error sending payload" assert res == 0, "Error sending payload"
pass pass
def exploit(self, payload: bytes): def exploit(self, payload: bytes):
''' '''
Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload. Exploit the Exynos device, payload of 502 bytes max. This will send stage1 payload.
@ -221,9 +227,10 @@ class ExynosDevice():
#Overwrite all calls to make the concrete target function properly #Overwrite all calls to make the concrete target function properly
concrete_device.copy_functions() concrete_device.copy_functions()
def usb_debug(self): def usb_debug(self):
""" """
Function to debug USB behaviour. Sends and receives data in continuous flow. Function to debug USB behaviour
""" """
transferred = ctypes.c_int() transferred = ctypes.c_int()
# Send some data # Send some data
@ -247,36 +254,85 @@ class ExynosDevice():
_recv_data() _recv_data()
count += 1 count += 1
def dump_memory(self, start: hex=0x0, end: hex=0x0206ffff, write=False):
"""
Dumps memory from the device.
Transfer XFER_BUFFER at 0x02021800, to: 0x02020F08. End of memory at 0x0206ffff.
"""
# NOT WORKING YET
transferred = ctypes.c_int()
dumped = b""
# Read data from memory
for block in tqdm.tqdm(range(start, end, 0x200)):
self.usb_write(p32(block-0x200))
res = self.usb_read(0x200)
dumped += res
if write: def dumb_interact(self):
filename = f"dump_{hex(start)}_{hex(end)}_{self.target}_{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.bin" '''
with open(filename, "wb") as f: Room for playing around with the debugger
f.write(dumped) '''
return dumped self.cd.arch_dbg.state.auto_sync = False
self.cd.arch_dbg.state.print_ctx()
# Overwrite jump back
# self.cd.memwrite_region(0x02020108, p32(0x2069000))
# self.cd.memwrite_region(0x02021800, p32(0x2069000))
self.cd.memwrite_region(0x020200e8, p32(0x02069000)) # address, data. Writes
AUTH_BL1 = 0x00012848
def memdump_try():
self.cd.arch_dbg.state.LR = 0x020200e8
self.cd.restore_stack_and_jump(0x02021810)
stack_pointer = 0x02021810
dumped = b""
for block in range(0x2020000, 0x2200000, 0x200):
stack_pointer += 0x200
self.cd.arch_dbg.state.print_ctx()
print(hex(block))
dumped += self.cd.memdump_region(block, 0x200)
if stack_pointer >= 0x02020F08:
print(f'stack_pointer at {stack_pointer}')
return dumped
def auth_bl1():
# Load the firmware
self.cd.arch_dbg.state.X0 = 1
self.cd.arch_dbg.state.X1 = 1
self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
self.cd.restore_stack_and_jump(AUTH_BL1)
def jump_bl1():
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x02024010)
# self.cd.restore_stack_and_jump(0x02021810)
#000125b4
# self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
# self.cd.restore_stack_and_jump(0x00012814)
# self.cd.restore_stack_and_jump(0x000125b4)
# transferred = ctypes.c_int() dumped = memdump_try()
# stack_pointer = 0x02021810
# for block in range(0x2020000, 0x2200000, 0x200): bl1 = open("../S7/bl1.bin", "rb").read()
# stack_pointer += 0x200 self.cd.memwrite_region(0x02024000, bl1)
# dumped += self.cd.memdump_region(block, 0x200) self.usb_write(b"FLSH")
# auth_bl1()
jump_bl1()
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
self.cd.arch_dbg.state.print_ctx()
def jump_bl31():
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x02021810)
bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl31)
jump_bl31()
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
self.cd.arch_dbg.state.print_ctx()
# memdump_try()
# auth_bl1()
self.cd.arch_dbg.state.print_ctx()
#authenticate it
pass
def setup_guppy_debugger(self): def setup_guppy_debugger(self):
""" """
Sets up guppy debugger on the device itself. Run the boot chain for the Exynos device.
Load and send stage1 payload - exploit (stage1)
""" """
def _setup_debugger(): def _setup_debugger():
@ -312,124 +368,26 @@ class ExynosDevice():
_initial_run_debugger() _initial_run_debugger()
_setup_debugger() _setup_debugger()
def dumb_interact(self):
'''
Room for playing around with the debugger
'''
self.cd.arch_dbg.state.auto_sync = False
self.cd.arch_dbg.state.auto_sync_special = False
self.cd.arch_dbg.state.print_ctx()
def relocate_debugger():
# Seems to be cleared upon cache clearing??
if os.getenv("USER") == "eljakim":
debugger_reloc = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read()
else:
try:
debugger_reloc = open("../../dump/debugger.bin", "rb").read()
except Exception as e:
print(f'Are you missing your debugger? Please ensure it is present in dump/debugger.bin. {e}')
sys.exit(0)
self.cd.memwrite_region(0x020c0000, debugger_reloc)
self.usb_write(b"FLSH") # Flush cache
self.cd.restore_stack_and_jump(0x020c0000)
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
self.cd.relocate_debugger(0x020c7000, 0x020c0000, 0x020c4000)
relocate_debugger()
# Try loading bl1
bl1 = open("../S7/bl1.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl1)
# self.usb_write(b"FLSH")
AUTH_BL1 = 0x00012848
def auth_bl1(lr=0x2069000):
# Load the firmware
self.cd.arch_dbg.state.W0 = 1
self.cd.arch_dbg.state.X1 = 1
self.cd.arch_dbg.state.LR = lr #jump back to debugger when finished
self.cd.restore_stack_and_jump(AUTH_BL1)
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
auth_bl1(0x020c0000)
# Works until here
pass
# Overwrite jump back
self.cd.memwrite_region(0x02020108, p32(0x2069000))
self.cd.memwrite_region(0x020200e8, p32(0x2069000))
def memdump_try():
self.cd.arch_dbg.state.LR = 0x020200e8
self.cd.restore_stack_and_jump(0x02021810)
stack_pointer = 0x02021810
dumped = b""
for block in range(0x2020000, 0x2200000, 0x200):
stack_pointer += 0x200
self.cd.arch_dbg.state.print_ctx()
print(hex(block))
dumped += self.cd.memdump_region(block, 0x200)
def jump_bl1():
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x02024010)
# self.cd.restore_stack_and_jump(0x02021810)
#000125b4
# self.cd.arch_dbg.state.LR = 0x2069000 #jump back to debugger when finished
# self.cd.restore_stack_and_jump(0x00012814)
# self.cd.restore_stack_and_jump(0x000125b4)
auth_bl1()
# auth_bl1()
jump_bl1()
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
self.cd.arch_dbg.state.print_ctx()
def jump_bl31():
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x02021810)
bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl31)
jump_bl31()
assert self.usb_read(0x200) == b"GiAs", "not jumped back to debugger?"
self.cd.arch_dbg.state.print_ctx()
# memdump_try()
# auth_bl1()
self.cd.arch_dbg.state.print_ctx()
#authenticate it
pass
if __name__ == "__main__": if __name__ == "__main__":
arg = argparse.ArgumentParser("Exynos exploit") arg = argparse.ArgumentParser("Exynos exploit")
arg.add_argument("--debug", action="store_true", help="Debug USB stack", default=False) arg.add_argument("--usb-debug", action="store_true", help="Debug USB stack", default=False)
arg.add_argument("--run-boot-chain", action="store_true", help="Run boot chain to boot different boot stages", default=False)
arg.add_argument("--dumb-debug", action="store_true", help="Live debugging on device", default=True)
args = arg.parse_args() args = arg.parse_args()
exynos = ExynosDevice() exynos = ExynosDevice()
if args.debug: if args.usb_debug:
shellcode = open("../dwc3_test/dwc3.bin", "rb").read() shellcode = open("../dwc3_test/dwc3.bin", "rb").read()
exynos.exploit(shellcode) exynos.exploit(shellcode)
exynos.dump_memory(write=True) exynos.usb_debug()
# exynos.usb_debug()
sys.exit(0)
stage1 = open("stage1/stage1.bin", "rb").read() elif args.run_boot_chain:
exynos.exploit(stage1) stage1 = open("stage1/stage1.bin", "rb").read()
exynos.setup_guppy_debugger() exynos.exploit(stage1)
exynos.dumb_interact() exynos.setup_guppy_debugger()
exynos.dumb_interact()
sys.exit(0) sys.exit(0)

View File

@ -8,21 +8,10 @@ OBJCOPY := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64
LD := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld.bfd LD := $(ANDROID_NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ld.bfd
#==================Target Samsung S7 (8890)================== #==================Target Samsung S7 (8890)==================
all: samsung_s7 samsung_s7_reloc
CFLAGS_SAMSUNGS7 = -Os -Idevices/samsung_s7/ CFLAGS_SAMSUNGS7 = -Os -Idevices/samsung_s7/
samsung_s7: samsung_s7:
[ -d bin/samsung_s7 ] || mkdir -p bin/samsung_s7/ [ -d bin/samsung_s7 ] || mkdir -p bin/samsung_s7/
$(CC) arm64_stub.S -c -o bin/samsung_s7/entry.o $(CFLAGS_SAMSUNGS7) $(CC) arm64_stub.S -c -o bin/samsung_s7/entry.o $(CFLAGS_SAMSUNGS7)
$(CC) debugger.c -c -o bin/samsung_s7/debugger.o $(CFLAGS_SAMSUNGS7) $(CC) debugger.c -c -o bin/samsung_s7/debugger.o $(CFLAGS_SAMSUNGS7)
$(LD) -T devices/samsung_s7/linkscript.ld bin/samsung_s7/entry.o bin/samsung_s7/debugger.o -o bin/samsung_s7/debugger.elf --just-symbols=devices/samsung_s7/symbols.txt $(LD) -T devices/samsung_s7/linkscript.ld bin/samsung_s7/entry.o bin/samsung_s7/debugger.o -o bin/samsung_s7/debugger.elf --just-symbols=devices/samsung_s7/symbols.txt
$(OBJCOPY) -O binary bin/samsung_s7/debugger.elf bin/samsung_s7/debugger.bin $(OBJCOPY) -O binary bin/samsung_s7/debugger.elf bin/samsung_s7/debugger.bin
CFLAGS_SAMSUNGS7_RELOC = -Os -DRELOC_DEBUGGER=1 -Idevices/samsung_s7/
samsung_s7_reloc:
[ -d bin/samsung_s7 ] || mkdir -p bin/samsung_s7/
$(CC) arm64_stub.S -c -o bin/samsung_s7/reloc_entry.o $(CFLAGS_SAMSUNGS7_RELOC)
$(CC) debugger.c -c -o bin/samsung_s7/reloc_debugger.o $(CFLAGS_SAMSUNGS7_RELOC)
$(LD) -T devices/samsung_s7/reloc_linkscript.ld bin/samsung_s7/reloc_entry.o bin/samsung_s7/reloc_debugger.o -o bin/samsung_s7/reloc_debugger.elf --just-symbols=devices/samsung_s7/reloc_symbols.txt
$(OBJCOPY) -O binary bin/samsung_s7/reloc_debugger.elf bin/samsung_s7/reloc_debugger.bin

View File

@ -1,14 +1,8 @@
# Gupje # Gupje
Current memory map: Current memory map:
## Stage 2
Memory map in stage2 after exploitation
![memory map](memory_map.drawio.svg) ![memory map](memory_map.drawio.svg)
## Stage 3
Memory map in stage3 after relocating the debugger
## Usage: ## Usage:
Copy this folder to <gupje>/devices/samsung_s7 and run: Copy this folder to <gupje>/devices/samsung_s7 and run:

View File

@ -8,8 +8,6 @@ extern int usb_event_handler(void);
extern uint32_t get_endpoint_recv_buffer(char endpoint); extern uint32_t get_endpoint_recv_buffer(char endpoint);
extern void exynos_sleep(int endpoint,uint32_t timeout); extern void exynos_sleep(int endpoint,uint32_t timeout);
extern void usb_send(uint32_t address,uint32_t size); extern void usb_send(uint32_t address,uint32_t size);
extern uint32_t recv_buffer;
extern uint32_t data_received;
int mystrlen(char *data) { int mystrlen(char *data) {
int i=0; int i=0;
@ -21,13 +19,9 @@ int mystrlen(char *data) {
return i-1; return i-1;
} }
#ifdef RELOC_DEBUGGER
#define recv_buffer 0x020c6200
#define data_received 0x020c6000
#else
#define recv_buffer 0x206fe00 //0x02021800 + 0x3000 #define recv_buffer 0x206fe00 //0x02021800 + 0x3000
#define data_received 0x206fd00 #define data_received 0x206fd00
#endif
void recv_data_cb(uint32_t endpoint, uint32_t len){ void recv_data_cb(uint32_t endpoint, uint32_t len){
char *dest_buf = (char *)recv_buffer; char *dest_buf = (char *)recv_buffer;

View File

@ -1,14 +0,0 @@
MEMORY {
ROM (rwx): ORIGIN = 0x020c0000, LENGTH = 0x1000
}
SECTIONS
{
. = 0x020c0000;
.text . : {
*(.text*)
*(.data*)
*(.rodata*)
} >ROM
}

View File

@ -1,12 +0,0 @@
debugger_storage = 0x020c4000;
debugger_stack = 0x020c2000;
debugger_entry = 0x020c0000;
maybe_usb_setup_read = 0x00006f88;
dwc3_ep0_start_trans = 0x0000791c;
usb_event_handler = 0x00007bac;
get_endpoint_recv_buffer = 0x00007a7c;
exynos_sleep = 0x000027c8;
g_recv_buffer = 0x020c6200;
g_data_received = 0x020c6000;

View File

@ -6,6 +6,4 @@ maybe_usb_setup_read = 0x00006f88;
dwc3_ep0_start_trans = 0x0000791c; dwc3_ep0_start_trans = 0x0000791c;
usb_event_handler = 0x00007bac; usb_event_handler = 0x00007bac;
get_endpoint_recv_buffer = 0x00007a7c; get_endpoint_recv_buffer = 0x00007a7c;
exynos_sleep = 0x000027c8; exynos_sleep = 0x000027c8;
RELOCATED = 0;