C3P0链
C3P0
早期的数据库连接池工具包 控制管理数据库连接
避免重复多次创建销毁连接
创建一次数据源 就可以直接多次使用
依赖地址
1 | <dependency> |
DataSource
数据源。为了提高系统性能,在真实的Java项目中通过不会使用原生的JDBC的DriverManager去连接数据库,而是使用数据源(java.sql.DataSource)来代替DriverManager管理数据库的连接。
其中C3P0就是一种常见的数据源,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。它提供了高效管理和复用数据库连接的功能。
简单使用
1 | ComboPooledDataSource source = new ComboPooledDataSource(); |
C3P0 URLClassLoader远程类加载
核心类
- PoolBackedDataSourceBase
- ReferenceIndirector$ReferenceSerialized
- ReferenceableUtils
PoolBackedDataSourceBase
重点在writeObject和readObject


来看看这个类 ReferenceIndirector
上面如果没有成功序列化 则会进入异常中
进入ReferenceIndirector.indirectForm

调用getReference() 被调用的对象必须要继承Reference接口才行 不然直接就异常跳出了
后续会实例化一个ReferenceSerialized
注意这里的ref可控 其他的参数都不可控
ref来自于ConnectionPoolDataSource成员对象
实例化的作用就是填入一些内容而已

writeObject方法的下面又是类似的操作 只不过换成了extensions 我们这里没用到他
接下来看看readObject方法

判断对象里面是否有 IndirectlySerialized
有则调用getObject方法
这里实际上调用刚刚序列化的IndirectlySerialized对象的getObject方法

可以看到会调用lookup方法 那么可以直接打jndi吗

刚刚也就reference是可控的地方 contextName是不可控的
所以没法直接打jndi
进入下面的 ReferenceableUtils.referenceToObject( reference, name, nameContext, env )


这个部分会获取ref对象的工厂类名和工厂类的地址也就是url了
然后获取ClassLoader对象

将获取的url包装为一个URL对象
创建一个URLCLassLoader对象 传入刚刚的URL对象和类加载器
这里指定类加载器尝试加载工厂类 Class fClass = Class.forName( fClassName, true, cl );
由于指定的是URLCLassLoader这个类加载器 那么他会尝试远程类加载
如果成功获取到相对于的类就直接实例化
我们只需要在PoolBackedDataSourceBase里面填入一个connectionPoolDataSource成员对象就可以了

调用setter就可以了
POC
1 | import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; |
调试一下

传入的成员对象要继承这2个接口

这个方法还得实现一下

断点打在writeObject上面

刚刚传入的对象没法序列化 所以进入下面的语句中
实例化一个ReferenceIndirector对象 并调用indirectForm并传入connectionPoolDataSource成员

刚刚实现了getReference方法 现在的引用对象ref有这些内容 都是后面远程类加载需要的

实例化内部类ReferenceSerialized
现在来到readobject方法

进入getObject方法

进入ReferenceableUtils.referenceToObject方法

上面是获取远程类的一些基本信息 下面开始加载这个远程类

远程的这个地方收到请求

成功加载并初始化
由于这里是调用forName方法 会触发类的静态方法 所以这里直接就会弹了
这条链子一个触发类就成了
并且也只要传入一个对象 其他的都是内部调用还是太权威了 出不过不出网就没法利用了
C3P0 jndi
这个工具包支持jndi 所以很多地方都有jndi的影子
这个链子打fastjson解析反序列化 或者是fastjson原生反序列化
核心类
- JndiRefConnectionPoolDataSource
- WrapperConnectionPoolDataSource
- JndiRefDataSourceBase
- JndiRefForwardingDataSource
JndiRefConnectionPoolDataSource
这个类名字就和jndi连接有关


跟进一下这个方法


会获得一个成员变量如何调用getLoginTimeout方法

我们先转到调试的时候的这个类
跟进inner方法

进入dereference方法

获取jndi地址 然后lookup 可以打jndi
看看jndiname怎么控制

调用的getJndiName来自JndiRefDataSourceBase类
JndiRefConnectionPoolDataSource这个类里面有这个成员对象 JndiRefForwardingDataSource
JndiRefForwardingDataSource继承了JndiRefDataSourceBase类


所以直接调用setJndiName就可以控制了
POC
1 | { |
C3P0 hex加载字节码
核心类
- WrapperConnectionPoolDataSource
- WrapperConnectionPoolDataSourceBase.setUserOverridesAsString
- VetoableChangeSupport.fireVetoableChange
- VetoableChangeSupport.fireVetoableChange
- WrapperConnectionPoolDataSource.vetoableChange
- C3P0ImplUtils.parseUserOverridesAsString
- SerializableUtils.fromByteArray( serBytes )
1 | { |
说些什么吧!