shiro550(CVE-2016-4437)
1 漏洞说明
Apache Shiro是一个开源安全框架,提供身份验证、授权、密码学和会话管理。在Apache Shiro <= 1.2.4版本中存在反序列化漏洞。
Shiro的“记住我”功能是设置cookie中的rememberMe值来实现。当后端接收到来自未经身份验证的用户的请求时,它将通过执行以下操作来寻找他们记住的身份:
- 检索cookie中RememberMe的值
- Base64解码
- 使用AES解密
- 反序列化
漏洞原因在于第三步,AES加解密的密钥是写死在代码中的,于是我们可以构造RememberMe的值,然后让其反序列化执行。
判断AES秘钥
shiro在1.4.2版本之前, AES的模式为CBC,在1.4.2版本之后为GCM
密钥不正确或类型转换异常时,目标Response包含Set-Cookie:rememberMe=deleteMe字段,
而当密钥正确且没有类型转换异常时,返回包不存在Set-Cookie:rememberMe=deleteMe字段
Shiro框架默认指纹特征:
未登陆的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
登陆失败的话,不管勾选RememberMe字段没有,返回包都会有rememberMe=deleteMe字段
不勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有rememberMe字段
勾选RememberMe字段,登陆成功的话,返回包set-Cookie会有rememberMe=deleteMe字段,还会有rememberMe字段,之后的所有请求中Cookie都会有rememberMe字段
2 漏洞分析
代码下载
1 | git clone https://github.com/apache/shiro.git |
编辑shiro\samples\web的pom.xml中的pom.xml文件:
1 | <dependency> |
首先看下RememberMe值的加密过程。在org.apache.shiro.mgt.AbstractRememberMeManager#onSuccessfulLogin下个断点,点击debug开启tomcat服务
之后在web端登录账户root/secret,勾选上Remember Me的按钮,程序会停在断点处
首先调用forgetIdentity构造方法处理request和response请求,包括在response中加入cookie信息,然后调用rememberIdentity函数,来处理cookie中的rememberme字段。跟进rememberIdentity函数
rememberIdentity函数首先调用getIdentityToRemember函数来获取用户身份,这里也就是”root”,跟进rememberIdentity构造方法
调用convertPrincipalsToBytes方法将accountPrincipals也就是”root”转换为字节形式,跟进函数
转换过程是先序列化用户身份”id”,在对其进行encrypt,跟进encrypt函数
encrypt函数就是调用AES加密对序列化后的”root”进行加密,加密的密钥由getEncryptionCipherKey()得到,跟进getEncryptionCipherKey()函数会发现其值为常量
继续f8,直到回到rememberIdentity函数
跟进rememberSerializedIdentity函数
发现其对其进行base64编码后,设置到cookie中。到这里我们可以梳理下整个过程,当我们勾选上rememberme选项框后,以root身份登录,后端会进行如下操作:
- 序列化用户身份”root”
- 对root进行AES加密,密钥为常量
- base64编码
- 设置到cookie中的rememberme字段
接下来看下rememberme字段的解密过程:
将断点打在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity,然后发送一个带有rememberMe Cookie的请求
跟进getRememberedPrincipals函数
跟进getRememberedSerializedIdentity函数,发现函数提取出cookie并且base64解码
回到getRememberedPrincipals函数,继续跟进到convertBytesToPrincipals函数,发现其对cookie进行AES解密和反序列化
decrypt函数就不贴图了,跟进去很明显就可以看出来其功能。
综上,整个流程为
- 读取cookie中rememberMe值
- base64解码
- AES解密
- 反序列化
其中AES加解密的密钥为常量,于是我们可以手动构造rememberMe值,改造其readObject()方法,让其在反序列化时执行任意操作
3 漏洞利用(直接拿网上的)
1 | from Crypto.Cipher import AES |
能检测到dnslog,就说明命令执行成功