Skip to content

Commit 64fd818

Browse files
committed
Land rapid7#4411, @bcook-r7's support for direct, atomic registry key access in meterpreter
2 parents 80b7643 + cdf5fec commit 64fd818

File tree

3 files changed

+159
-30
lines changed

3 files changed

+159
-30
lines changed

data/meterpreter/ext_server_stdapi.py

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,10 +1334,12 @@ def stdapi_net_socket_tcp_shutdown(request, response):
13341334
channel.shutdown(how)
13351335
return ERROR_SUCCESS, response
13361336

1337+
def _wreg_close_key(hkey):
1338+
ctypes.windll.advapi32.RegCloseKey(hkey)
1339+
13371340
@meterpreter.register_function_windll
13381341
def stdapi_registry_close_key(request, response):
1339-
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1340-
result = ctypes.windll.advapi32.RegCloseKey(hkey)
1342+
_wreg_close_key(packet_get_tlv(request, TLV_TYPE_HKEY)['value'])
13411343
return ERROR_SUCCESS, response
13421344

13431345
@meterpreter.register_function_windll
@@ -1372,11 +1374,9 @@ def stdapi_registry_delete_value(request, response):
13721374
result = ctypes.windll.advapi32.RegDeleteValueA(root_key, ctypes.byref(value_name))
13731375
return result, response
13741376

1375-
@meterpreter.register_function_windll
1376-
def stdapi_registry_enum_key(request, response):
1377+
def _wreg_enum_key(request, response, hkey):
13771378
ERROR_MORE_DATA = 0xea
13781379
ERROR_NO_MORE_ITEMS = 0x0103
1379-
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
13801380
name = (ctypes.c_char * 4096)()
13811381
index = 0
13821382
tries = 0
@@ -1399,10 +1399,22 @@ def stdapi_registry_enum_key(request, response):
13991399
return result, response
14001400

14011401
@meterpreter.register_function_windll
1402-
def stdapi_registry_enum_value(request, response):
1402+
def stdapi_registry_enum_key(request, response):
1403+
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1404+
return _wreg_enum_key(request, response, hkey)
1405+
1406+
@meterpreter.register_function_windll
1407+
def stdapi_registry_enum_key_direct(request, response):
1408+
err, hkey = _wreg_open_key(request)
1409+
if err != ERROR_SUCCESS:
1410+
return err, response
1411+
ret = _wreg_enum_key(request, response, hkey)
1412+
_wreg_close_key(hkey)
1413+
return ret
1414+
1415+
def _wreg_enum_value(request, response, hkey):
14031416
ERROR_MORE_DATA = 0xea
14041417
ERROR_NO_MORE_ITEMS = 0x0103
1405-
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
14061418
name = (ctypes.c_char * 4096)()
14071419
name_sz = ctypes.c_uint32()
14081420
index = 0
@@ -1426,6 +1438,20 @@ def stdapi_registry_enum_value(request, response):
14261438
index += 1
14271439
return result, response
14281440

1441+
@meterpreter.register_function_windll
1442+
def stdapi_registry_enum_value(request, response):
1443+
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1444+
return _wreg_enum_value(request, response, hkey)
1445+
1446+
@meterpreter.register_function_windll
1447+
def stdapi_registry_enum_value_direct(request, response):
1448+
err, hkey = _wreg_open_key(request)
1449+
if err != ERROR_SUCCESS:
1450+
return err, response
1451+
ret = _wreg_enum_value(request, response, hkey)
1452+
_wreg_close_key(hkey)
1453+
return ret
1454+
14291455
@meterpreter.register_function_windll
14301456
def stdapi_registry_load_key(request, response):
14311457
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)
@@ -1434,16 +1460,22 @@ def stdapi_registry_load_key(request, response):
14341460
result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name)
14351461
return result, response
14361462

1437-
@meterpreter.register_function_windll
1438-
def stdapi_registry_open_key(request, response):
1463+
def _wreg_open_key(request):
14391464
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
14401465
base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
14411466
base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
14421467
permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS)
14431468
handle_id = ctypes.c_void_p()
14441469
if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS:
1445-
return error_result_windows(), response
1446-
response += tlv_pack(TLV_TYPE_HKEY, handle_id.value)
1470+
return error_result_windows(), 0
1471+
return ERROR_SUCCESS, handle_id.value
1472+
1473+
@meterpreter.register_function_windll
1474+
def stdapi_registry_open_key(request, response):
1475+
err, hkey = _wreg_open_key(request)
1476+
if err != ERROR_SUCCESS:
1477+
return err, response
1478+
response += tlv_pack(TLV_TYPE_HKEY, hkey)
14471479
return ERROR_SUCCESS, response
14481480

14491481
@meterpreter.register_function_windll
@@ -1467,9 +1499,7 @@ def stdapi_registry_query_class(request, response):
14671499
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data))
14681500
return ERROR_SUCCESS, response
14691501

1470-
@meterpreter.register_function_windll
1471-
def stdapi_registry_query_value(request, response):
1472-
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1502+
def _query_value(request, response, hkey):
14731503
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
14741504
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
14751505
value_type = ctypes.c_uint32()
@@ -1496,15 +1526,41 @@ def stdapi_registry_query_value(request, response):
14961526
return error_result_windows(), response
14971527

14981528
@meterpreter.register_function_windll
1499-
def stdapi_registry_set_value(request, response):
1529+
def stdapi_registry_query_value(request, response):
15001530
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1531+
return _query_value(request, response, hkey)
1532+
1533+
@meterpreter.register_function_windll
1534+
def stdapi_registry_query_value_direct(request, response):
1535+
err, hkey = _wreg_open_key(request)
1536+
if err != ERROR_SUCCESS:
1537+
return err, response
1538+
ret = _query_value(request, response, hkey)
1539+
_wreg_close_key(hkey)
1540+
return ret
1541+
1542+
def _set_value(request, response, hkey):
15011543
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
15021544
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
15031545
value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value']
15041546
value_data = packet_get_tlv(request, TLV_TYPE_VALUE_DATA)['value']
15051547
result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data))
15061548
return result, response
15071549

1550+
@meterpreter.register_function_windll
1551+
def stdapi_registry_set_value(request, response):
1552+
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
1553+
return _set_value(request, response, hkey)
1554+
1555+
@meterpreter.register_function_windll
1556+
def stdapi_registry_set_value_direct(request, response):
1557+
err, hkey = _wreg_open_key(request)
1558+
if err != ERROR_SUCCESS:
1559+
return err, response
1560+
ret = _set_value(request, response, hkey)
1561+
_wreg_close_key(hkey)
1562+
return ret
1563+
15081564
@meterpreter.register_function_windll
15091565
def stdapi_registry_unload_key(request, response):
15101566
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']

lib/msf/core/post/windows/registry.rb

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -439,12 +439,10 @@ def meterpreter_registry_enumkeys(key, view)
439439
subkeys = []
440440
root_key, base_key = session.sys.registry.splitkey(key)
441441
perms = meterpreter_registry_perms(KEY_READ, view)
442-
open_key = session.sys.registry.open_key(root_key, base_key, perms)
443-
keys = open_key.enum_key
442+
keys = session.sys.registry.enum_key_direct(root_key, base_key, perms)
444443
keys.each { |subkey|
445444
subkeys << subkey
446445
}
447-
open_key.close
448446
return subkeys
449447
rescue Rex::Post::Meterpreter::RequestError => e
450448
return nil
@@ -460,12 +458,10 @@ def meterpreter_registry_enumvals(key, view)
460458
vals = {}
461459
root_key, base_key = session.sys.registry.splitkey(key)
462460
perms = meterpreter_registry_perms(KEY_READ, view)
463-
open_key = session.sys.registry.open_key(root_key, base_key, perms)
464-
vals = open_key.enum_value
461+
vals = session.sys.registry.enum_value_direct(root_key, base_key, perms)
465462
vals.each { |val|
466463
values << val.name
467464
}
468-
open_key.close
469465
return values
470466
rescue Rex::Post::Meterpreter::RequestError => e
471467
return nil
@@ -480,10 +476,8 @@ def meterpreter_registry_getvaldata(key, valname, view)
480476
value = nil
481477
root_key, base_key = session.sys.registry.splitkey(key)
482478
perms = meterpreter_registry_perms(KEY_READ, view)
483-
open_key = session.sys.registry.open_key(root_key, base_key, perms)
484-
v = open_key.query_value(valname)
479+
v = session.sys.registry.query_value_direct(root_key, base_key, valname, perms)
485480
value = v.data
486-
open_key.close
487481
rescue Rex::Post::Meterpreter::RequestError => e
488482
return nil
489483
end
@@ -516,9 +510,8 @@ def meterpreter_registry_setvaldata(key, valname, data, type, view)
516510
begin
517511
root_key, base_key = session.sys.registry.splitkey(key)
518512
perms = meterpreter_registry_perms(KEY_WRITE, view)
519-
open_key = session.sys.registry.open_key(root_key, base_key, perms)
520-
open_key.set_value(valname, session.sys.registry.type2str(type), data)
521-
open_key.close
513+
session.sys.registry.set_value_direct(root_key, base_key,
514+
valname, session.sys.registry.type2str(type), data, perms)
522515
return true
523516
rescue Rex::Post::Meterpreter::RequestError => e
524517
return nil

lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ def Registry.open_remote_key(target_host, root_key)
8989
request.add_tlv(TLV_TYPE_TARGET_HOST, target_host)
9090
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
9191

92-
9392
response = client.send_request(request)
9493

9594
return Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RemoteRegistryKey.new(
@@ -166,6 +165,24 @@ def Registry.enum_key(hkey)
166165
return keys
167166
end
168167

168+
def Registry.enum_key_direct(root_key, base_key, perm = KEY_READ)
169+
request = Packet.create_request('stdapi_registry_enum_key_direct')
170+
keys = []
171+
172+
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
173+
request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
174+
request.add_tlv(TLV_TYPE_PERMISSION, perm)
175+
176+
response = client.send_request(request)
177+
178+
# Enumerate through all of the registry keys
179+
response.each(TLV_TYPE_KEY_NAME) do |key_name|
180+
keys << key_name.value
181+
end
182+
183+
keys
184+
end
185+
169186
##
170187
#
171188
# Registry value interaction
@@ -195,10 +212,55 @@ def Registry.set_value(hkey, name, type, data)
195212
return true
196213
end
197214

215+
def Registry.set_value_direct(root_key, base_key, name, type, data, perm = KEY_WRITE)
216+
request = Packet.create_request('stdapi_registry_set_value_direct')
217+
218+
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
219+
request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
220+
request.add_tlv(TLV_TYPE_PERMISSION, perm)
221+
request.add_tlv(TLV_TYPE_VALUE_NAME, name)
222+
request.add_tlv(TLV_TYPE_VALUE_TYPE, type)
223+
224+
if type == REG_SZ
225+
data += "\x00"
226+
elsif type == REG_DWORD
227+
data = [data.to_i].pack('V')
228+
end
229+
230+
request.add_tlv(TLV_TYPE_VALUE_DATA, data)
231+
232+
response = client.send_request(request)
233+
234+
true
235+
end
236+
198237
#
199238
# Queries the registry value supplied in name and returns an
200239
# initialized RegistryValue instance if a match is found.
201240
#
241+
def Registry.query_value_direct(root_key, base_key, name, perm = KEY_READ)
242+
request = Packet.create_request('stdapi_registry_query_value_direct')
243+
244+
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
245+
request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
246+
request.add_tlv(TLV_TYPE_PERMISSION, perm)
247+
request.add_tlv(TLV_TYPE_VALUE_NAME, name)
248+
249+
response = client.send_request(request)
250+
251+
type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value
252+
data = response.get_tlv(TLV_TYPE_VALUE_DATA).value
253+
254+
if type == REG_SZ
255+
data = data[0..-2]
256+
elsif type == REG_DWORD
257+
data = data.unpack('N')[0]
258+
end
259+
260+
Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new(
261+
client, 0, name, type, data)
262+
end
263+
202264
def Registry.query_value(hkey, name)
203265
request = Packet.create_request('stdapi_registry_query_value')
204266

@@ -207,8 +269,8 @@ def Registry.query_value(hkey, name)
207269

208270
response = client.send_request(request)
209271

210-
data = response.get_tlv(TLV_TYPE_VALUE_DATA).value;
211-
type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value;
272+
data = response.get_tlv(TLV_TYPE_VALUE_DATA).value
273+
type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value
212274

213275
if (type == REG_SZ)
214276
data = data[0..-2]
@@ -272,6 +334,24 @@ def Registry.enum_value(hkey)
272334
return values
273335
end
274336

337+
def Registry.enum_value_direct(root_key, base_key, perm = KEY_READ)
338+
request = Packet.create_request('stdapi_registry_enum_value_direct')
339+
values = []
340+
341+
request.add_tlv(TLV_TYPE_ROOT_KEY, root_key)
342+
request.add_tlv(TLV_TYPE_BASE_KEY, base_key)
343+
request.add_tlv(TLV_TYPE_PERMISSION, perm)
344+
345+
response = client.send_request(request)
346+
347+
response.each(TLV_TYPE_VALUE_NAME) do |value_name|
348+
values << Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new(
349+
client, 0, value_name.value)
350+
end
351+
352+
values
353+
end
354+
275355
#
276356
# Return the key value associated with the supplied string. This is useful
277357
# for converting HKLM as a string into its actual integer representation.

0 commit comments

Comments
 (0)