metasploit-framework/lib/rex/proto/dcerpc/svcctl/packet.rb

336 lines
10 KiB
Ruby

# -*- coding: binary -*-
module Rex
###
# This module implements MSRPC functions that control creating, deleting,
# starting, stopping, and querying system services.
###
module Proto::DCERPC::SVCCTL
require 'windows_error'
require 'windows_error/win32'
require 'msf/core/exploit/windows_constants'
NDR = Rex::Encoder::NDR
class Client
include WindowsError::Win32
include Msf::Exploit::Windows_Constants
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 [Integer] 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
elog("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 [Integer] :access (SERVICE_ALL_ACCESS) the access level.
# @option opts [Integer] :type (SERVICE_WIN32_OWN_PROCESS ||
# SERVICE_INTERACTIVE_PROCESS) the type of service.
# @option opts [Integer] :start (SERVICE_DEMAND_START) the start options.
# @option opts [Integer] :errors (SERVICE_ERROR_IGNORE) the error options.
# @option opts [Integer] :load_order_group (0) the load order group.
# @option opts [Integer] :dependencies (0) the dependencies of the service.
# @option opts [Integer] :service_start (0)
# @option opts [Integer] :password1 (0)
# @option opts [Integer] :password2 (0)
# @option opts [Integer] :password3 (0)
# @option opts [Integer] :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)
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
elog("Error creating service: #{e}")
end
if response
svc_status = error_code(response[24,4])
if svc_status == ERROR_SUCCESS
svc_handle = response[4,20]
end
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
elog("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)
end
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
elog("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 [Integer] 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
elog("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 args [Array] an array of arguments to pass to the service (or nil)
#
# @return [Integer] Windows error code
def startservice(svc_handle, args=[])
svc_status = nil
if args.empty?
stubdata = svc_handle + NDR.long(0) + NDR.long(0)
else
# This is just an arbitrary "pointer" value, gonna match it to what the real version uses
id_value = 0x00000200
stubdata = svc_handle
stubdata += NDR.long(args.length) + NDR.long(id_value) + NDR.long(args.length)
# Encode an id value for each parameter
args.each do
id_value += 0x04000000
stubdata += NDR.long(id_value)
end
# Encode the values now
args.each do |arg|
# We can't use NDR.uwstring here, because we need the "id" values to come first
stubdata += NDR.long(arg.length + 1) + NDR.long(0) + NDR.long(arg.length + 1)
# Unicode string
stubdata += Rex::Text.to_unicode(arg + "\0")
# Padding
if((arg.length % 2) == 0)
stubdata += Rex::Text.to_unicode("\0")
end
end
end
begin
response = dcerpc_client.call(0x13, stubdata)
if response
svc_status = error_code(response)
end
rescue Rex::Proto::DCERPC::Exceptions::Fault => e
elog("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 [Integer] 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
elog("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
elog("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 [Integer] 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
elog("Error deleting service: #{e}")
end
ret
end
end
end
end