很久以前的洞
这次来写一下是复习一下之前的笔记
也为了后面的无依赖链和cc链以及TemplatesImpl
做铺垫
首先是要知道这个漏洞的大致流程
就是关于cookie的加密方式是AES这种对称的加密
其中秘钥是写在框架代码中的,也就是硬编码
这就是漏洞产生的原因
首先就是getRememberedSerializedIdentity
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
来进行处理数据
if (bytes != null && bytes.length > 0) {
principals = convertBytesToPrincipals(bytes, subjectContext);
}
再来看下convertBytesToPrincipals的功能
protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
if (getCipherService() != null) {
bytes = decrypt(bytes);
}
return deserialize(bytes);
}
很明显是实现了对数据的解密,然后再反序列化
这里先来看下解密函数的实现
先在接口处看下参数
第一个参数是解密的字段,第二个参数是解密的key,也就是秘钥
正是我们前面提到的对称的加密方式AES
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);
}
再来来看下解密的函数
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直接复现