1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-11-05 14:57:30 +01:00

Refactor WindowsServices

* Pulls common code up from several methods into #open_sc_manager
* Deprecates the name Windows::WindowsServices in favor of
  Windows::Services. The platform is already clear from the namespace.
* Makes the post/test/services test module actually work

[See #1007]
[See #1012]
This commit is contained in:
James Lee 2012-11-06 17:30:04 -06:00
parent b973927ab2
commit 34bc92584b
14 changed files with 292 additions and 136 deletions

View File

@ -12,10 +12,10 @@ module Msf::Payload::Php
#
# The generated code will initialize
#
# @options options [String] :disabled_varname PHP variable name in which to
# @option options [String] :disabled_varname PHP variable name in which to
# store an array of disabled functions.
#
# @returns [String] A chunk of PHP code
# @return [String] A chunk of PHP code
#
def php_preamble(options = {})
dis = options[:disabled_varname] || '$' + Rex::Text.rand_text_alpha(rand(4) + 4)
@ -42,15 +42,15 @@ module Msf::Payload::Php
#
# Generate a chunk of PHP code that tries to run a command.
#
# @options options [String] :cmd_varname PHP variable name containing the
# @option options [String] :cmd_varname PHP variable name containing the
# command to run
# @options options [String] :disabled_varname PHP variable name containing
# @option options [String] :disabled_varname PHP variable name containing
# an array of disabled functions. See #php_preamble
# @options options [String] :output_varname PHP variable name in which to
# @option options [String] :output_varname PHP variable name in which to
# store the output of the command. Will contain 0 if no exec functions
# work.
#
# @returns [String] A chunk of PHP code that, with a little luck, will run a
# @return [String] A chunk of PHP code that, with a little luck, will run a
# command.
#
def php_system_block(options = {})

View File

@ -5,20 +5,94 @@ 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
#
# List all Windows Services present. Returns an Array containing the names
# of the services.
# 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"
threadnum = 0
a =[]
services = []
registry_enumkeys(serviceskey).each do |s|
if threadnum < 10
1.upto 10 do
a.push(::Thread.new(s) { |sk|
begin
srvtype = registry_getvaldata("#{serviceskey}\\#{sk}","Type").to_s
@ -28,10 +102,10 @@ module WindowsServices
rescue
end
})
threadnum += 1
else
sleep(0.05) and a.delete_if {|x| not x.alive?} while not a.empty?
threadnum = 0
end
until a.empty?
a.first.join
a.delete_if {|x| not x.alive?}
end
end
@ -45,6 +119,13 @@ module WindowsServices
# 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}"
@ -68,6 +149,8 @@ module WindowsServices
# 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
@ -81,22 +164,30 @@ module WindowsServices
end
#
# Create a service that runs it's own process.
# Create a service that runs +executable_on_host+ on the session host
#
# It takes as values the service name as string, the display name as
# string, the path of the executable on the host that will execute at
# startup as string and the startup type as an integer of 2 for Auto, 3 for
# Manual or 4 for Disable, default Auto.
# @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)
machine_str = server ? "\\\\#{server}" : nil
adv = session.railgun.advapi32
manag = adv.OpenSCManagerA(machine_str,nil,0x13)
if(manag["return"] != 0)
# 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_opt LPCTSTR lpDisplayName,
# __in DWORD dwDesiredAccess,
# __in DWORD dwServiceType,
# __in DWORD dwStartType,
@ -108,113 +199,112 @@ module WindowsServices
# __in_opt LPCTSTR lpServiceStartName,
# __in_opt LPCTSTR lpPassword
#);
# SC_MANAGER_CREATE_SERVICE = 0x0002
newservice = adv.CreateServiceA(manag["return"],name,display_name,
0x0010,0X00000010,startup,0,executable_on_host,nil,nil,nil,nil,nil)
newservice = adv.CreateServiceA(manager, name, display_name,
0x0010, 0X00000010, startup, 0, executable_on_host,
nil, nil, nil, nil, nil)
adv.CloseServiceHandle(newservice["return"])
adv.CloseServiceHandle(manag["return"])
#SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010
#SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0
if newservice["GetLastError"] == 0
return true
else
return false
end
else
raise "Could not open Service Control Manager, Access Denied"
end
end
#
# Start a service.
#
# Returns 0 if service started, 1 if service is already started and 2 if
# service is disabled.
# @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)
machine_str = server ? "\\\\#{server}" : nil
adv = session.railgun.advapi32
manag = adv.OpenSCManagerA(machine_str,nil,1)
if(manag["return"] == 0)
raise "Could not open Service Control Manager, Access Denied"
end
#open with SERVICE_START (0x0010)
servhandleret = adv.OpenServiceA(manag["return"],name,0x10)
if(servhandleret["return"] == 0)
adv.CloseServiceHandle(manag["return"])
raise "Could not Open Service, Access Denied"
end
retval = adv.StartServiceA(servhandleret["return"],0,nil)
adv.CloseServiceHandle(servhandleret["return"])
adv.CloseServiceHandle(manag["return"])
if retval["GetLastError"] == 0
return 0
elsif retval["GetLastError"] == 1056
return 1
elsif retval["GetLastError"] == 1058
return 2
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.
#
# Returns 0 if service is stopped successfully, 1 if service is already
# stopped or disabled and 2 if the service can not be stopped.
# @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)
machine_str = server ? "\\\\#{server}" : nil
adv = session.railgun.advapi32
manag = adv.OpenSCManagerA(machine_str,nil,1)
if(manag["return"] == 0)
raise "Could not open Service Control Manager, Access Denied"
end
#open with SERVICE_STOP (0x0020)
servhandleret = adv.OpenServiceA(manag["return"],name,0x30)
if(servhandleret["return"] == 0)
adv.CloseServiceHandle(manag["return"])
raise "Could not Open Service, Access Denied"
end
retval = adv.ControlService(servhandleret["return"],1,56)
adv.CloseServiceHandle(servhandleret["return"])
adv.CloseServiceHandle(manag["return"])
if retval["GetLastError"] == 0
return 0
elsif retval["GetLastError"] == 1062
return 1
elsif retval["GetLastError"] == 1052
return 2
# 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)
machine_str = server ? "\\\\#{server}" : nil
adv = session.railgun.advapi32
# #define SC_MANAGER_ALL_ACCESS 0xF003F
manag = adv.OpenSCManagerA(machine_str,nil,0xF003F)
if (manag["return"] == 0)
raise "Could not open Service Control Manager, Access Denied"
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
# 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
servhandleret = adv.OpenServiceA(manag["return"],name,0x10000)
if (servhandleret["return"] == 0)
adv.CloseServiceHandle(manag["return"])
raise "Could not Open Service, Access Denied"
end
# Lastly, delete it
adv.DeleteService(servhandleret["return"])
adv.CloseServiceHandle(manag["return"])
adv.CloseServiceHandle(servhandleret["return"])
end
end

View File

@ -10,7 +10,7 @@ module Windows
# http://pauldotcom.com/2011/11/safely-dumping-hashes-from-liv.html
module ShadowCopy
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
#
# Get the device name for the shadow copy, which is used when accessing

View File

@ -6,7 +6,7 @@ module Scripts
module Meterpreter
module Common
include ::Msf::Post::Windows::WindowsServices
include ::Msf::Post::Windows::Services
end
end

View File

@ -20,7 +20,7 @@ class Metasploit3 < Msf::Exploit::Local
Rank = ExcellentRanking
include Post::Common
include Post::Windows::WindowsServices
include Post::Windows::Services
include Exploit::EXE
include Post::File

View File

@ -12,7 +12,7 @@ require 'rex'
class Metasploit3 < Msf::Exploit::Local
Rank = GreatRanking
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
def initialize(info={})
super( update_info( info,

View File

@ -17,7 +17,7 @@ class Metasploit3 < Msf::Exploit::Local
include Msf::Exploit::EXE
include Msf::Post::Common
include Msf::Post::File
include Post::Windows::WindowsServices
include Post::Windows::Services
def initialize(info={})
super( update_info( info,

View File

@ -15,7 +15,7 @@ require 'rex'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
def initialize(info={})
super( update_info( info,

View File

@ -16,7 +16,7 @@ require 'msf/core//post/windows/services'
class Metasploit3 < Msf::Post
include ::Msf::Post::Windows::WindowsServices
include ::Msf::Post::Windows::Services
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Escalate Service Permissions Local Privilege Escalation',

View File

@ -14,7 +14,7 @@ require 'msf/core/post/windows/services'
class Metasploit3 < Msf::Post
include Post::Windows::WindowsServices
include Post::Windows::Services
def initialize
super(

View File

@ -16,7 +16,7 @@ require 'msf/core/post/windows/services'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
def initialize(info={})
super(update_info(info,

View File

@ -21,7 +21,7 @@ class Metasploit3 < Msf::Post
include Msf::Post::Windows::Accounts
include Msf::Post::Windows::Registry
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
include Msf::Post::Windows::Priv
include Msf::Post::Common
include Msf::Post::File

View File

@ -23,7 +23,7 @@ class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Registry
include Msf::Post::Windows::WindowsServices
include Msf::Post::Windows::Services
def initialize(info={})
super( update_info( info,

View File

@ -6,35 +6,124 @@ require 'msf/core'
require 'rex'
require 'msf/core/post/windows/services'
load 'lib/msf/core/post/windows/services.rb'
class Metasploit3 < Msf::Post
include Msf::Post::Windows::WindowsServices
include Msf::ModuleTest::PostTest
def initialize(info={})
super( update_info( info,
'Name' => 'services_post_testing',
'Description' => %q{ This module will test windows services methods within a shell},
'License' => MSF_LICENSE,
'Author' => [ 'kernelsmith'],
'Author' => [ 'kernelsmith', 'egypt' ],
'Version' => '$Revision: 11663 $',
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'shell' ]
'SessionTypes' => [ 'meterpreter', 'shell' ]
))
register_options(
[
OptBool.new("VERBOSE" , [true, "Verbose test, shows service status after each test", false]),
OptString.new("QSERVICE" , [true, "Service (keyname) to query", "winmgmt"]),
OptString.new("NSERVICE" , [true, "New Service (keyname) to create/del", "testes"]),
OptString.new("SSERVICE" , [true, "Service (keyname) to start/stop", "W32Time"]),
OptString.new("MODE" , [true, "Mode to use for startup/create tests", "demand"]),
OptString.new("DNAME" , [true, "Display name used for create test", "Cool display name"]),
OptString.new("BINPATH" , [true, "Binary path for create test", "C:\\WINDOWS\\system32\\svchost.exe -k netsvcs"]),
OptEnum.new("MODE", [true, "Mode to use for startup/create tests", "auto",
["auto", "manual", "disable"]
]),
], self.class)
end
def test_start
it "should start #{datastore["SSERVICE"]}" do
ret = true
results = service_start(datastore['SSERVICE'])
ret &&= (results == 0)
ret
end
it "should stop #{datastore["SSERVICE"]}" do
ret = true
results = service_stop(datastore['SSERVICE'])
ret &&= (results == 0)
ret
end
end
def test_list
it "should list services" do
ret = true
results = service_list
ret &&= results.kind_of? Array
ret &&= results.length > 0
ret &&= results.include? datastore["QSERVICE"]
ret
end
end
def test_info
it "should return info on a given service" do
ret = true
results = service_info(datastore['QSERVICE'])
ret &&= results.kind_of? Hash
if ret
ret &&= results.has_key? "Name"
ret &&= (results["Name"] == "Windows Management Instrumentation")
ret &&= results.has_key? "Startup"
ret &&= results.has_key? "Command"
ret &&= results.has_key? "Credentials"
end
ret
end
end
def test_create
it "should create a service" do
mode = case datastore["MODE"]
when "disable"; 4
when "manual"; 3
when "auto"; 2
else; 2
end
ret = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],mode)
ret
end
it "should return info on the newly-created service" do
ret = true
results = service_info(datastore['NSERVICE'])
ret &&= results.kind_of? Hash
ret &&= results.has_key? "Name"
ret &&= (results["Name"] == datastore["DNAME"])
ret &&= results.has_key? "Startup"
ret &&= (results["Startup"].downcase == datastore["MODE"])
ret &&= results.has_key? "Command"
ret &&= results.has_key? "Credentials"
ret
end
it "should delete the new service" do
ret = service_delete(datastore['NSERVICE'])
ret
end
end
=begin
def run
blab = datastore['VERBOSE']
print_status("Running against session #{datastore["SESSION"]}")
print_status("Session type is #{session.type}")
@ -43,21 +132,6 @@ class Metasploit3 < Msf::Post
print_line("than the windows SCM, just make sure the errors are sane. You can")
print_line("set VERBOSE to true to see more details")
print_status()
print_status("TESTING service_list")
results = service_list
print_status("RESULTS: #{results.class} #{results.pretty_inspect}")
print_status()
print_status("TESTING service_list_running")
results = service_list_running
print_status("RESULTS: #{results.class} #{results.pretty_inspect}")
print_status()
print_status("TESTING service_info on servicename: #{datastore["QSERVICE"]}")
results = service_info(datastore['QSERVICE'])
print_status("RESULTS: #{results.class} #{results.pretty_inspect}")
print_status()
print_status("TESTING service_query_ex on servicename: #{datastore["QSERVICE"]}")
results = service_query_ex(datastore['QSERVICE'])
@ -76,15 +150,6 @@ class Metasploit3 < Msf::Post
print_status("Current status of this service " +
"#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab
print_status()
print_status("TESTING service_create on servicename: #{datastore['NSERVICE']} using\n" +
"display_name: #{datastore['DNAME']}, executable_on_host: " +
"#{datastore['BINPATH']}, and startupmode: #{datastore['MODE']}")
results = service_create(datastore['NSERVICE'],datastore['DNAME'],datastore['BINPATH'],datastore['MODE'])
print_status("RESULTS: #{results.class} #{results.pretty_inspect}")
print_status("Current status of this service " +
"#{service_query_ex(datastore['QSERVICE']).pretty_inspect}") if blab
print_status()
print_status("TESTING service_start on servicename: #{datastore['SSERVICE']}")
results = service_start(datastore['SSERVICE'])
@ -110,5 +175,6 @@ class Metasploit3 < Msf::Post
print_status()
print_status("Testing complete.")
end
=end
end