add shell support for registry methdos, thanks kernelsmith!

git-svn-id: file:///home/svn/framework3/trunk@11784 4d416f70-5f16-0410-b530-b9f4589650da
unstable
James Lee 2011-02-21 19:13:11 +00:00
parent 72ffa260c2
commit e71cd9b524
2 changed files with 477 additions and 72 deletions

View File

@ -0,0 +1,139 @@
module Msf
class Post
module Windows
module CliParse
#
# Parses output of some windows CLI commands and returns hash with the keys/vals detected
# if the item has multiple values, they will all be returned in the val separated by commas
#
#--- sc.exe example (somewhat contrived)
# SERVICE_NAME: dumbservice
# DISPLAY_NAME: KernelSmith Dumb Service - User-mode
# TYPE : 20 WIN32_SHARE_PROCESS
# STATE : 4 RUNNING
# (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
# START_TYPE : 2 AUTO_START
# BINARY_PATH_NAME : C:\Windows\system32\svchost.exe -k LocalSystemNetworkRestricted
# DEPENDENCIES : PlugPlay
# : DumberService
# SERVICE_START_NAME : LocalSystem
# PID : 368
# FLAGS :
#--- END sc.exe example
#
# Example would return:
# {
# 'SERVICE_NAME' => "dumbservice",
# 'DISPLAY_NAME' => "KernelSmith Dumb Service - User-mod",
# 'STATE' => "4 RUNNING",
# 'START_TYPE' => "2 AUTO_START",
# 'BINARY_PATH_NAME' => "C:\Windows\system32\svchost.exe -k LocalSystemNetworkRestricted",
# 'DEPENDENCIES' => "PlugPlay,DumberService"
# <...etc...>
# }
#
def win_parse_results(str)
#print_status "Parsing results string: #{str}" if $blab
tip = false
hashish = Hash.new(nil)
lastkey = nil
str.each_line do |line|
line.chomp!
line.gsub!("\t",' ') # lose any tabs
if (tip == true && line =~ /^ + :/)
# then this is probably a continuation of the previous, let's append to previous
# NOTE: this will NOT pickup the (NOT_STOPPABLE, NOT_PAUSABLE), see next, but it
# will pickup when there's multiple dependencies
#print_status "Caught line continuation with :" if $blab
arr = line.scan(/\w+/)
val = arr.join(',') # join with commas, tho there is probably only one item in arr
hashish[lastkey] << ",#{val}" # append to old val with preceding ','
# if that's confusing, maybe: hashish[lastkey] = "#{hashish[lastkey]},#{val}"
tip = false
elsif (tip == true && line =~ /^ + \(/)
# then this is probably a continuation of the previous, let's append to previous
# NOTE: this WILL pickup (NOT_STOPPABLE, NOT_PAUSABLE) etc
#print_status "Caught line continuation with (" if $blab
arr = line.scan(/\w+/) # put each "word" into an array
val = arr.join(',') # join back together with commas in case comma wasn't the sep
hashish[lastkey] << ",#{val}" # append to old val with preceding ','
# if that's confusing, maybe: hashish[lastkey] = "#{hashish[lastkey]},#{val}"
tip = false
elsif line =~ /^ *[A-Z]+[_]*[A-Z]+.*:/
tip = true
arr = line.split(':')
#print_status "Array split is #{arr.inspect}" if $blab
k = arr[0].strip
# grab all remaining fields for hash val in case ':' present in val
v = arr[1..-1].join(':').strip
# now add this entry to the hash
#print_status "Adding the following hash entry: #{k} => #{v}" if $blab
hashish[k] = v
lastkey = k
end
end
return hashish
end
#
# Parses error output of some windows CLI commands and returns hash with the keys/vals detected
# always returns hash as follows but :errval only comes back from sc.exe using 'FAILED' keyword
#
# Note, most of the time the :errval will be nil, it's not usually provided
#
#
#--- sc.exe error example
# [SC] EnumQueryServicesStatus:OpenService FAILED 1060:
#
# The specified service does not exist as an installed service.
#--- END sc.exe error example
# returns:
# {
# :error => "The specified service does not exist as an installed service",
# :errval => 1060
# }
#
#
#--- reg.exe error example
# ERROR: Invalid key name.
# Type "REG QUERY /?" for usage.
#--- END reg.exe error example
# returns:
# {
# :error => "INVALID KEY NAME."
# :errval => nil
# }
#
def win_parse_error(str)
hashish = {
:error => "Unknown Error",
:errval => nil
}
if ma = /^error:.*/i.match(str) # if line starts with Error: just pass to regular parser
hashish.merge!(win_parse_results(ma[0].upcase)) #upcase required to satisfy regular parser
# merge results. Results from win_parse_results will override any duplicates in hashish
elsif ma = /FAILED +[0-9]+/.match(str) # look for 'FAILED ' followed by some numbers
#print_status "Found FAILED, ma is #{ma.inspect}" if $blab
sa = ma[0].split(' ')
#print_status "sa is #{sa.inspect}" if $blab
hashish[:errval] = sa[1].chomp.to_i
# above intended to capture the numbers after the word 'FAILED' as [:errval]
ma = /^[^\[\n].+/.match(str)
#print_status "ma is #{ma.inspect}" if $blab
hashish[:error] = ma[0].chomp.strip
# above intended to capture first non-empty line not starting with '[' or \n into [:error]
else
# do nothing, defaults are good
end
#print_error "Error hash: #{hashish.inspect}" if $blab
print_error "This error hash is optionally available: #{hashish.pretty_inspect}"
return hashish
end
end
end
end
end

View File

@ -1,52 +1,21 @@
require 'msf/core/post/windows/cli_parse'
module Msf
class Post
module Registry
#
# Return the data and type of a given registry key and value
#
def registry_getvalinfo(key,valname)
value = {}
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
v = open_key.query_value(valname)
value["Data"] = v.data
value["Type"] = v.type
open_key.close
end
return value
end
include Msf::Post::Windows::CliParse
#
# Return the data of a given registry key and value
# Create the given registry key
#
def registry_getvaldata(key,valname)
value = nil
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
v = open_key.query_value(valname)
value = v.data
open_key.close
end
return value
end
#
# Sets the data for a given value and type of data on the target registry
#
# returns true if succesful
#
def registry_setvaldata(key,valname,data,type)
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE)
open_key.set_value(valname, session.sys.registry.type2str(type), data)
open_key.close
return true
def registry_createkey(key)
if session_has_registry_ext?
meterpreter_registry_createkey(key)
else
shell_registry_createkey(key)
end
end
@ -55,13 +24,35 @@ module Registry
#
# returns true if succesful
#
def registry_deleteval(key,valname)
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE)
open_key.delete_value(valname)
open_key.close
return true
def registry_deleteval(key, valname)
if session_has_registry_ext?
meterpreter_registry_deleteval(key, valname)
else
shell_registry_deleteval(key, valname)
end
end
#
# Delete a given registry key
#
# returns true if succesful
#
def registry_deletekey(key)
if session_has_registry_ext?
meterpreter_registry_deletekey(key)
else
shell_registry_deletekey(key)
end
end
#
# Return an array of subkeys for the given registry key
#
def registry_enumkeys(key)
if session_has_registry_ext?
meterpreter_registry_enumkeys(key)
else
shell_registry_enumkeys(key)
end
end
@ -69,6 +60,283 @@ module Registry
# Return an array of value names for the given registry key
#
def registry_enumvals(key)
if session_has_registry_ext?
meterpreter_registry_enumvals(key)
else
shell_registry_enumvals(key)
end
end
#
# Return the data of a given registry key and value
#
def registry_getvaldata(key, valname)
if session_has_registry_ext?
meterpreter_registry_getvaldata(key, valname)
else
shell_registry_getvaldata(key, valname)
end
end
#
# Return the data and type of a given registry key and value
#
def registry_getvalinfo(key,valname)
if session_has_registry_ext?
meterpreter_registry_getvalinfo(key, valname)
else
shell_registry_getvalinfo(key, valname)
end
end
#
# Sets the data for a given value and type of data on the target registry
#
# returns true if succesful
#
def registry_setvaldata(key, valname, data, type)
if session_has_registry_ext?
meterpreter_registry_setvaldata(key, valname, data, type)
else
shell_registry_setvaldata(key, valname, data, type)
end
end
protected
#
# Determines whether the session can use meterpreter registry methods
#
def session_has_registry_ext?
(session.sys and session.sys.respond_to?(:registry))
end
##
# Generic registry manipulation methods based on reg.exe
##
def shell_registry_createkey(key)
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
end
def shell_registry_deleteval(key, valname)
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
end
def shell_registry_deletekey(key)
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
end
def shell_registry_enumkeys(key)
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}")
end
else
error_hash = win_parse_error("ERROR:Unknown error running #{cmd}")
end
end
return subkeys
end
def shell_registry_enumvals(key)
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}].*/)
# 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|
t = line.split(' ')[0].chomp #chomp for good measure
# check if reg returned "<NO NAME>", which splits to "<NO", if so nil instead
t = nil if t == "<NO"
t
end
else
error_hash = win_parse_error("ERROR:Unknown error running #{cmd}")
end
end
return values
end
def shell_registry_getvaldata(key, valname)
value = nil
begin
a = shell_registry_getvalinfo(key, valname)
value = a["Data"] || nil
end
return value
end
def shell_registry_getvalinfo(key, valname)
key = normalize_key(key)
value = {}
value["Data"] = nil # defaults
value["Type"] = nil
begin
# REG QUERY KeyName [/v ValueName | /ve] [/s]
cmd = "cmd.exe /c reg query \"#{key}\" /v \"#{valname}\""
results = session.shell_command_token_win32(cmd)
if match_arr = /^ +#{valname}.*/i.match(results)
# pull out the interesting line (the one with the value name in it)
# and split it with ' ' yielding [valname,REGvaltype,REGdata]
split_arr = match_arr[0].split(' ')
value["Type"] = split_arr[1]
value["Data"] = split_arr[2]
# need to test to ensure all results can be parsed this way
elsif results =~ /^Error:/
error_hash = win_parse_error(results)
else
error_hash = win_parse_error("ERROR:Unknown error running #{cmd}")
end
end
return value
end
def shell_registry_setvaldata(key, valname)
key = normalize_key(key)
boo = false
begin
# REG ADD KeyName [/v ValueName | /ve] [/t Type] [/s Separator] [/d Data] [/f]
# /f to overwrite w/o prompt
cmd = "cmd.exe /c reg add \"#{key}\" /v \"#{valname}\" /t \"#{type}\" /d \"#{data}\" /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
end
##
# Meterpreter-specific registry manipulation methods
##
def meterpreter_registry_createkey(key)
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.create_key(root_key, base_key)
open_key.close
return true
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
end
def meterpreter_registry_deleteval(key, valname)
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE)
open_key.delete_value(valname)
open_key.close
return true
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
end
def meterpreter_registry_deletekey(key)
begin
root_key, base_key = session.sys.registry.splitkey(key)
deleted = session.sys.registry.delete_key(root_key, base_key)
return deleted
rescue Rex::Post::Meterpreter::RequestError => e
print_status "curses, foiled again"
end
end
def meterpreter_registry_enumkeys(key)
subkeys = []
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
keys = open_key.enum_key
keys.each { |subkey|
subkeys << subkey
}
open_key.close
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
return subkeys
end
def meterpreter_registry_enumvals(key)
values = []
begin
vals = {}
@ -79,53 +347,51 @@ module Registry
values << val.name
}
open_key.close
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
return values
end
#
# Return an array of subkeys for the given registry key
#
def registry_enumkeys(key)
subkeys = []
def meterpreter_registry_getvaldata(key, valname)
value = nil
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
keys = open_key.enum_key
keys.each { |subkey|
subkeys << subkey
}
v = open_key.query_value(valname)
value = v.data
open_key.close
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
return subkeys
return value
end
#
# Create the given registry key
#
def registry_createkey(key)
def meterpreter_registry_getvalinfo(key, valname)
value = {}
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.create_key(root_key, base_key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_READ)
v = open_key.query_value(valname)
value["Data"] = v.data
value["Type"] = v.type
open_key.close
return true
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
return value
end
#
# Delete a given registry key
#
# returns true if succesful
#
def registry_delkey(key)
def meterpreter_registry_setvaldata(key, valname, data, type)
begin
root_key, base_key = session.sys.registry.splitkey(key)
open_key = session.sys.registry.delete_key(root_key, base_key)
open_key = session.sys.registry.open_key(root_key, base_key, KEY_WRITE)
open_key.set_value(valname, session.sys.registry.type2str(type), data)
open_key.close
return true
rescue Rex::Post::Meterpreter::RequestError => e
return nil
end
end
end