314 lines
9.6 KiB
Ruby
314 lines
9.6 KiB
Ruby
# -*- coding: binary -*-
|
|
require 'msf/core/post/windows/registry'
|
|
|
|
module Msf
|
|
class Post
|
|
module Windows
|
|
|
|
|
|
# @deprecated Use {Services} instead
|
|
module WindowsServices
|
|
def self.included(base)
|
|
include Services
|
|
end
|
|
|
|
def setup
|
|
print_error("The Windows::WindowsServices mixin is deprecated, use Windows::Services instead")
|
|
super
|
|
end
|
|
end
|
|
|
|
#
|
|
# Post module mixin for dealing with Windows services
|
|
#
|
|
module Services
|
|
|
|
include ::Msf::Post::Windows::Registry
|
|
|
|
#
|
|
# 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.
|
|
#
|
|
# @param opts [Hash]
|
|
# @option opts [String] :host (nil) The host on which to open the
|
|
# service manager. May be a hostname or IP address.
|
|
# @option opts [Fixnum] :access (0xF003F) Bitwise-or of the
|
|
# SC_MANAGER_* constants (see
|
|
# {http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx})
|
|
#
|
|
# @return [Fixnum] Opaque Windows handle SC_HANDLE as returned by
|
|
# OpenSCManagerA()
|
|
# @yield [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}.
|
|
# @raise [RuntimeError] if OpenSCManagerA returns a NULL handle
|
|
#
|
|
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
|
|
|
|
#
|
|
# Call advapi32.dll!CloseServiceHandle on the given handle
|
|
#
|
|
def close_sc_manager(handle)
|
|
if handle
|
|
session.railgun.advapi32.CloseServiceHandle(handle)
|
|
end
|
|
end
|
|
|
|
#
|
|
# List all Windows Services present
|
|
#
|
|
# @return [Array] The names of the services.
|
|
#
|
|
# @todo Rewrite to allow operating on a remote host
|
|
#
|
|
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
|
|
|
|
#
|
|
# 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.
|
|
#
|
|
# @param name [String] The target service's name (not to be confused
|
|
# with Display Name). Case sensitive.
|
|
#
|
|
# @return [Hash]
|
|
#
|
|
# @todo Rewrite to allow operating on a remote host
|
|
#
|
|
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
|
|
|
|
#
|
|
# 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.
|
|
#
|
|
# @todo Rewrite to allow operating on a remote host
|
|
#
|
|
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
|
|
|
|
#
|
|
# Create a service that runs +executable_on_host+ on the session host
|
|
#
|
|
# @param name [String] Name of the service to be used as the key
|
|
# @param display_name [String] Name of the service as displayed by mmc
|
|
# @param executable_on_host [String] EXE on the remote filesystem to
|
|
# be used as the service executable
|
|
# @param startup [Fixnum] Constant used by CreateServiceA for startup
|
|
# type: 2 for Auto, 3 for Manual, 4 for Disable. Default is Auto
|
|
# @param server [String,nil] A hostname or IP address. Default is the
|
|
# remote localhost
|
|
#
|
|
# @return [true,false] True if there were no errors, false otherwise
|
|
#
|
|
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
|
|
|
|
#
|
|
# Start a service.
|
|
#
|
|
# @param name [String] Service name (not display name)
|
|
# @param server [String,nil] A hostname or IP address. Default is the
|
|
# remote localhost
|
|
#
|
|
# @return [Fixnum] 0 if service started successfully, 1 if it failed
|
|
# because the service is already running, 2 if it is disabled
|
|
#
|
|
# @raise [RuntimeError] if OpenServiceA failed
|
|
#
|
|
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
|
|
|
|
#
|
|
# Stop a service.
|
|
#
|
|
# @param (see #service_start)
|
|
# @return [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.
|
|
#
|
|
# @raise (see #service_start)
|
|
#
|
|
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
|
|
|
|
#
|
|
# Delete a service.
|
|
#
|
|
# @param (see #service_start)
|
|
#
|
|
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
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|