Keyi 的日常 


shiro系列漏洞探索


shiro系列漏洞探索

分类

按照利用难度和危害我们依次分为
shiro550 > shiro721 > shiro权限绕过

为什么叫SHIRO-550 呢?
https://issues.apache.org/jira/browse/SHIRO-550

shiro550漏洞分析

shiro-core-1.2.4.jar!/org.apache.shiro.mgt.AbstractRememberMeManager.class#RememberMeManager

AbstractRememberMeManager 继承了RememberMeManager,看到了硬编码的key
-w970

第一个断点打在
org.apache.shiro.mgt.AbstractRememberMeManager.class#getRememberedPrincipals

然后点击step over进行跟踪
当cookie的base64解密后不等于deleteMe的进入循环,把base64.decode后的值赋值给数组类型的decoded

这边的decode是shiro自定义函数
-w561

接着往下走, principals = this.convertBytesToPrincipals(bytes, subjectContext);

点击step into跟进convertBytesToPrincipals, 最后会执行deserialize反序列化输入的字节,至此触发了反序列化漏洞

那shiro自定义的decode函数内部是如何实现的呢?网上说的AES加解密又是怎么回事?
我们这边进上边的this.decrypt函数,getDecryptionCipherKey的值是默认的
-w940

就是private byte[] decryptionCipherKey;也就是我们常见的默认key: kPH+bIxk5D2deZiIxcaaaA==
-w1026

继续进行跟进 cipherService.decrypt
org.apache.shiro.crypto.JcaCipherService.class#ByteSource decrypt
iv 不为空,cbc解密,这样就比较奇怪了,我们生成cookie的时候并没有输入iv偏移量,继续跟踪
-w1009

发现cbc解密的时候iv 为空

函数System.arraycopy(ciphertext, ivByteSize, encrypted, 0, encryptedSize)中iv是
[-5, -30, 5, 65, -34, 25, 34, -11, -102, -125, 98, -56, 73, 25, -120, 114],经过多次测试发现是一个变化的值,那cookie是怎么解密的呢?
在183行System.arraycopy以前,这时候的iv值还是空值
-w1402

第183行中System.arraycopy(ciphertext, 0, iv, 0, ivByteSize);函数进行了iv的赋值。这个iv也就是cookie中的iv变量的值
-w1419

我们对现在ciphertext函数中的iv值
{75, -17, -35, 46, -2, 49, 73, -35, -97, 115, -13, 61, 80, -54, 47, 111 }进行解密得到K��.�1I��s�=P�/o

对生成利用脚本中生成的cookie值K��.�1I��s�=P�/o iv 做比较,发现一致,证明这个iv就是我们攻击payload中cookie的iv值,也就是说这个iv是我们前段可以控制的。
-w904

最后调用
org.apache.shiro.mgt.AbstractRememberMeManager.class#PrincipalCollection deserialize(byte[] serializedIdentity)
触发反序列化(也就是说shiro重写了readObject)
org.apache.shiro.crypto.JcaCipherService.class#DefaultSerializer.class
-w927

其他代码问题

1 shiro是从哪里获取传入的cookie值

1.2.4/shiro-web-1.2.4.jar!/org/apache/shiro/web/mgt/CookieRememberMeManager.class

byte[] decoded = Base64.decode(base64)
cookie 的所有值
-w1282

2 rememberMe中的cookie加密的AES是哪种模式?

org.apache.shiro.web.mgt.CookieRememberMeManager.class#getRememberedSerializedIdentity函数中
String base64 = this.getCookie().readValue(request, response)
-w635

3 断点应该怎么打?直接在触发点T deserialized = ois.readObject()处打,就可以看到所有的利用链了,这也是反序列化漏洞审计的一个通用方法,因为触发点都在readObject()函数

/org/apache/shiro/shiro-core/1.2.4/shiro-core-1.2.4.jar!/org.apache.shiro.io.DefaultSerializer.class#deserialize
-w1350
点击Copy Stack 复制调用链

4 shiro的本身的路由是如何进行的

/org/apache/shiro/shiro-web/1.2.4/shiro-web-1.2.4.jar!/org/apache/shiro/web/servlet/OncePerRequestFilter.class

相关请求

函数调用链

deserialize:77, DefaultSerializer (org.apache.shiro.io)
deserialize:514, AbstractRememberMeManager (org.apache.shiro.mgt)
convertBytesToPrincipals:431, AbstractRememberMeManager (org.apache.shiro.mgt)
getRememberedPrincipals:396, AbstractRememberMeManager (org.apache.shiro.mgt)
getRememberedIdentity:604, DefaultSecurityManager (org.apache.shiro.mgt)
resolvePrincipals:492, DefaultSecurityManager (org.apache.shiro.mgt)

后续

orange 说使用cc3链利用不成功?

Pwn a CTF Platform with Java JRMP Gadget中评论说到
-w886

发现原本的 ObjectInputStream.resolveClass() 是调用的 Class.forName() 来加载类。shiro这边的ObjectInputStream.resolveClass()是调用的loadClass
org.apache.shiro.util.ClassUtils.class#loadClass
-w1005

这边参考voidfoo子航师傅文章了解到:
所以这里就涉及到一个问题,ClassLoader.loadClass() 和 Class.forName() 在加载类时有什么区别?
Class.forName("SomeClass") 会进行类初始化(执行 static 代码块),ClassLoader.loadClass("SomeClass") 则不会
但这个区别不是这里 commons-collections gadget 在 shiro 反序列化漏洞中无法直接利用的原因,这里直接利用会失败的真正原因是 ClassLoader.loadClass() 不支持加载数组类型的类,Class.forName() 才支持

如果目标机器不出网怎么检测key

这边主要是参考了一种另类的 shiro 检测方式文章

不出网利用

1.jrmp延时
2.获取前台js文件在相同下路径写shell
3.内存shell

实战中的一点思考

shiro和fastjson负载问题
因为有些负载主机的shiro.jar 包没有升级,导致可以继续被利用,这种情况下可以多尝试几遍攻击,来尝试流量经过未被加固过的主机
-w747

路径回现

参考

voidfoo大佬wiki文章
一种另类的 shiro 检测方式
漏洞修复
Apache Shiro Java 反序列化漏洞分析






© - 无尾熊 - 2019 - 2021 - Powered by hexo