Shrio 550 和 721 漏洞的加密算法分析
Shrio550是大家比较熟悉的一个,因此本文侧重与Shrio721的产生原因 本文只做漏洞分析,未作漏洞复现
在学习Shrio漏洞时,只知道如何使用工具,不知道其中的底层原理,网上的文章对于Shrio721的底层分析又少之又少
https://www.cnblogs.com/Smile3306/p/18984943 作者也是看这个文章才弄明白Shrio的底层原理的,如果看不懂作者的描述,可访问此链接学习
什么是Shrio
Shrio是一个主流的JAVA安全框架,不依赖任何容器,可在JAVASE,JAVAEE项目运行
主要功能为对用户进行身份认证授权会话管理加密等
Shrio核心组件
a. UsernamepasswordToken :封装用户登录信息,并用来创建Token
b. SecurityManager :负责安全认证和授权,Shrio核心部分
c. Subject :Shrio中的一个抽象概念,把当前用户当作主体
d. Realm :自定义模块,一般包含验证和授权的逻辑
e. AuthenticationInfo :用户角色信息集合,认证时使用,有哪些角色
f. AuthorizationInfo :用户权限信息集合,授权时使用,有哪些权限
g. DefaultWebSecurityManager :安全管理器,Realm注入进来才能生效
h. ShrioFilterFactorBean :具体的操作执行,项目入口,数据流中枢
Shrio550 (CVE-2016-4437)
漏洞成因
将反序列化的key硬编码在代码中,并且直接将用户cookie中的iv进行解密,从而导致攻击者可直接利用任意iv和伪造恶意密文
漏洞影响范围
Apache Shrio <=1.2.4
漏洞原理
Shrio默认使用硬编码的AES密钥KeykPH+bIxk5D2deZiIxcaaaA==和IV(16个0x00)进行加密remremberMe Cookie
攻击者可利用默认密钥和任意iv加密序列化数据
服务器利用Cookie中的iv和服务器的Key进行解密
注意:任意iv,虽然服务器加密是利用IV(16个0x00),但是服务器解密是提取Cookie中的IV进行解密
Shrio721 (CVE-2019-12422)
漏洞成因
AES-128-CBC在解密时会先对解密的明文进行验证是否符合PSCK5Padding
通过Padding Oracle Attack得到中间值,然后就可伪造恶意明文,通过与中间值异或得到恶意密文
漏洞影响范围
1.2.5 <=Apache Shrio < 1.4.2
漏洞原理
解密过程中中间值的出现弱化了加密时对于Key的依赖,对于攻击者来说,只需要得到对应的中间值,就相当于可以伪造密文,伪造明文

○ 在服务器视角下,先解出中间值3之后跟密文2异或得到明文3,之后判断明文3的填充规则是否符合PKCS5Padding规则 符合 返回200 不符合 异常
○ 那么就可以根据改变密文,根据服务器响应得出中间值数据,那么就可以改变明文,跟中间值异或得到恶意的明文
Padding Oracle Attack (POA)
爆破密文
密文2 ==CipherText
中间值3 ==TmpKey
公式:0x01= CipherText[16] ⊕ TmpKey[16]
通用公式:0x0n .... 0x0n= CipherText[16-(n-1)] .... CipherText[16] ⊕ TmpKey[16-(n-1)] ....TmpKey[16]
最多需要爆破256次
最后利用0x0n .... 0x0n 和 CipherText进行异或即可得到TmpKey
误判
我们只知道服务器校验是否合法,但有种特殊情况
在爆破TmpKey[16]位时,本来我们想要达到0x01=CipherText[16] ⊕ TmpKey[16]
但恰好发生 0x02 0x02 = CipherText[15] CipherText[15] ⊕ TmpKey[15] TmpKey[16]
这个时候服务器也会返回200
这样的情况有十六种0x0f .... 0x0f
P(0x0n fault)=(1/256)^n =》 P(fault)≈1/(256×255)=1/65280
扩充明文组
以上原理只能控制256(密文分组-1)个字符,对于攻击来说根本不够
我们伪造密文4,无论是否合理,服务器都会对这个分组密文解密,得到中间值4
由于密文4不合理那么中间值4也不合理,但是没关系,我们只需要修改密文3,爆破中间值4就行了
这样我们就从原本的:
IV+C1+C2+C3 (可控值:明文2+明文3 16*2=32字节)
变为(扩容1组)
IV+新C1+新C2+新C3+C4 (可控值:明文2+明文3+明文4 16*3=48字节)
同理(扩容2组)
IV+新C1+新C2+新C3+新C4+C5(可控值:明文2+明文3+明文4+明文5 16*4=64字节)
理论可以扩容到Shrio上限,但每扩容一个分组,增加16256次爆破
举例
假如我们原本登录成功得到一个正确Cookie:
Set-Cookie:rememberMe=Base64(IV+CipherText)
rememberMe=Base64(0x00 ... 0x00 0x01 ... 0x01 0x02 ... 0x02)
通过分组得到:
IV = 0x00 0x00 ... 0x00 C1 = 0x01 0x01 ... 0x01 C2 = 0x02 0x02 ... 0x02
现在按扩容到C4构造:
IV = 0x00 0x00 ... 0x00 C1 = 0x01 0x01 ... 0x01 C2 = 0x02 0x02 ... 0x02
C3 = 0x03 0x03 ... 0x03 C4 = 0x04 0x04 ... 0x04 (C3,C4 写任意值AES都能解密)
将rememberMe=Base64(IV+C1+C2+C3+C4)作为请求头发送给服务器
服务器会先解密C4,假设得到C4的中间值4(攻击者未知):
中间值4 = F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
1、获取中间值
爆破倒数第一位
保持C4不变,首先改变C3的最后一个字节C3[16],比如改为0xDD
令rememberMe=Base64(00 ... 00|01 ... 01|02 ... 02|03 ... DD|04 ... 04)
服务器视角:
判断错误:
让0xDD与中间值4[16]的0xFF异或(攻击者不可视),结果得到的不是0x01,所以返回 “密码不正确、校验不合格”的相关内容(不考虑误报),如状态码500 (攻击者只能看到响应信息)
判断正确:
而当C3[16]遍历到 0xFE 时:
0x01 = 中间值4[16] 异或 0xFE
服务器返回“密码不正确、校验合格”的相关内容,如状态码301,那么就得到:
中间值4[16] = 0x01 异或 0xFE
= 0xFF
2、伪造明文
因为公式:
明文 = C3 异或 中间值4
而中间值4是已知的,所以公式:新C3 = 伪造明文4 异或 中间值4
我们伪造一个明文:helloword
步骤 1:
把“helloword”变成 16 字节明文
明文4 = 68 65 6C 6C 6F 77 6F 72 64 07 07 07 07 07 07 07
步骤 2:
计算新 C3(16 字节)
新C3[i] = 明文4[i] 异或 中间值4[i]
得到(示例值,按位计算即可):
新C3 = 97 93 9F 9F 9A 81 9A 8E 9B F8 F8 F8 F8 F8 F8 F8
3、截断
得到新的C3,现在要进行C3位置的爆破操作,改变C2,
固定新C3,去掉C4
该过程发送的Cookie值去掉C4
原Cookie:
IV + C1 + C2 + C3 + C4
00 ... 00 01 ... 01 02 ... 02 03 ... 03 04 ... 04
现Cookie:
IV + C1 + C2 + 新C3
00 ... 00 01 ... 01 02 ... 02 97 93 9F 9F 9A 81 9A 8E 9B F8 F8 F8 F8 F8 F8 F8
4、重复获取中间值
该阶段固定伪造出C4位置明文的新C3,遍历C2的值,去获取新C3的中间值,从而伪造C3位置的明文