From 0af8915fd530376c30ceaf5761370d2adba021b9 Mon Sep 17 00:00:00 2001 From: Joshua Drake Date: Mon, 24 Jan 2011 17:23:48 +0000 Subject: [PATCH] Fixes #1936, Add accounts mixin and accompanying post module git-svn-id: file:///home/svn/framework3/trunk@11629 4d416f70-5f16-0410-b530-b9f4589650da --- lib/msf/core/post/windows/accounts.rb | 126 +++++++++++++++++++++ modules/post/windows/gather/resolve_sid.rb | 52 +++++++++ 2 files changed, 178 insertions(+) create mode 100644 lib/msf/core/post/windows/accounts.rb create mode 100644 modules/post/windows/gather/resolve_sid.rb diff --git a/lib/msf/core/post/windows/accounts.rb b/lib/msf/core/post/windows/accounts.rb new file mode 100644 index 0000000000..fdc4e1f8e8 --- /dev/null +++ b/lib/msf/core/post/windows/accounts.rb @@ -0,0 +1,126 @@ +module Msf +class Post + +module Accounts + + ## + # resolve_sid(sid, system_name = nil) + # + # Summary: + # Retrieves the name, domain, and type of account for the given sid + # + # Parameters: + # sid - A SID string (e.g. S-1-5-32-544) + # system_name - Where to search. If nil, first local system then trusted DCs + # + # Returns: + # { + # :name => account name (e.g. "SYSTEM") + # :domain => domain where the account name was found. May have values such as + # the work station's name, BUILTIN, NT AUTHORITY, or an empty string + # :type => one of :user, :group, :domain, :alias, :well_known_group, + # :deleted_account, :invalid, :unknown, :computer + # :mapped => There was a mapping found for the SID + # } + # + # OR nil if there was an exceptional windows error (example: ran out of memory) + # + # Caveats: + # If a valid mapping is not found, only { :mapped => false } will be returned + # nil is returned if there is an *exceptional* windows error. That error is printed. + # If an invalid system_name is provided, there will be a windows error and nil returned + ## + def resolve_sid(sid, system_name = nil) + adv = client.railgun.advapi32; + + # Second param is the size of the buffer where the pointer will be written + # In railgun, if you specify 4 bytes for a PDWORD it will grow to 8, as needed. + conversion = adv.ConvertStringSidToSidA(sid, 4) + + # If the call failed, handle errors accordingly. + unless conversion['return'] + error = conversion['GetLastError'] + + case error + when client.railgun.const('ERROR_INVALID_SID') + # An invalid SID was supplied + return { :type => :invalid, :mapped => false } + else + print_error "Unexpected windows error #{error}" + return nil + end + end + + # A reference to the SID data structure. Generally needed when working with sids + psid = conversion['pSid'] + + # http://msdn.microsoft.com/en-us/library/aa379166(v=vs.85).aspx + # TODO: The buffer sizes here need to be reviewed/adjusted/optimized + lookup = adv.LookupAccountSidA(system_name, psid, 100, 100, 100, 100, 1) + + # We no longer need the sid so free it. + # NOTE: We do not check to see if this call succeeded. Do we care? + adv.FreeSid(psid) + + # If the call failed, handle errors accordingly. + unless lookup['return'] + error = lookup['GetLastError'] + + case error + when client.railgun.const('ERROR_INVALID_PARAMETER') + # Unless the railgun call is broken, this means revesion is wrong + return { :type => :invalid } + when client.railgun.const('ERROR_NONE_MAPPED') + # There were no accounts associated with this SID + return { :mapped => false } + else + print_error "Unexpected windows error #{error}" + return nil + end + end + + # peUse is the enum "SID_NAME_USE" + sid_type = lookup_SID_NAME_USE(lookup['peUse'].unpack('C')[0]) + + return { + :name => lookup['Name'], + :domain => lookup['ReferencedDomainName'], + :type => sid_type, + :mapped => true + } + end + + private + + ## + # Converts a WinAPI's SID_NAME_USE enum to a symbol + # Symbols are (in order) :user, :group, :domain, :alias, :well_known_group, + # :deleted_account, :invalid, :unknown, :computer + ## + def lookup_SID_NAME_USE(enum_value) + [ + # SidTypeUser = 1 + :user, + # SidTypeGroup, + :group, + #SidTypeDomain, + :domain, + #SidTypeAlias, + :alias, + #SidTypeWellKnownGroup, + :well_known_group, + #SidTypeDeletedAccount, + :deleted_account, + #SidTypeInvalid, + :invalid, + #SidTypeUnknown, + :unknown, + #SidTypeComputer, + :computer, + #SidTypeLabel + :integrity_label + ][enum_value - 1] + end +end # Accounts +end # Post +end # Msf diff --git a/modules/post/windows/gather/resolve_sid.rb b/modules/post/windows/gather/resolve_sid.rb new file mode 100644 index 0000000000..25af17e4a2 --- /dev/null +++ b/modules/post/windows/gather/resolve_sid.rb @@ -0,0 +1,52 @@ +require 'msf/core' +require 'msf/core/post/windows/accounts' + +class Metasploit3 < Msf::Post + + include Msf::Post::Accounts + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Resolve SID', + 'Description' => %q{ This module prints information about a given SID from the perspective of this session }, + 'License' => BSD_LICENSE, + 'Author' => [ 'chao-mu'], + 'Platform' => [ 'windows' ], + 'SessionTypes' => [ 'meterpreter' ] + )) + register_options( + [ + OptString.new('SID', [ true, 'SID to lookup' ]), + OptString.new('SYSTEM_NAME', [ false, 'Where to search. If undefined, first local then trusted DCs' ]), + ], self.class) + + end + + def run + sid = datastore['SID'] + target_system = datastore['SYSTEM_NAME'] + + info = resolve_sid(sid, target_system ? target_system : nil) + + if info.nil? + print_error 'Unable to resolve SID. Giving up.' + return + end + + sid_type = info[:type] + + if sid_type.eql? :invalid + print_error 'Invalid SID provided' + return + end + + unless info[:mapped] + print_error 'No account found for the given SID' + return + end + + print_status "SID Type: #{sid_type.to_s}" + print_status "Name: #{info[:name]}" + print_status "Domain: #{info[:domain]}" + end +end