Module: Msf::Post::Windows::Services

Includes:
Registry
Included in:
ShadowCopy, Scripts::Meterpreter::Common
Defined in:
lib/msf/core/post/windows/services.rb

Overview

Post module mixin for dealing with Windows services

Instance Method Summary collapse

Methods included from Registry

#registry_createkey, #registry_deletekey, #registry_deleteval, #registry_enumkeys, #registry_enumvals, #registry_getvaldata, #registry_getvalinfo, #registry_loadkey, #registry_setvaldata, #registry_unloadkey

Methods included from CliParse

#win_parse_error, #win_parse_results

Instance Method Details

#close_sc_manager(handle) ⇒ Object

Call advapi32.dll!CloseServiceHandle on the given handle


77
78
79
80
81
# File 'lib/msf/core/post/windows/services.rb', line 77

def close_sc_manager(handle)
  if handle
    session.railgun.advapi32.CloseServiceHandle(handle)
  end
end

#open_sc_manager(opts = {}) {|manager| ... } ⇒ Fixnum

Open the service manager with advapi32.dll!OpenSCManagerA on the given host or the local machine if :host option is nil. If called with a block, yields the manager and closes it when the block returns.

Parameters:

  • opts (Hash) (defaults to: {})

Options Hash (opts):

Yields:

  • (manager)

    Gives the block a manager handle as returned by advapi32.dll!OpenSCManagerA. When the block returns, the handle will be closed with #close_sc_manager.

Returns:

  • (Fixnum)

    Opaque Windows handle SC_HANDLE as returned by OpenSCManagerA()

Raises:

  • (RuntimeError)

    if OpenSCManagerA returns a NULL handle


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/msf/core/post/windows/services.rb', line 48

def open_sc_manager(opts={})
  host = opts[:host] || nil
  access = opts[:access] || 0xF003F
  machine_str = host ? "\\\\#{host}" : nil

  # SC_HANDLE WINAPI OpenSCManager(
  #   _In_opt_  LPCTSTR lpMachineName,
  #   _In_opt_  LPCTSTR lpDatabaseName,
  #   _In_      DWORD dwDesiredAccess
  # );
  manag = session.railgun.advapi32.OpenSCManagerA(machine_str,nil,access)
  if (manag["return"] == 0)
    raise RuntimeError.new("Unable to open service manager, GetLastError: #{manag["GetLastError"]}")
  end

  if (block_given?)
    begin
      yield manag["return"]
    ensure
      close_sc_manager(manag["return"])
    end
  else
    return manag["return"]
  end
end

#service_change_startup(name, mode) ⇒ Object

TODO:

Rewrite to allow operating on a remote host

Changes a given service startup mode, name must be provided and the mode.

Mode is a string with either auto, manual or disable for the corresponding setting. The name of the service is case sensitive.


154
155
156
157
158
159
160
161
162
163
164
# File 'lib/msf/core/post/windows/services.rb', line 154

def service_change_startup(name,mode)
  servicekey = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\#{name.chomp}"
  case mode.downcase
  when "auto" then
    registry_setvaldata(servicekey,"Start","2","REG_DWORD")
  when "manual" then
    registry_setvaldata(servicekey,"Start","3","REG_DWORD")
  when "disable" then
    registry_setvaldata(servicekey,"Start","4","REG_DWORD")
  end
end

#service_create(name, display_name, executable_on_host, startup = 2, server = nil) ⇒ true, false

Create a service that runs executable_on_host on the session host

Parameters:

  • name (String)

    Name of the service to be used as the key

  • display_name (String)

    Name of the service as displayed by mmc

  • executable_on_host (String)

    EXE on the remote filesystem to be used as the service executable

  • startup (Fixnum) (defaults to: 2)

    Constant used by CreateServiceA for startup type: 2 for Auto, 3 for Manual, 4 for Disable. Default is Auto

  • server (String, nil) (defaults to: nil)

    A hostname or IP address. Default is the remote localhost

Returns:

  • (true, false)

    True if there were no errors, false otherwise


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/msf/core/post/windows/services.rb', line 180

def service_create(name, display_name, executable_on_host, startup=2, server=nil)
  adv = session.railgun.advapi32

  # SC_MANAGER_CONNECT           0x01
  # SC_MANAGER_CREATE_SERVICE    0x02
  # SC_MANAGER_QUERY_LOCK_STATUS 0x10
  open_sc_manager(:host=>server, :access=>0x13) do |manager|
    # SC_HANDLE WINAPI CreateService(
    #  __in       SC_HANDLE hSCManager,
    #  __in       LPCTSTR lpServiceName,
    #  __in_opt   LPCTSTR lpDisplayName,
    #  __in       DWORD dwDesiredAccess,
    #  __in       DWORD dwServiceType,
    #  __in       DWORD dwStartType,
    #  __in       DWORD dwErrorControl,
    #  __in_opt   LPCTSTR lpBinaryPathName,
    #  __in_opt   LPCTSTR lpLoadOrderGroup,
    #  __out_opt  LPDWORD lpdwTagId,
    #  __in_opt   LPCTSTR lpDependencies,
    #  __in_opt   LPCTSTR lpServiceStartName,
    #  __in_opt   LPCTSTR lpPassword
    #);
    newservice = adv.CreateServiceA(manager, name, display_name,
      0x0010, 0X00000010, startup, 0, executable_on_host,
      nil, nil, nil, nil, nil)
    adv.CloseServiceHandle(newservice["return"])
    if newservice["GetLastError"] == 0
      return true
    else
      return false
    end
  end
end

#service_delete(name, server = nil) ⇒ Object

Delete a service.

Parameters:

  • name (String)

    Service name (not display name)

  • server (String, nil) (defaults to: nil)

    A hostname or IP address. Default is the remote localhost


288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/msf/core/post/windows/services.rb', line 288

def service_delete(name, server=nil)
  adv = session.railgun.advapi32

  open_sc_manager(:host=>server) do |manager|
    # Now to grab a handle to the service.
    # Thank you, Wine project for defining the DELETE constant since it,
    # and all its friends, are missing from the MSDN docs.
    # #define DELETE 0x00010000
    handle = adv.OpenServiceA(manager, name, 0x10000)
    if (handle["return"] == 0)
      raise RuntimeError.new("Could not open service. OpenServiceA error: #{handle["GetLastError"]}")
    end

    # Lastly, delete it
    adv.DeleteService(handle["return"])

    adv.CloseServiceHandle(handle["return"])

    handle["GetLastError"]
  end
end

#service_info(name) ⇒ Hash

TODO:

Rewrite to allow operating on a remote host

Get Windows Service information.

Information returned in a hash with display name, startup mode and command executed by the service. Service name is case sensitive. Hash keys are Name, Start, Command and Credentials.

Parameters:

  • name (String)

    The target service's name (not to be confused with Display Name). Case sensitive.

Returns:

  • (Hash)

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/msf/core/post/windows/services.rb', line 129

def service_info(name)
  service = {}
  servicekey = "HKLM\\SYSTEM\\CurrentControlSet\\Services\\#{name.chomp}"
  service["Name"] = registry_getvaldata(servicekey,"DisplayName").to_s
  srvstart = registry_getvaldata(servicekey,"Start").to_i
  if srvstart == 2
    service["Startup"] = "Auto"
  elsif srvstart == 3
    service["Startup"] = "Manual"
  elsif srvstart == 4
    service["Startup"] = "Disabled"
  end
  service["Command"] = registry_getvaldata(servicekey,"ImagePath").to_s
  service["Credentials"] = registry_getvaldata(servicekey,"ObjectName").to_s
  return service
end

#service_listArray

TODO:

Rewrite to allow operating on a remote host

List all Windows Services present

Returns:

  • (Array)

    The names of the services.


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/msf/core/post/windows/services.rb', line 90

def service_list
  serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services"
  a =[]
  services = []
  keys = registry_enumkeys(serviceskey)
  keys.each do |s|
    if a.length >= 10
      a.first.join
      a.delete_if {|x| not x.alive?}
    end
    t = framework.threads.spawn(self.refname+"-ServiceRegistryList",false,s) { |sk|
      begin
        srvtype = registry_getvaldata("#{serviceskey}\\#{sk}","Type").to_s
        if srvtype == "32" or srvtype == "16"
          services << sk
        end
      rescue
      end
    }
    a.push(t)
  end

  return services
end

#service_start(name, server = nil) ⇒ Fixnum

Start a service.

Parameters:

  • name (String)

    Service name (not display name)

  • server (String, nil) (defaults to: nil)

    A hostname or IP address. Default is the remote localhost

Returns:

  • (Fixnum)

    0 if service started successfully, 1 if it failed because the service is already running, 2 if it is disabled

Raises:

  • (RuntimeError)

    if OpenServiceA failed


226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/msf/core/post/windows/services.rb', line 226

def service_start(name, server=nil)
  adv = session.railgun.advapi32
  open_sc_manager(:host=>server, :access=>1) do |manager|
    # SC_HANDLE WINAPI OpenService(
    #   _In_  SC_HANDLE hSCManager,
    #   _In_  LPCTSTR lpServiceName,
    #   _In_  DWORD dwDesiredAccess
    # );
    # open with access SERVICE_START (0x0010)
    handle = adv.OpenServiceA(manager, name, 0x10)
    if(handle["return"] == 0)
      raise RuntimeError.new("Could not open service. OpenServiceA error: #{handle["GetLastError"]}")
    end
    retval = adv.StartServiceA(handle["return"],0,nil)
    adv.CloseServiceHandle(handle["return"])

    # This is terrible. Magic return values should be refactored to
    # something meaningful.
    case retval["GetLastError"]
    when 0;    return 0 # everything worked
    when 1056; return 1 # service already started
    when 1058; return 2 # service disabled
    end
  end
end

#service_status(name, server = nil) ⇒ Object

Query Service Status

Parameters:

  • name (String)

    Service name (not display name)

  • server (String, nil) (defaults to: nil)

    A hostname or IP address. Default is the remote localhost

Returns:

  • {} representing lpServiceStatus

Raises:

  • (RuntimeError)

    if OpenServiceA failed


320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/msf/core/post/windows/services.rb', line 320

def service_status(name, server=nil)
  adv = session.railgun.advapi32
  ret = nil
  
  # 0x80000000 GENERIC_READ 
  open_sc_manager(:host => server, :access => 0x80000000) do |manager|
    # Now to grab a handle to the service.
    handle = adv.OpenServiceA(manager, name, 0x80000000)
    if (handle["return"] == 0)
      raise RuntimeError.new("Could not open service. OpenServiceA error: #{handle["GetLastError"]}")
    end

    status = adv.QueryServiceStatus(handle["return"],28)
    if (status["return"] == 0)
      raise RuntimeError.new("Could not query service. QueryServiceStatus error: #{handle["GetLastError"]}")
    end

    vals = status['lpServiceStatus'].unpack('L*')
    adv.CloseServiceHandle(handle["return"])

    ret = {
      :type              => vals[0],
      :state             => vals[1],
      :controls_accepted => vals[2],
      :win32_exit_code   => vals[3],
      :service_exit_code => vals[4],
      :check_point       => vals[5],
      :wait_hint         => vals[6]
    }
  end

  return ret
end

#service_stop(name, server = nil) ⇒ Fixnum

Stop a service.

Parameters:

  • name (String)

    Service name (not display name)

  • server (String, nil) (defaults to: nil)

    A hostname or IP address. Default is the remote localhost

Returns:

  • (Fixnum)

    0 if service stopped successfully, 1 if it failed because the service is already stopped or disabled, 2 if it cannot be stopped for some other reason.

Raises:

  • (RuntimeError)

    if OpenServiceA failed


262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/msf/core/post/windows/services.rb', line 262

def service_stop(name, server=nil)
  adv = session.railgun.advapi32

  # SC_MANAGER_SERVICE_STOP (0x0020)
  open_sc_manager(:host=>server, :access=>1) do |manager|
    # open with SERVICE_STOP (0x0020)
    handle = adv.OpenServiceA(manager, name, 0x20)
    if(handle["return"] == 0)
      raise RuntimeError.new("Could not open service. OpenServiceA error: #{handle["GetLastError"]}")
    end
    retval = adv.ControlService(handle["return"],1,56)
    adv.CloseServiceHandle(handle["return"])

    case retval["GetLastError"]
    when 0;    return 0 # worked
    when 1062; return 1 # already stopped or disabled
    when 1052; return 2 # cannot be stopped
    end
  end
end