# -*- 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] 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("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) 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("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("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("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 print_error("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 [Integer] an unknown value. # @param magic2 [Integer] 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("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 print_error("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("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 print_error("Error deleting service: #{e}") end ret end end end end