Land #9869, Add support for shellcode encryption for msfvenom

GSoC/Meterpreter_Web_Console
Jeffrey Martin 2018-04-25 15:51:05 -05:00
commit 2487314821
No known key found for this signature in database
GPG Key ID: 0CD9BBC2AF15F171
9 changed files with 254 additions and 22 deletions

View File

@ -18,9 +18,13 @@ module Buffer
# Serializes a buffer to a provided format. The formats supported are raw,
# num, dword, ruby, python, perl, bash, c, js_be, js_le, java and psh
#
def self.transform(buf, fmt = "ruby", var_name = 'buf')
def self.transform(buf, fmt = "ruby", var_name = 'buf', encryption_opts={})
default_wrap = 60
unless encryption_opts.empty?
buf = encrypt_buffer(buf, encryption_opts)
end
case fmt
when 'raw'
when 'num'
@ -120,6 +124,50 @@ module Buffer
]
end
def self.encryption_formats
[
'xor',
'base64',
'aes256',
'rc4'
]
end
private
def self.encrypt_buffer(value, encryption_opts)
buf = ''
case encryption_opts[:format]
when 'aes256'
if encryption_opts[:iv].blank?
raise ArgumentError, 'Initialization vector is missing'
elsif encryption_opts[:key].blank?
raise ArgumentError, 'Encryption key is missing'
end
buf = Rex::Crypto.encrypt_aes256(encryption_opts[:iv], encryption_opts[:key], value)
when 'base64'
buf = Rex::Text.encode_base64(value)
when 'xor'
if encryption_opts[:key].blank?
raise ArgumentError, 'XOR key is missing'
end
buf = Rex::Text.xor(encryption_opts[:key], value)
when 'rc4'
if encryption_opts[:key].blank?
raise ArgumentError, 'Encryption key is missing'
end
buf = Rex::Crypto.rc4(encryption_opts[:key], value)
else
raise ArgumentError, "Unsupported encryption format: #{encryption_opts[:format]}", caller
end
return buf
end
end
end

View File

@ -83,6 +83,15 @@ module Msf
# @!attribute var_name
# @return [String] The custom variable string for certain output formats
attr_accessor :var_name
# @!attribute encryption_format
# @return [String] The encryption format to use for the shellcode.
attr_accessor :encryption_format
# @!attribute encryption_key
# @return [String] The key to use for the encryption
attr_accessor :encryption_key
# @!attribute encryption_iv
# @return [String] The initialization vector for the encryption (not all apply)
attr_accessor :encryption_iv
# @param opts [Hash] The options hash
@ -123,6 +132,9 @@ module Msf
@var_name = opts.fetch(:var_name, 'buf')
@smallest = opts.fetch(:smallest, false)
@encoder_space = opts.fetch(:encoder_space, @space)
@encryption_format = opts.fetch(:encryption_format, nil)
@encryption_key = opts.fetch(:encryption_key, nil)
@encryption_iv = opts.fetch(:encryption_iv, nil)
@framework = opts.fetch(:framework)
@ -276,15 +288,20 @@ module Msf
# @param shellcode [String] the processed shellcode to be formatted
# @return [String] The final formatted form of the payload
def format_payload(shellcode)
encryption_opts = {}
encryption_opts[:format] = encryption_format if encryption_format
encryption_opts[:iv] = encryption_iv if encryption_iv
encryption_opts[:key] = encryption_key if encryption_key
case format.downcase
when "js_be"
if Rex::Arch.endian(arch) != ENDIAN_BIG
raise IncompatibleEndianess, "Big endian format selected for a non big endian payload"
else
::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
::Msf::Simple::Buffer.transform(shellcode, format, @var_name, encryption_opts)
end
when *::Msf::Simple::Buffer.transform_formats
::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
::Msf::Simple::Buffer.transform(shellcode, format, @var_name, encryption_opts)
when *::Msf::Util::EXE.to_executable_fmt_formats
::Msf::Util::EXE.to_executable_fmt(framework, arch, platform_list, shellcode, format, exe_options)
else

View File

@ -114,6 +114,10 @@ require 'rex/compat'
require 'rex/sslscan/scanner'
require 'rex/sslscan/result'
# Cryptography
require 'rex/crypto/aes256'
require 'rex/crypto/rc4'
# Overload the Kernel.sleep() function to be thread-safe
Kernel.class_eval("

33
lib/rex/crypto/aes256.rb Normal file
View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
module Rex
module Crypto
# Returns an encrypted string using AES256-CBC.
#
# @param iv [String] Initialization vector.
# @param key [String] Secret key.
# @return [String] The encrypted string.
def self.encrypt_aes256(iv, key, value)
aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.encrypt
aes.iv = iv
aes.key = key
aes.update(value) + aes.final
end
# Returns a decrypted string using AES256-CBC.
#
# @param iv [String] Initialization vector.
# @param key [String] Secret key.
# @return [String] The decrypted string.
def self.decrypt_aes256(iv, key, value)
aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.decrypt
aes.iv = iv
aes.key = key
aes.update(value) + aes.final
end
end
end

20
lib/rex/crypto/rc4.rb Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: binary -*-
require 'rc4'
module Rex
module Crypto
# Returns a decrypted or encrypted RC4 string.
#
# @param key [String] Secret key.
# @param [String]
def self.rc4(key, value)
rc4 = RC4.new(key)
# This can also be used to decrypt
rc4.encrypt(value)
end
end
end

View File

@ -63,12 +63,13 @@ def parse_args(args)
opt = OptionParser.new
banner = "MsfVenom - a Metasploit standalone payload generator.\n"
banner << "Also a replacement for msfpayload and msfencode.\n"
banner << "Usage: #{$0} [options] <var=val>"
banner << "Usage: #{$0} [options] <var=val>\n"
banner << "Example: #{$0} -p windows/meterpreter/reverse_tcp LHOST=<IP> -f exe -o payload.exe"
opt.banner = banner
opt.separator('')
opt.separator('Options:')
opt.on('-p', '--payload <payload>', String,
opt.on('-p', '--payload <payload>', String,
'Payload to use. Specify a \'-\' or stdin to use custom payloads') do |p|
if p == '-'
opts[:payload] = 'stdin'
@ -81,18 +82,18 @@ def parse_args(args)
opts[:list_options] = true
end
opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l|
opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l|
if l.nil? or l.empty?
l = ["all"]
end
opts[:list] = l
end
opt.on('-n', '--nopsled <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
opt.on('-n', '--nopsled <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
opts[:nops] = n.to_i
end
opt.on('-f', '--format <format>', String, "Output format (use --help-formats for a list)") do |f|
opt.on('-f', '--format <format>', String, "Output format (use --help-formats for a list)") do |f|
opts[:format] = f
end
@ -105,15 +106,34 @@ def parse_args(args)
raise HelpError, msg
end
opt.on('-e', '--encoder <encoder>', String, 'The encoder to use') do |e|
opt.on('--encrypt <value>', String, 'The type of encryption or encoding to apply to the shellcode') do |e|
opts[:encryption_format] = e
end
opt.on('--encrypt-formats', String, 'List Available encryption formats') do
init_framework(:module_types => [])
msg = "Encryption formats:\n" +
"\t" + ::Msf::Simple::Buffer.encryption_formats.join(", ")
raise HelpError, msg
end
opt.on('--encrypt-key <value>', String, 'A key to be used for the encryptor') do |e|
opts[:encryption_key] = e
end
opt.on('--encrypt-iv <value>', String, 'An initialization vector for the encryption') do |e|
opts[:encryption_iv] = e
end
opt.on('-e', '--encoder <encoder>', String, 'The encoder to use') do |e|
opts[:encoder] = e
end
opt.on('-a', '--arch <arch>', String, 'The architecture to use') do |a|
opt.on('-a', '--arch <arch>', String, 'The architecture to use') do |a|
opts[:arch] = a
end
opt.on('--platform <platform>', String, 'The platform of the payload') do |l|
opt.on('--platform <platform>', String, 'The platform of the payload') do |l|
opts[:platform] = l
end
@ -126,28 +146,28 @@ def parse_args(args)
raise HelpError, msg
end
opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
opts[:space] = s
end
opt.on('--encoder-space <length>', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
opt.on('--encoder-space <length>', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
opts[:encoder_space] = s
end
opt.on('-b', '--bad-chars <list>', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
opt.on('-b', '--bad-chars <list>', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
init_framework()
opts[:badchars] = Rex::Text.hex_to_raw(b)
end
opt.on('-i', '--iterations <count>', Integer, 'The number of times to encode the payload') do |i|
opt.on('-i', '--iterations <count>', Integer, 'The number of times to encode the payload') do |i|
opts[:iterations] = i
end
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
opts[:add_code] = x
end
opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
opts[:template] = x
end
@ -155,11 +175,11 @@ def parse_args(args)
opts[:keep] = true
end
opt.on('-o', '--out <path>', 'Save the payload') do |x|
opt.on('-o', '--out <path>', 'Save the payload') do |x|
opts[:out] = x
end
opt.on('-v', '--var-name <name>', String, 'Specify a custom variable name to use for certain output formats') do |x|
opt.on('-v', '--var-name <name>', String, 'Specify a custom variable name to use for certain output formats') do |x|
opts[:var_name] = x
end
@ -336,8 +356,8 @@ if generator_opts[:list_options]
$stderr.puts "Advanced options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ')
$stderr.puts "Evasion options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ')
$stderr.puts "Encryption options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_encrypt_options(payload_mod, ' ')
exit(0)
end

View File

@ -1056,7 +1056,7 @@ RSpec.describe Msf::PayloadGenerator do
}
}
it 'applies the appropriate transform format' do
expect(::Msf::Simple::Buffer).to receive(:transform).with(shellcode, 'c', 'buf')
expect(::Msf::Simple::Buffer).to receive(:transform).with(shellcode, 'c', 'buf', {})
payload_generator.format_payload(shellcode)
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
require 'securerandom'
RSpec.describe Rex::Crypto do
let(:iv) {
SecureRandom.random_bytes(16)
}
let(:key) {
SecureRandom.random_bytes(32)
}
let(:value) {
'Hello World'
}
describe '#encrypt_aes256' do
it 'raises an exception due to a short IV' do
iv = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.encrypt_aes256(iv, key, value) }.to raise_exception
end
it 'raises an exception due to a short key' do
key = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.encrypt_aes256(iv, key, value) }.to raise_exception
end
it 'encrypts the string Hello World' do
encrypted_str = Rex::Crypto.encrypt_aes256(iv, key, value)
expect(encrypted_str).not_to eq(value)
end
end
describe '#decrypt_aes256' do
it 'raises an exception due to a short IV' do
iv = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.decrypt_aes256(iv, key, value) }.to raise_exception
end
it 'raises an exception due to a short key' do
key = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.decrypt_aes256(iv, key, value) }.to raise_exception
end
it 'decrypts the value to Hello World' do
encrypted_str = Rex::Crypto.encrypt_aes256(iv, key, value)
decrypted_str = Rex::Crypto.decrypt_aes256(iv, key, encrypted_str)
expect(decrypted_str).to eq(value)
end
end
end

View File

@ -0,0 +1,28 @@
require 'spec_helper'
require 'securerandom'
RSpec.describe Rex::Crypto do
describe '#rc4' do
let(:key) {
SecureRandom.random_bytes(32)
}
let(:value) {
'Hello World'
}
it 'encrypts a string' do
expect(Rex::Crypto.rc4(key, value)).not_to eq(value)
end
it 'decrypts a string' do
encrypted_str = Rex::Crypto.rc4(key, value)
decrypted_str = Rex::Crypto.rc4(key, encrypted_str)
expect(decrypted_str).to eq(value)
end
end
end