Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/dcerpc/svcctl/client.rb
Views: 11766
# -*- coding: binary -*-1module Rex23###4# This module implements MSRPC functions that control creating, deleting,5# starting, stopping, and querying system services.6###7module Proto::DCERPC::SVCCTL89require 'windows_error'10require 'windows_error/win32'11NDR = Rex::Encoder::NDR121314class Client1516include WindowsError::Win3217include Msf::Exploit::Windows_Constants1819attr_accessor :dcerpc_client2021def initialize(dcerpc_client)22self.dcerpc_client = dcerpc_client23end2425# Returns the Windows Error Code in numeric format26#27# @param raw_error [String] the raw error code in binary format.28#29# @return [Integer] the Windows Error Code integer.30def error_code(raw_error)31raw_error.unpack('V').first32end3334# Calls OpenSCManagerW() to obtain a handle to the service control manager.35#36# @param rhost [String] the target host.37# @param access [Integer] the access flags requested.38#39# @return [Array<String,Integer>] the handle to the service control manager or nil if40# the call is not successful and the Windows error code41def openscmanagerw(rhost, access = SC_MANAGER_ALL_ACCESS)42scm_handle = nil43scm_status = nil44stubdata =45NDR.uwstring("\\\\#{rhost}") +46NDR.long(0) +47NDR.long(access)48begin49response = dcerpc_client.call(OPEN_SC_MANAGER_W, stubdata)50if response51scm_status = error_code(response[20,4])52if scm_status == ERROR_SUCCESS53scm_handle = response[0,20]54end55end56rescue Rex::Proto::DCERPC::Exceptions::Fault => e57elog('Error getting scm handle', error: e)58end5960[scm_handle, scm_status]61end6263# Calls CreateServiceW() to create a system service. Returns a handle to64# the service on success, or nil.65#66# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).67# @param service_name [String] the service name.68# @param display_name [String] the display name.69# @param binary_path [String] the path of the binary to run.70# @param opts [Hash] arguments for CreateServiceW()71# @option opts [Integer] :access (SERVICE_ALL_ACCESS) the access level.72# @option opts [Integer] :type (SERVICE_WIN32_OWN_PROCESS ||73# SERVICE_INTERACTIVE_PROCESS) the type of service.74# @option opts [Integer] :start (SERVICE_DEMAND_START) the start options.75# @option opts [Integer] :errors (SERVICE_ERROR_IGNORE) the error options.76# @option opts [Integer] :load_order_group (0) the load order group.77# @option opts [Integer] :dependencies (0) the dependencies of the service.78# @option opts [Integer] :service_start (0)79# @option opts [Integer] :password1 (0)80# @option opts [Integer] :password2 (0)81# @option opts [Integer] :password3 (0)82# @option opts [Integer] :password4 (0)83#84# @return [String, Integer] a handle to the created service, windows85# error code.86def createservicew(scm_handle, service_name, display_name, binary_path, opts)87default_opts = {88:access => SERVICE_ALL_ACCESS,89:type => SERVICE_WIN32_OWN_PROCESS || SERVICE_INTERACTIVE_PROCESS,90:start => SERVICE_DEMAND_START,91:errors => SERVICE_ERROR_IGNORE,92:load_order_group => 0,93:dependencies => 0,94:service_start => 0,95:password1 => 0,96:password2 => 0,97:password3 => 0,98:password4 => 099}.merge(opts)100101svc_handle = nil102svc_status = nil103stubdata = scm_handle +104NDR.wstring(service_name) +105NDR.uwstring(display_name) +106NDR.long(default_opts[:access]) +107NDR.long(default_opts[:type]) +108NDR.long(default_opts[:start]) +109NDR.long(default_opts[:errors]) +110NDR.wstring(binary_path) +111NDR.long(default_opts[:load_order_group]) +112NDR.long(default_opts[:dependencies]) +113NDR.long(default_opts[:service_start]) +114NDR.long(default_opts[:password1]) +115NDR.long(default_opts[:password2]) +116NDR.long(default_opts[:password3]) +117NDR.long(default_opts[:password4])118begin119response = dcerpc_client.call(CREATE_SERVICE_W, stubdata)120rescue Rex::Proto::DCERPC::Exceptions::Fault => e121elog('Error creating service', error: e)122end123124if response125svc_status = error_code(response[24,4])126if svc_status == ERROR_SUCCESS127svc_handle = response[4,20]128end129end130131return svc_handle, svc_status132end133134# Calls ChangeServiceConfig2() to change the service description.135#136# @param svc_handle [String] the service handle to change.137# @param service_description [String] the service description.138#139# @return [Integer] Windows error code140def changeservicedescription(svc_handle, service_description)141svc_status = nil142stubdata =143svc_handle +144NDR.long(SERVICE_CONFIG_DESCRIPTION) +145NDR.long(1) + # lpInfo -> *SERVICE_DESCRIPTION146NDR.long(0x0200) + # SERVICE_DESCRIPTION struct147NDR.long(0x04000200) +148NDR.wstring(service_description)149begin150response = dcerpc_client.call(CHANGE_SERVICE_CONFIG2_W, stubdata) # ChangeServiceConfig2151svc_status = error_code(response)152rescue Rex::Proto::DCERPC::Exceptions::Fault => e153elog('Error changing service description', error: e)154end155156svc_status157end158159160# Calls CloseHandle() to close a handle.161#162# @param handle [String] the handle to close.163#164# @return [Integer] Windows error code165def closehandle(handle)166svc_status = nil167begin168response = dcerpc_client.call(CLOSE_SERVICE_HANDLE, handle)169if response170svc_status = error_code(response)171end172rescue Rex::Proto::DCERPC::Exceptions::Fault => e173elog('Error closing service handle', error: e)174end175176svc_status177end178179# Calls OpenServiceW to obtain a handle to an existing service.180#181# @param scm_handle [String] the SCM handle (from {#openscmanagerw}).182# @param service_name [String] the name of the service to open.183# @param access [Integer] the level of access requested (default is maximum).184#185# @return [String, nil] the handle of the service opened, or nil on failure.186def openservicew(scm_handle, service_name, access = SERVICE_ALL_ACCESS)187svc_handle = nil188svc_status = nil189stubdata = scm_handle + NDR.wstring(service_name) + NDR.long(access)190begin191response = dcerpc_client.call(OPEN_SERVICE_W, stubdata)192if response193svc_status = error_code(response[20,4])194if svc_status == ERROR_SUCCESS195svc_handle = response[0,20]196end197end198rescue Rex::Proto::DCERPC::Exceptions::Fault => e199elog('Error opening service handle', error: e)200end201202svc_handle203end204205# Calls StartService() on a handle to an existing service in order to start206# it. Returns true on success, or false.207#208# @param svc_handle [String] the handle of the service (from {#openservicew}).209# @param args [Array] an array of arguments to pass to the service (or nil)210#211# @return [Integer] Windows error code212def startservice(svc_handle, args=[])213svc_status = nil214215if args.empty?216stubdata = svc_handle + NDR.long(0) + NDR.long(0)217else218# This is just an arbitrary "pointer" value, gonna match it to what the real version uses219id_value = 0x00000200220221stubdata = svc_handle222stubdata += NDR.long(args.length) + NDR.long(id_value) + NDR.long(args.length)223224# Encode an id value for each parameter225args.each do226id_value += 0x04000000227stubdata += NDR.long(id_value)228end229230# Encode the values now231args.each do |arg|232# We can't use NDR.uwstring here, because we need the "id" values to come first233stubdata += NDR.long(arg.length + 1) + NDR.long(0) + NDR.long(arg.length + 1)234235# Unicode string236stubdata += Rex::Text.to_unicode(arg + "\0")237238# Padding239if((arg.length % 2) == 0)240stubdata += Rex::Text.to_unicode("\0")241end242end243end244245begin246response = dcerpc_client.call(0x13, stubdata)247if response248svc_status = error_code(response)249end250rescue Rex::Proto::DCERPC::Exceptions::Fault => e251elog('Error starting service', error: e)252end253254svc_status255end256257# Stops a running service.258#259# @param svc_handle [String] the handle of the service (from {#openservicew}).260#261# @return [Integer] Windows error code262def stopservice(svc_handle)263dce_controlservice(svc_handle, SERVICE_CONTROL_STOP)264end265266# Controls an existing service.267#268# @param svc_handle [String] the handle of the service (from {#openservicew}).269# @param operation [Integer] the operation number to perform (1 = stop270# service; others are unknown).271#272# @return [Integer] Windows error code273def controlservice(svc_handle, operation)274svc_status = nil275begin276response = dcerpc_client.call(CONTROL_SERVICE, svc_handle + NDR.long(operation))277if response278svc_status = error_code(response[28,4])279end280rescue Rex::Proto::DCERPC::Exceptions::Fault => e281elog('Error controlling service', error: e)282end283284svc_status285end286287# Calls DeleteService() to delete a service.288#289# @param svc_handle [String] the handle of the service (from {#openservicew}).290#291# @return [Integer] Windows error code292def deleteservice(svc_handle)293svc_status = nil294begin295response = dcerpc_client.call(DELETE_SERVICE, svc_handle)296if response297svc_status = error_code(response)298end299rescue Rex::Proto::DCERPC::Exceptions::Fault => e300elog('Error deleting service', error: e)301end302303svc_status304end305306# Calls QueryServiceStatus() to query the status of a service.307#308# @param svc_handle [String] the handle of the service (from {#openservicew}).309#310# @return [Integer] Returns 0 if the query failed (i.e.: a state was returned311# that isn't implemented), 1 if the service is running, and312# 2 if the service is stopped.313def queryservice(svc_handle)314ret = 0315316begin317response = dcerpc_client.call(QUERY_SERVICE_STATUS, svc_handle)318if response[0,9] == "\x10\x00\x00\x00\x04\x00\x00\x00\x01"319ret = 1320elsif response[0,9] == "\x10\x00\x00\x00\x01\x00\x00\x00\x00"321ret = 2322end323rescue Rex::Proto::DCERPC::Exceptions::Fault => e324elog('Error deleting service', error: e)325end326327ret328end329330end331end332end333334335336