前言
关于shrio漏洞,网上有很多博文讲解,这些博文对漏洞的解释似乎有一套约定俗成的说辞,让人云里来云里去,都没有对漏洞产生的原因深入地去探究.....
本文从现象到本质,旨在解释清楚Shrio漏洞是怎么回事! 550和721到底有什么区别!两者利用前提到底是什么?(有别于网络上的解释,如有错误还望大佬及时指正)。
shiro框架产品介绍
Apache Shiro是一个强大且易于使用的Java安全框架,用于身份验证、授权、加密和会话管理。它为应用程序提供了诸如用户认证、角色和权限检查、会话管理等安全功能,使开发人员能够轻松地集成安全性需求而不必过多关注实现细节。
Apache Shiro的主要特点包括:
-
身份验证(Authentication):支持多种身份验证机制,包括基于用户名密码的认证、基于LDAP的认证、基于OAuth的认证等。
-
授权(Authorization):提供简单且灵活的授权管理机制,可以基于角色、权限字符串或自定义逻辑进行访问控制。
-
会话管理(Session Management):支持会话管理功能,包括会话存储、会话超时管理等,能够帮助开发人员管理用户的会话状态。
-
加密(Cryptography):提供加密算法和工具类,用于对用户密码和其他敏感数据进行加密处理。
-
Web集成:对Web应用程序提供了方便的集成支持,可以轻松地与各种主流的Java Web框架(如Spring、Struts、JSF等)结合使用。
总的来说,Apache Shiro为Java应用程序提供了一套完整的安全解决方案,使开发人员能够专注于业务逻辑的开发,而无需过多关注安全细节的实现。
shiro550漏洞原理
(1)shiro在登录处提供了Remember Me这个功能,来记录用户登录的凭证,然后shiro会对用户传入的cookie进行解密并进行反序列化,服务端接收rememberMe的cookie值后的操作是:Cookie中rememberMe字段内容---> Base64解码---> 使用密钥进行AES-CBC解密---> 反序列化
(2)由于该版本AES加密的密钥Key被硬编码在代码里(漏洞能够被利用的本质),且大部分项目未修改默认AES密钥,这意味着攻击者只要通过源代码找到AES加密的密钥,就可以构造一个恶意对象,对其进行序列化,AES加密,Base64编码,然后将其作为cookie的Remember Me字段值发送,Shiro将数据进行解密并且反序列化,最终触发反序列化漏洞。
(3)处理Cookie的类是CookieRememberMeManaer,该类继承AbstractRememberMeManager类,跟AbstractRememberMeManager类,很容易看到AES的key。org.apache.shiro.mgt.AbstractRememberMeManager ——kPH+bIxk5D2deZiIxcaaaA==
shiro框架验证
1.未登录的情况下,请求包的cookie中没有rememberMe字段,返回包set-Cookie里也没有deleteMe字段
2、登录失败的话,不管有没有勾选RememberMe字段,返回包都会有 rememberMe= deleteMe 字段
3.不勾选RememberMe,登录成功的话,返回包set-Cookie里有rememberMe=deleteMe字段。但是之后的所有请求中Cookie都不会有RememberMe字段
4.勾选RememberMe,登录成功的话,返回包set-Cookie里有rememberMe=deleteMe字段,还会有remember 字段,之后的所有请求中Cookie都会有rememberMe字段
5.可以在cookie后面自己加一个rememberMe=1,看返回包有没有rememberMe= deleteMe
shiro550漏洞复现
shiro550利用条件
0.Shiro <= 1.2.4中
1.aes加密的key
2.目标服务器含有可利用的攻击链。
工具准备
这是一款专门针对shiro漏洞利用的工具,写的确实不错。直接拿过来一把梭哈
靶场搭建
查看web界面
shrio框架验证
开始漏洞攻击,输入shiro的默认密钥,检测当前利用链
命令执行
shiro720漏洞原理
在Shiro550漏洞中,Cookie所使用的AES加密密钥为硬编码,所以我们可以构造恶意序列化数据并使用固定的AES密钥进行正确加密恶意序列发送给服务端,进而达到攻击的目的。但在该漏洞公布后,Shiro官方修复了这一漏洞,将AES密钥修改成了动态生成。也就是说,对于每一个Cookie,都是使用不同的密钥进行加解密的。
public AbstractRememberMeManager() { this.serializer = new DefaultSerializer<PrincipalCollection>(); AesCipherService cipherService = new AesCipherService(); this.cipherService = cipherService; setCipherKey(cipherService.generateNewKey().getEncoded()); }
具体的密钥产生调试参考
Java反序列化漏洞——Shiro721 - 枫のBlog
CBC字节翻转攻击&Padding Oracle Attack原理
简单介绍下这个加密过程
CBC字节翻转攻击
CBC要求第一个分组的明文在加密运算前先与IV进行异或;从第二组开始,所有的明文先与前一分组加密后的密文进行异或。
解密过程刚好相反,第一组密文在解密之后与初始向量IV异或得到第一组明文。第二组密文解密之后和第一组密文异或得到第二组明文
为什么使用要使用异或呢! 异或加密是一种简单的加密方法,它基于异或运算(XOR)的性质。相同为0,不同为1。例如0异或1为1,1异或1为0。这就造就了异或运算的一个特性:任何数和0异或结果仍然是原来的数,任何数和自身异或结果是0。这意味着如果你对一个字符串进行异或加密,然后再对结果使用相同的密钥进行异或,你将得到原始字符串。
这说的还是有点抽象,我们还是举个例子。假如我们 有明文01101011 有IV11001001 两者进行异或结果为10100010 拿这个结果在于IV进行异或 10100010异或11001001=01101011,你会发现明文又回来了。事实上异或加密在软件中广泛使用。
我们把目光返回CBC字节翻转攻击
参考文章CBC字节翻转攻击&Padding Oracle Attack原理解析 - 枫のBlog
假如A(可控的)异或B(不可控的)得C(结果) 那么A异或C得B, 如此假如我们有想要的明文X ,A异或C异或B异或X,这个结果是不是就是X了,这样的话就可以来控制输出明文为自己想要的内容
那么现在的问题是对于结果C我们怎么得知?这就需要了解一下padding oracle攻击了
Padding Oracle Attack原理
在介绍Padding Oracle Attack原理前,我们先了解下如下的概念,
我们知道在分组加密算法中有填充字节的概念,需要填充的比较参考值是BlockSize(一组的字节数,不够需要进行填充)
举个例子 假如我们想加密BRIAN;12;1;0x050x050x050x050x05,这里设置的初始向量iv为0x7B 0x21 0x6A 0x63 0x49 0x51 0x17 0x0F
BlockSize=8
加密过程
BRIAN;12
此为第一组 ;1;
此为第二组 由于不够BlockSize我们需要填充5个0x05(8-3=5)
这时服务器发送的Cookie应该为7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
,初始向量iv被填充在加密密文之前。
解密过程
最后解得明文,判断最后一个字节。这里是0x05,哪么是5个0x05吗?如果是则pading成功去除填充字节就是解密明文,如果不是则padding非法,等待系统处理。(如果padding失败的信息返回给了用户,就达成了攻击的必要条件)
现在思考 我们有密文块可控,知道BlockSize的大小,知道最后的padding是否成功,能不能把明文猜出来?
明文;1;0x050x050x050x050x05是 0xF8 0x51 0xD6 0xCC 0x60 0xFC 0x95 0x37异或0xC3 0x60 0xED 0xC9 0x6D 0xF9 0x90 0x32得到的 其中我们可以控制 0xF8 0x51 0xD6 0xCC 0x60 0xFC 0x95 0x37 0xC3 0x60 0xED 0xC9 0x6D 0xF9 0x90 0x32由des加密生成不可控,当然如果得知了这一块,明文也就可以算出来了
请看下表
密 | 密 | 密 | 密 |
---|---|---|---|
⊕ | ⊕ | ⊕ | ⊕ |
00 | 00 | 00 | [枚举] |
0x1 |
我们可以不断改变异或值得最后一位字节,直到padding成功。如果成功有两种情况,1是刚好是0x1,这时不管前面的密文如何都可以padding成功,2是瞎猫碰到死耗子和最后的前几位密文刚刚好pading成功,我们只需你是0X01的情况,修改最后前几位的密文padding失败即可排除情况2。这时我们可以得到第一个密文的值:0x1⊕刚刚爆出来的异或值=密文的明文
密 | 密 | 密 | 明 |
---|---|---|---|
⊕ | ⊕ | ⊕ | ⊕ |
00 | 00 | [枚举] | 固定异或值 |
0x02 | 0x2 |
接下来假设padding值为0x2(成功padding是要有两个0x02的) 固定最后一个字节异或值 =明文⊕ 0x2,对倒数第二个字节进行枚举。直到padding成功 成功后是不是就可以把响应的密文解出来了,0x2⊕刚刚爆出来的异或值=密文的明文
密 | 密 | 明 | 明 |
---|---|---|---|
⊕ | ⊕ | ⊕ | ⊕ |
00 | [枚举] | 固定异或值 | 固定异或值 |
0x03 | 0x03 | 0x03 |
继续重复上一步的操作,设paddding为0x03对倒数第3个字节进行枚举,直到padding成功,得到相应位置的明文..............
最后的结果我们可以把最后的明文信息爆破出来,结合字节翻转攻击就可以把明文修改为我们想要的明文了。最终会进行序列化处理,那么我们需利用一个条反向序列化链就可以造成代码执行命令执行的漏洞了。
参考Padding Oracle 攻击_哔哩哔哩_bilibili
Shiro721利用条件
0.Apache Shiro < 1.4.2
1.已经登陆用户的合法cookie ·加密的初始向量iv以及密文
2.Padding正确,反序列化错误处理(Padding Oracle Attack) ·Padding正确,服务器正常响应 ·Padding错误,服务器返回Set-Cookie: rememberMe=deleteMe
3.目标服务器含有可利用的攻击链 ·构造恶意类
漏洞利用缺陷,类似于sql盲注需要发送大量http请求,非常容易触发防火墙或waf。流量过大的可能会把服务器打崩。
漏洞复现
Docker搭建环境
Copy Highlighter-hljsgit clone https://github.com/3ndz/Shiro-721.git cd Shiro-721/Docker docker build -t shiro-721 . //不要忽略721后面的那个点,意为当前路径 docker run -p 8080:8080 -d shiro-721
打开web页面
使用现有的密码登录一下,记得把Remember Me打上勾,
现在我们已经有一个合法的cookie了
启动漏洞利用工具,设置下代理为burp。分析下攻击流量
启用dnslog回显攻击,burp查看攻击流量,分析流量特征
特征1
特征2
根据流量我们可以发现这样的特征,
1.流量很大 需要不断的在cookie中做填充字节尝试 2.可能被重定页面 3.在填充测试中会有两个不同的结果 成功没有deleteme 失败则有deleteme
此时的工具攻击页面 不断改变 Calulating block 的值进行填充测试。
攻击的过程十分漫长,条件利用比较苛刻,不能高并发,容易被检测,也容易把服务器打崩。
参考文章
https://www.cnblogs.com/qianxinggz/p/13388405.htmlhttps://www.cnblogs.com/qianxinggz/p/13388405.html
Padding Oracle 攻击_哔哩哔哩_bilibiliPadding Oracle 攻击, 视频播放量 488、弹幕量 0、点赞数 25、投硬币枚数 12、收藏人数 19、转发人数 4, 视频作者 jiegec, 作者简介 ,相关视频:LLL 格基归约算法,ECDSA 椭圆曲线数字签名算法,Shamir 秘密共享算法,Coppersmith 方法,普通人千万别学网络工程师,这里面的水真的太深了!!!,【密码学】PLONK协议,23级第一次例会 - Crypto入门,【加法的几何表示】两个点如何相加?画两条直线就够了,【极致低阻】航模XT60大电流电流表,没事干了就堆点料吧,月入过万?5个建议!零基础转行新媒体运营,劝你谨慎入坑!https://www.bilibili.com/video/BV1au4y1m7KQ/?spm_id_from=333.337.search-card.all.click&vd_source=4e747ff54c26459caabbedaa87d1c684
CBC字节翻转攻击&Padding Oracle Attack原理解析 - 枫のBlogCBC字节翻转攻击 概述 CBC字节翻转攻击是一种对分组密码的攻击,属于侧信道攻击。这种攻击针对的是CBC M…https://goodapple.top/archives/217