Shiro550与721的原理分析

什么是Shrio

Shrio是一个主流的JAVA安全框架,不依赖任何容器,可在JAVASEJAVAEE项目运行

主要功能为对用户进行身份认证授权会话管理加密等

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的依赖,对于攻击者来说,只需要得到对应的中间值,就相当于可以伪造密文,伪造明文

image.png

○ 在服务器视角下,先解出中间值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位置的明文

温馨提示:本文内容仅用于合法授权的安全学习与研究交流,严禁用于未授权渗透测试、漏洞利用或任何违法行为。

涉及企业或平台的未公开漏洞信息,请遵循负责任披露原则,勿公开传播可直接复现的敏感细节。

如存在侵权、错误信息或不当内容,请联系站方处理,我们将及时核实并删除。邮箱:admin@baimaojianghu.com。