diff --git a/lib/msf/core/post/windows/registry.rb b/lib/msf/core/post/windows/registry.rb index 764c273f7e..78f28aed55 100644 --- a/lib/msf/core/post/windows/registry.rb +++ b/lib/msf/core/post/windows/registry.rb @@ -10,16 +10,34 @@ module Registry include Msf::Post::Windows::CliParse + # + # This is the default view. It reflects what the remote process would see + # natively. So, if you are using a remote 32-bit meterpreter session, you + # will see 32-bit registry keys and values. + # + REGISTRY_VIEW_NATIVE = 0 + + # + # Access 32-bit registry keys and values regardless of whether the session is + # 32 or 64-bit. + # + REGISTRY_VIEW_32_BIT = 1 + + # + # Access 64-bit registry keys and values regardless of whether the session is + # 32 or 64-bit. + # + REGISTRY_VIEW_64_BIT = 2 + # # Load a hive file # - def registry_loadkey(key,file) + def registry_loadkey(key, file) if session_has_registry_ext - retval=meterpreter_registry_loadkey(key,file) + meterpreter_registry_loadkey(key, file) else - retval=shell_registry_loadkey(key,file) + shell_registry_loadkey(key, file) end - return retval end # @@ -27,21 +45,20 @@ module Registry # def registry_unloadkey(key) if session_has_registry_ext - retval=meterpreter_registry_unloadkey(key) + meterpreter_registry_unloadkey(key) else - retval=shell_registry_unloadkey(key) + shell_registry_unloadkey(key) end - return retval end # # Create the given registry key # - def registry_createkey(key) + def registry_createkey(key, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_createkey(key) + meterpreter_registry_createkey(key, view) else - shell_registry_createkey(key) + shell_registry_createkey(key, view) end end @@ -50,11 +67,11 @@ module Registry # # returns true if succesful # - def registry_deleteval(key, valname) + def registry_deleteval(key, valname, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_deleteval(key, valname) + meterpreter_registry_deleteval(key, valname, view) else - shell_registry_deleteval(key, valname) + shell_registry_deleteval(key, valname, view) end end @@ -63,55 +80,55 @@ module Registry # # returns true if succesful # - def registry_deletekey(key) + def registry_deletekey(key, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_deletekey(key) + meterpreter_registry_deletekey(key, view) else - shell_registry_deletekey(key) + shell_registry_deletekey(key, view) end end # # Return an array of subkeys for the given registry key # - def registry_enumkeys(key) + def registry_enumkeys(key, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_enumkeys(key) + meterpreter_registry_enumkeys(key, view) else - shell_registry_enumkeys(key) + shell_registry_enumkeys(key, view) end end # # Return an array of value names for the given registry key # - def registry_enumvals(key) + def registry_enumvals(key, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_enumvals(key) + meterpreter_registry_enumvals(key, view) else - shell_registry_enumvals(key) + shell_registry_enumvals(key, view) end end # # Return the data of a given registry key and value # - def registry_getvaldata(key, valname) + def registry_getvaldata(key, valname, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_getvaldata(key, valname) + meterpreter_registry_getvaldata(key, valname, view) else - shell_registry_getvaldata(key, valname) + shell_registry_getvaldata(key, valname, view) end end # # Return the data and type of a given registry key and value # - def registry_getvalinfo(key,valname) + def registry_getvalinfo(key, valname, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_getvalinfo(key, valname) + meterpreter_registry_getvalinfo(key, valname, view) else - shell_registry_getvalinfo(key, valname) + shell_registry_getvalinfo(key, valname, view) end end @@ -120,11 +137,11 @@ module Registry # # returns true if succesful # - def registry_setvaldata(key, valname, data, type) + def registry_setvaldata(key, valname, data, type, view = REGISTRY_VIEW_NATIVE) if session_has_registry_ext - meterpreter_registry_setvaldata(key, valname, data, type) + meterpreter_registry_setvaldata(key, valname, data, type, view) else - shell_registry_setvaldata(key, valname, data, type) + shell_registry_setvaldata(key, valname, data, type, view) end end @@ -146,23 +163,27 @@ protected # Generic registry manipulation methods based on reg.exe ## + def shell_registry_cmd(suffix, view = REGISTRY_VIEW_NATIVE) + cmd = "cmd.exe /c reg" + if view == REGISTRY_VIEW_32_BIT + cmd += " /reg:32" + elsif view == REGISTRY_VIEW_64_BIT + cmd += " /reg:64" + end + session.shell_command_token_win32("#{cmd} #{suffix}") + end + + def shell_registry_cmd_result(suffix, view = REGISTRY_VIEW_NATIVE) + results = shell_registry_cmd(suffix, view); + results.include?('The operation completed successfully') + end + # # Use reg.exe to load the hive file +file+ into +key+ # - def shell_registry_loadkey(key,file) + def shell_registry_loadkey(key, file) key = normalize_key(key) - boo = false - file = "\"#{file}\"" - cmd = "cmd.exe /c reg load #{key} #{file}" - results = session.shell_command_token_win32(cmd) - if results =~ /The operation completed successfully/ - boo = true - elsif results =~ /^Error:/ - error_hash = win_parse_error(results) - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd}") - end - return boo + shell_registry_cmd_result("load \"#{key}\" \"#{file}\"") end # @@ -170,131 +191,71 @@ protected # def shell_registry_unloadkey(key) key = normalize_key(key) - boo = false - cmd = "cmd.exe /c reg unload #{key}" - results = session.shell_command_token_win32(cmd) - if results =~ /The operation completed successfully/ - boo = true - elsif results =~ /^Error:/ - error_hash = win_parse_error(results) - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd} INSPECT: #{error_hash.inspect}") - end - return boo + shell_registry_cmd_result("unload \"#{key}\"") end - # # Use reg.exe to create a new registry key # - def shell_registry_createkey(key) + def shell_registry_createkey(key, view) key = normalize_key(key) - boo = false - begin - # REG ADD KeyName [/v ValueName | /ve] [/t Type] [/s Separator] [/d Data] [/f] - cmd = "cmd.exe /c reg add \"#{key}\"" - results = session.shell_command_token_win32(cmd) - if results =~ /The operation completed successfully/ - boo = true - elsif results =~ /^Error:/ - error_hash = win_parse_error(results) - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd}") - end - end + # REG ADD KeyName [/v ValueName | /ve] [/t Type] [/s Separator] [/d Data] [/f] + shell_registry_cmd_result("add /f \"#{key}\"", view) end # # Use reg.exe to delete +valname+ in +key+ # - def shell_registry_deleteval(key, valname) + def shell_registry_deleteval(key, valname, view) key = normalize_key(key) - boo = false - begin - # REG DELETE KeyName [/v ValueName | /ve | /va] [/f] - cmd = "cmd.exe /c reg delete \"#{key}\" /v \"#{valname}\" /f" - results = session.shell_command_token_win32(cmd) - if results =~ /The operation completed successfully/ - boo = true - elsif results =~ /^Error:/ - error_hash = win_parse_error(results) - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd}") - end - end - return boo + # REG DELETE KeyName [/v ValueName | /ve | /va] [/f] + shell_registry_cmd_result("delete \"#{key}\" /v \"#{valname}\" /f", view) end # # Use reg.exe to delete +key+ and all its subkeys and values # - def shell_registry_deletekey(key) + def shell_registry_deletekey(key, view) key = normalize_key(key) - boo = false - begin - # REG DELETE KeyName [/v ValueName | /ve | /va] [/f] - cmd = "cmd.exe /c reg delete \"#{key}\" /f" - results = session.shell_command_token_win32(cmd) - if results =~ /The operation completed successfully/ - boo = true - elsif results =~ /^Error:/ - error_hash = win_parse_error(results) - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd}") - end - end - return boo + # REG DELETE KeyName [/v ValueName | /ve | /va] [/f] + shell_registry_cmd_result("delete \"#{key}\" /f", view) end # # Use reg.exe to enumerate all the subkeys in +key+ # - def shell_registry_enumkeys(key) + def shell_registry_enumkeys(key, view) key = normalize_key(key) subkeys = [] reg_data_types = 'REG_SZ|REG_MULTI_SZ|REG_DWORD_BIG_ENDIAN|REG_DWORD|REG_BINARY|' reg_data_types << 'REG_DWORD_LITTLE_ENDIAN|REG_NONE|REG_EXPAND_SZ|REG_LINK|REG_FULL_RESOURCE_DESCRIPTOR' - begin - bslashes = key.count('\\') - cmd = "cmd.exe /c reg query \"#{key}\"" - results = session.shell_command_token_win32(cmd) - if results - if results =~ /^Error:/ - error_hash = win_parse_error(results) - else # would like to use elsif results =~ /#{key}/ but can't figure it out - results.each_line do |line| - # now let's keep the ones that have a count = bslashes+1 - # feels like there's a smarter way to do this but... - if (line.count('\\') == bslashes+1 && !line.ends_with?('\\')) - #then it's a first level subkey - subkeys << line.split('\\').last.chomp # take & chomp the last item only - end - end - #else - # error_hash = win_parse_error("ERROR:Unrecognizable results from #{cmd}") + bslashes = key.count('\\') + results = shell_registry_cmd("query \"#{key}\"", view) + unless results.include?('Error') + results.each_line do |line| + # now let's keep the ones that have a count = bslashes+1 + # feels like there's a smarter way to do this but... + if (line.count('\\') == bslashes+1 && !line.ends_with?('\\')) + #then it's a first level subkey + subkeys << line.split('\\').last.chomp # take & chomp the last item only end - else - error_hash = win_parse_error("ERROR:Unknown error running #{cmd}") end end - return subkeys + subkeys end # # Use reg.exe to enumerate all the values in +key+ # - def shell_registry_enumvals(key) + def shell_registry_enumvals(key, view) key = normalize_key(key) values = [] reg_data_types = 'REG_SZ|REG_MULTI_SZ|REG_DWORD_BIG_ENDIAN|REG_DWORD|REG_BINARY|' reg_data_types << 'REG_DWORD_LITTLE_ENDIAN|REG_NONE|REG_EXPAND_SZ|REG_LINK|REG_FULL_RESOURCE_DESCRIPTOR' - begin - # REG QUERY KeyName [/v ValueName | /ve] [/s] - cmd = "cmd.exe /c reg query \"#{key}\"" - results = session.shell_command_token_win32(cmd) - if results =~ /^Error:/ - error_hash = win_parse_error(results) - elsif values = results.scan(/^ +.*[#{reg_data_types}].*/) + # REG QUERY KeyName [/v ValueName | /ve] [/s] + results = shell_registry_cmd("query \"#{key}\"", view) + unless results.include?('Error') + if values = results.scan(/^ +.*[#{reg_data_types}].*/) # yanked the lines with legit REG value types like REG_SZ # now let's parse out the names (first field basically) values.collect! do |line| @@ -303,75 +264,50 @@ protected t = nil if t == " e case e.to_s when "stdapi_registry_load_key: Operation failed: 1314" @@ -412,7 +357,6 @@ protected rescue return false end - end # @@ -445,11 +389,11 @@ protected # # Create a new registry key # - def meterpreter_registry_createkey(key) + def meterpreter_registry_createkey(key, view) begin root_key, base_key = session.sys.registry.splitkey(key) - - open_key = session.sys.registry.create_key(root_key, base_key) + perms = meterpreter_registry_perms(KEY_WRITE, view) + open_key = session.sys.registry.create_key(root_key, base_key, perms) open_key.close return true rescue Rex::Post::Meterpreter::RequestError => e @@ -460,10 +404,11 @@ protected # # Delete the registry value +valname+ store in +key+ # - def meterpreter_registry_deleteval(key, valname) + def meterpreter_registry_deleteval(key, valname, view) begin root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE) + perms = meterpreter_registry_perms(KEY_WRITE, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) open_key.delete_value(valname) open_key.close return true @@ -475,10 +420,11 @@ protected # # Delete the registry key +key+ # - def meterpreter_registry_deletekey(key) + def meterpreter_registry_deletekey(key, view) begin root_key, base_key = session.sys.registry.splitkey(key) - deleted = session.sys.registry.delete_key(root_key, base_key) + perms = meterpreter_registry_perms(KEY_WRITE, view) + deleted = session.sys.registry.delete_key(root_key, base_key, perms) return deleted rescue Rex::Post::Meterpreter::RequestError => e return nil @@ -488,50 +434,53 @@ protected # # Enumerate the subkeys in +key+ # - def meterpreter_registry_enumkeys(key) - subkeys = [] + def meterpreter_registry_enumkeys(key, view) begin + subkeys = [] root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + perms = meterpreter_registry_perms(KEY_READ, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) keys = open_key.enum_key keys.each { |subkey| subkeys << subkey } open_key.close + return subkeys rescue Rex::Post::Meterpreter::RequestError => e return nil end - return subkeys end # # Enumerate the values in +key+ # - def meterpreter_registry_enumvals(key) - values = [] + def meterpreter_registry_enumvals(key, view) begin + values = [] vals = {} root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + perms = meterpreter_registry_perms(KEY_READ, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) vals = open_key.enum_value vals.each { |val| values << val.name } open_key.close + return values rescue Rex::Post::Meterpreter::RequestError => e return nil end - return values end # # Get the data stored in the value +valname+ # - def meterpreter_registry_getvaldata(key, valname) - value = nil + def meterpreter_registry_getvaldata(key, valname, view) begin + value = nil root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + perms = meterpreter_registry_perms(KEY_READ, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) v = open_key.query_value(valname) value = v.data open_key.close @@ -544,11 +493,12 @@ protected # # Enumerate the type and data of the value +valname+ # - def meterpreter_registry_getvalinfo(key, valname) + def meterpreter_registry_getvalinfo(key, valname, view) value = {} begin root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ) + perms = meterpreter_registry_perms(KEY_READ, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) v = open_key.query_value(valname) value["Data"] = v.data value["Type"] = v.type @@ -562,10 +512,11 @@ protected # # Add the value +valname+ to the key +key+ with the specified +type+ and +data+ # - def meterpreter_registry_setvaldata(key, valname, data, type) + def meterpreter_registry_setvaldata(key, valname, data, type, view) begin root_key, base_key = session.sys.registry.splitkey(key) - open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE) + perms = meterpreter_registry_perms(KEY_WRITE, view) + open_key = session.sys.registry.open_key(root_key, base_key, perms) open_key.set_value(valname, session.sys.registry.type2str(type), data) open_key.close return true diff --git a/test/modules/post/test/registry.rb b/test/modules/post/test/registry.rb index 3241f6d1b2..a95defaafa 100644 --- a/test/modules/post/test/registry.rb +++ b/test/modules/post/test/registry.rb @@ -56,6 +56,13 @@ class Metasploit3 < Msf::Post valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP") ret &&= !!(valinfo["Data"] == valdata) + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP", REGISTRY_VIEW_NATIVE) + ret &&= !!(valinfo["Data"] == valdata) + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP", REGISTRY_VIEW_32_BIT) + ret &&= !!(valinfo["Data"] == valdata) + valdata = registry_getvaldata(%q#HKCU\Environment#, "TEMP", REGISTRY_VIEW_64_BIT) + ret &&= !!(valinfo["Data"] == valdata) + ret end