shiro550反序列化漏洞
2022-08-01 01:14:36

# 前言

很久以前的洞
这次来写一下是复习一下之前的笔记
也为了后面的无依赖链和 cc 链以及 TemplatesImpl 做铺垫

# 分析

首先是要知道这个漏洞的大致流程
就是关于 cookie 的加密方式是 AES 这种对称的加密
其中秘钥是写在框架代码中的,也就是硬编码
这就是漏洞产生的原因

首先就是 getRememberedSerializedIdentity

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
protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) {

if (!WebUtils.isHttp(subjectContext)) {
if (log.isDebugEnabled()) {
String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a " +
"servlet request and response in order to retrieve the rememberMe cookie. Returning " +
"immediately and ignoring rememberMe operation.";
log.debug(msg);
}
return null;
}

WebSubjectContext wsc = (WebSubjectContext) subjectContext;
if (isIdentityRemoved(wsc)) {
return null;
}

HttpServletRequest request = WebUtils.getHttpRequest(wsc);
HttpServletResponse response = WebUtils.getHttpResponse(wsc);

String base64 = getCookie().readValue(request, response);
// Browsers do not always remove cookies immediately (SHIRO-183)
// ignore cookies that are scheduled for removal
if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) {
return null;
}

if (base64 != null) {
base64 = ensurePadding(base64);
if (log.isTraceEnabled()) {
log.trace("Acquired Base64 encoded identity [" + base64 + "]");
}
byte[] decoded = Base64.decode(base64);
if (log.isTraceEnabled()) {
log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes.");
}
return decoded;
} else {
//no cookie set - new site visitor?
return null;
}
}

从函数名可以看出这是获取 cookie 并进行处理的类,首先就是获取参数,就是 cookie,然后再 base64 解码,返回结果

发现在 getRememberedPrincipals 处调用了该函数

在这里插入图片描述
并调用了 convertBytesToPrincipals 来进行处理数据

1
2
3
if (bytes != null && bytes.length > 0) {
principals = convertBytesToPrincipals(bytes, subjectContext);
}

再来看下 convertBytesToPrincipals 的功能

1
2
3
4
5
6
protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
if (getCipherService() != null) {
bytes = decrypt(bytes);
}
return deserialize(bytes);
}

很明显是实现了对数据的解密,然后再反序列化

这里先来看下解密函数的实现

先在接口处看下参数
在这里插入图片描述
第一个参数是解密的字段,第二个参数是解密的 key,也就是秘钥
正是我们前面提到的对称的加密方式 AES

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
public ByteSource decrypt(byte[] ciphertext, byte[] key) throws CryptoException {

byte[] encrypted = ciphertext;

//No IV, check if we need to read the IV from the stream:
byte[] iv = null;

if (isGenerateInitializationVectors(false)) {
try {
//We are generating IVs, so the ciphertext argument array is not actually 100% cipher text. Instead, it
//is:
// - the first N bytes is the initialization vector, where N equals the value of the
// 'initializationVectorSize' attribute.
// - the remaining bytes in the method argument (arg.length - N) is the real cipher text.

//So we need to chunk the method argument into its constituent parts to find the IV and then use
//the IV to decrypt the real ciphertext:

int ivSize = getInitializationVectorSize();
int ivByteSize = ivSize / BITS_PER_BYTE;

//now we know how large the iv is, so extract the iv bytes:
iv = new byte[ivByteSize];
System.arraycopy(ciphertext, 0, iv, 0, ivByteSize);

//remaining data is the actual encrypted ciphertext. Isolate it:
int encryptedSize = ciphertext.length - ivByteSize;
encrypted = new byte[encryptedSize];
System.arraycopy(ciphertext, ivByteSize, encrypted, 0, encryptedSize);
} catch (Exception e) {
String msg = "Unable to correctly extract the Initialization Vector or ciphertext.";
throw new CryptoException(msg, e);
}
}

return decrypt(encrypted, key, iv);
}

再来来看下解密的函数

1
2
3
4
5
6
7
8
9
protected byte[] decrypt(byte[] encrypted) {
byte[] serialized = encrypted;
CipherService cipherService = getCipherService();
if (cipherService != null) {
ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
serialized = byteSource.getBytes();
}
return serialized;
}

这里获取了解密的秘钥
一直跟进到后面发现其就是常量并写在代码中

在这里插入图片描述
接着在跟进反序列化

在这里插入图片描述

是通过原生的 readObject 触发反序列化

# 结尾

这里就没有复现了
知识整理一下以前的笔记
可以用 vulhub 直接复现

最后的最后

# 热烈庆祝中国人民解放军成立 95 周年