点击“关注”了解更多信息

关于Apache Shiro反序列化

在shiro≤1.2.4版本,默认使⽤了CookieRememberMeManager,由于AES使用的key泄露,导致反序列化的cookie可控,从而引发反序列化攻击。(理论上只要AES加密钥泄露,都会导致反序列化漏洞)利用的两个关键条件是key和可用gadget。1.2.4版本默认key为kPH+bIxk5D2deZiIxcaaaA==,当然也可以通过下面的方式自定义key:

private static final String ENCRYPTION_KEY = "3AvVhmFLUs0KTA3Kprsdag==";public CookieRememberMeManager rememberMeManager() {        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();        cookieRememberMeManager.setCookie(rememMeCookie());        // remeberMe cookie 加密的密钥 各个项目不一样 默认AES算法 密钥长度(128 256 512)        cookieRememberMeManager.setCipherKey(Base64.decode(ENCRYPTION_KEY));        return cookieRememberMeManager;    }

下面结合实战以及shiro的CookieRememberMeManaer的调用过程,浅谈获取Key的几种方式。

Shiro key的获取方式

1

结合Dnslog与URLDNS

在进行漏洞探测的时候,一般会使用ysoserial-URLDNS-gadget结合dnslog进⾏检测,其不受JDK版本和相关的安全策略影响, 除非存在网络限制DNS不能出网

通过判断dnslog是否收到对应的请求,判断漏洞是否存在。这是获取key比较实用方法,通过在dnslog域名前加⼊对应key的randomNum,结合对应的dnslog记录,即可获取到应用对应的Shiro key了

例如下图通过结合Dnslog与URLDNS成果枚举出当前应用的key为kPH+bIxk5D2deZiIxcaaaA==:

2

利用时间延迟或报错

结合Dnslog与URLDNS方法有一个前提是DNS能出网。那么在不出网的情况下就需要找一个替代的方案了。结合SQL盲注的思路,可以考虑执行如下代码结合时间延迟进行判断,若系统是linux系统,则睡眠10s:

try{   if(!(System.getProperty("os.name").toLowerCase().contains("win"))){     Thread.currentThread().sleep(10000L);     }   } catch(Exception e){}

同理,可以考虑结合触发Java异常进⾏判断,若系统返回对应的报错系统,或者返回通用的报错提示,说明当前的key和gadget组合是成功的:

String result = "shiro-Vul-Discover";throw new NoClassDefFoundError(new String(result));

上述的思路是通过执行相关的恶意代码来进行判断的,那么就需要在有相关的gadget的前提下才能进行key的枚举了。
例如下面的案例,使用CommonsBeanutils1结合时间延迟的方式成功枚举出当前key为4AvVhmFLUs0KTA3Kprsdag==:

同理,也可以使用报错的方式进行key的枚举:

这种方法的话存在一个比较棘手的点:枚举的次数多,耗时长。因为要结合可用gadget执行相关代码进行判断,那么假设字典的key个数为100个,那么枚举的次数就是gadget与key的笛卡尔积(10个gadget就耀枚举1000次),以下是一些常用的gadget:

URLDNSCommonsBeanutils1CommonsCollections*JRMPClientJRMPListenerC3P0Spring1......

自动化不稳定:例如部分场景报错时会统一返回登陆页面,在实际利用中很多情况下也仅仅是在登陆页面的接口进行检测,那么就可能会出现漏报误报的情况。所以在DNS不出网的情况下,这种方式比较繁琐。

3

结合CookieRememberMeManaer

shiro提供了记住我(RememberMe)的功能,关闭了浏览器下次再打开时还是能保存身份信息,使得无需再登录即可访问。
在登陆成功时,如果启用了RememberMe功能,shiro会在CookieRememberMeManaer类中将cookie中rememberMe字段内容进行序列化、AES加密、Base64编码操作。然后保存在cookie中。在关闭浏览器后,重新访问对应的业务接口,此时就是反过来的操作,解码,解密,然后序列化。最后获取到当前用户的身份信息。
简单看看具体的代码实现,看看能不能找到相关的思路来解决枚举key的问题。在获取到rememberMe后,会调用getRememberedPrincipals方法解密反序列化,得到用户凭证组信息:

protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContext)  {    RememberMeManager rmm = getRememberMeManager();    if (rmm != null) {      try      {        return rmm.getRememberedPrincipals(subjectContext);      }  ......}

getRememberedPrincipals的具体实现:

public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext)  {    PrincipalCollection principals = null;    try    {      byte[] bytes = getRememberedSerializedIdentity(subjectContext);      if ((bytes != null) && (bytes.length > 0)) {        principals = convertBytesToPrincipals(bytes, subjectContext);      }    }    catch (RuntimeException re)    {      principals = onRememberedPrincipalFailure(re, subjectContext);    }    return principals;  }

在getRememberedSerializedIdentity方法里主要是对cookie里的相关内容进行base64解码,然后调用convertBytesToPrincipals方法进行解密操作:

protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext)  {    if (getCipherService() != null) {      bytes = decrypt(bytes);    }    return deserialize(bytes);  }


解密后就是对应的反序列化以及生成对应的用户凭证组的信息了。在调用上述方式时,如果抛出异常,则会调用onRememberedPrincipalFailure方法:

principals = onRememberedPrincipalFailure(re, subjectContext);

查看onRememberedPrincipalFailure的具体实现:

protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context){    if (log.isDebugEnabled()) {      log.debug("There was a failure while trying to retrieve remembered principals.  This could be due to a configuration problem or corrupted principals.  This could also be due to a recently changed encryption key.  The remembered identity will be forgotten and not used for this request.", e);    }    forgetIdentity(context);    throw e;  }

里面调用的是CookieRememberMeManager

类的forgetIdentity方法:

public void forgetIdentity(SubjectContext subjectContext)  {    if (WebUtils.isHttp(subjectContext))    {      HttpServletRequest request = WebUtils.getHttpRequest(subjectContext);      HttpServletResponse response = WebUtils.getHttpResponse(subjectContext);      forgetIdentity(request, response);    }  }  private void forgetIdentity(HttpServletRequest request, HttpServletResponse response)  {    getCookie().removeFrom(request, response);  }

然后调用removeFrom方法,这里具体是设置对应的

responseheader,也就是常见的

rememberMe=deleteMe:

public void removeFrom(HttpServletRequest request, HttpServletResponse response)  {    String name = getName();    String value = "deleteMe";    String comment = null;    String domain = getDomain();    String path = calculatePath(request);    int maxAge = 0;    int version = getVersion();    boolean secure = isSecure();    boolean httpOnly = false;    addCookieHeader(response, name, value, comment, domain, path, maxAge, version, secure, httpOnly);    log.trace("Removed '{}' cookie by setting maxAge=0", name);  }

结合前面的简单分析,可以知道,当从cookie中获取到rememberMe字段时,通过一系列的解码解密反序列化,成功话的会则得到用户凭证组信息。否则在response中返回Set-Cookie:rememberMe=deleteMe。
也就说,可以尝试构造好一个包含用户凭证组信息的伪造rememberMe的值,然后经过AES加密后进行请求。经过一系列的解码解密操作后,若此时返回包不返回Set-Cookie: rememberMe=deleteMe,说明当前的key是正确的。可以以此作为判断标准,使用不同密钥对这串序列化数据进行加密并发包,即可快速爆破获取到Shiro加密密钥。
这里做一个实验验证下以上的猜想,登录写好的环境http://localhost:8080/login,勾选rememberMe,登录成功后,看到一个key为rememberMecookie:

NMhQ5j+uiYfUA+gQF93wGknW88ru39LFDKiOmaAuphx7h+r/XUhlebml7+KNwfF0gIIOnJg6LA8xVpzPJTYknq/aYPeeDNJEVYX8DSUMNUh0nbCdHW1YNuFDdBNg6chk5nEZwkh7dG9k+uAnZEfpFbRTajQ4vEolbOktGAS+feNmpurL2P/0dpWwzsSGMZubiVs0ICMVt6CS3qvU8rKC22lbPILSqTiD5Ao+6YNCm19qm/6uQ7De2E+gmKmxGA9o/EsaRUE71wdiHdJbaDeNOQ5am8rXiejqtfEl5YHzeU2MEdxqo+POVUgaSal7O3FYhLjfn4U1nS97/VUHfY7mlz3iP9rU4KvIYjtB5RhbNwkgoFmtUY6MFyFaJNoOAwKBfkeVY0w7QoF7zo0P1HEA3G1XEBR7GeC4O/XAChMnDx7NYfm5D5RZuWWNkW8qI0U9n5UJXmpVsS1hB3vor0eB/5gO5USMy+ToHAW3bOB6REK1x3/U9IS82sY/aLv7aXBA

通过一系列的解密,上面的rememberMe解密后的序列化内容base64编码如下:

rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBzcgAXamF2YS51dGlsLkxpbmtlZEhhc2hNYXA0wE5cEGzA+wIAAVoAC2FjY2Vzc09yZGVyeHIAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAADHcIAAAAEAAAAAF0AAhpbmlSZWFsbXNyABdqYXZhLnV0aWwuTGlua2VkSGFzaFNldNhs11qV3SoeAgAAeHIAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAED9AAAAAAAABdAAEcm9vdHh4AHcBAXEAfgAFeBAQEBAQEBAQEBAQEBAQEBA=

可以看到里面包含了shiro的用户凭证组的信息:

得到该内容后即可结合key字典进行AES加密,然后进行base64编码,作为rememberMe的值在请求中进行提交,然后根据respnose是否返回deleteMe判断key是否正确。

结合实际场景测试,当key正确时,response的header没有相关关键字:

当key不正确时,返回包返回

Set-Cookie: rememberMe=deleteMe:

综上,测试环境的shiro key为
4AvVhmFLUs0KTA3Kprsdag==。
相比前两种方式,该方式不受网络限制的影响,并且结合并发,效率上也有一定的保证。那么就可以先枚举出对应的key,然后结合实际情况,结合对应的gadget进行深入的漏洞检测/利用了。扫码关注我们在这里,探索技术与热爱点分享点点赞点在看

aes key长度_原创 | 浅谈Shiro反序列化获取Key的几种方式相关推荐

  1. .net mysql和php mysql数据库连接_浅谈PHP连接MySQL数据库的三种方式

    本篇文章给大家介绍一下PHP连接MySQL数据库的三种方式(mysql.mysqli.pdo),结合实例形式分析了PHP基于mysql.mysqli.pdo三种方式连接MySQL数据库的相关操作技巧与 ...

  2. sap 双计量单位_[原创]浅谈MM模块的双计量单位(二)

    [原创]浅谈MM模块的双计量单位(二) 本博文为隐身人原创作品,请勿转载. 我们继续按照以下的步骤来执行双计量单位的有关流程. 第一步:切换到批次类的"特性"视图,将我们刚才创建的 ...

  3. tomcat temp 大量 upload 文件_原创 | 浅谈URI中的任意文件下载

    点击上方蓝字 关注我吧引言 文件下载是比较常见的业务.常见的接口格式为/download?fileName=xxx.png,整个过程若没过滤目录穿越符号-/或者未对下载的路径进行处理限制.当传入的fi ...

  4. java 循环依赖_浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  5. 《浅谈F5健康检查常用的几种方式》—那些你应该知道的知识(二)

    负载均衡作为实现应用高可用和高可靠的一种方式,已成为目前数据中心内不可或缺的一个环节,并扮演着越来越重要的作用,而F5正是这一领域的佼佼者.要实现应用的高可用,如何探测负载均衡后端应用的可用性是其中非 ...

  6. 业务异常 java_浅谈RxJava处理业务异常的几种方式

    本文介绍了RxJava处理业务异常的几种方式,分享给大家.具体如下: 关于异常 Java的异常可以分为两种:运行时异常和检查性异常. 运行时异常: RuntimeException类及其子类都被称为运 ...

  7. rails 调用php函数_潜藏在PHP安全的边缘——浅谈PHP反序列化漏洞

    潜藏在PHP安全的边缘--浅谈PHP反序列化漏洞 注意事项:1.本篇文章由复眼小组的瞳话原创,未经允许禁止转载2.本文一共1376字,8张图,预计阅读时间6分钟3.本文比较基础,请大佬酌情观看,如果有 ...

  8. [原创]浅谈对华为34岁以上员工“退休”

    [原创]浅谈对华为34岁以上员工"退休" 今年过年后,网上对华为34岁以上员工"退休"的文章争议很大,我想这个可能是每个做It的人都不可避免要的问题,我对这个事 ...

  9. [原创]浅谈持续集成在测试中的应用

    [原创]浅谈持续集成在测试中的应用 今天抽空理了下思路,来谈谈持续集成在测试中的应用,关于持续集成的介绍,可以参见我之前写的 浅谈我对持续集成的理解. 闲话少说,简单先介绍下,持续集成在测试中应用的范 ...

最新文章

  1. 利用非递归方法实现二叉树的中序遍历
  2. python web 框架例子_最快的 Python Web 框架入门
  3. 今天是个特殊的一天,有意义的一天,值得纪念的一天~
  4. 33. 高精度练习之乘法
  5. squid是否支持http1.1和对KeepAlive支持的个人理解
  6. Orabbix监控oracle
  7. Java中Arrays类的两个方法:deepEquals和equals
  8. flacs 安装教程_绍兴拖拽式撬装加油装置建站方案
  9. 刺客信条3重制版修改器|刺客信条3重制版十项修改器风灵月影版下载
  10. 在哪能查到英文论文?
  11. C#winform软件长时间运行后无响应问题解决
  12. 斯蒂文斯理工学院计算机科学硕士,斯蒂文斯理工学院计算机科学硕士
  13. 服务器怎么向指定客户端发送信息,WebSocket 如何实现服务端向客户端发送消息?...
  14. 图灵机器人微信自动聊天功能
  15. sip协议呼叫流程详解
  16. 光学成像系统中的像差
  17. R与结构方程模型(2):潜变量
  18. 震惊!我竟然在1080Ti上加载了一个35亿参数的模型(ZeRO, Zero Redundancy Optimizer)
  19. 什么是 D2C ( Direct To Consumer ) ?
  20. java文件解析器_jvm:java类文件结构(字节码文件的解析)

热门文章

  1. 【高并发】面试官问我:为啥局部变量是线程安全的?
  2. 月薪30K+的程序员都会啥,通过3000字告诉你……
  3. 大厂面试官必问的Mysql锁机制
  4. 提升职场价值,把握成长方向
  5. 【系统缓慢、CPU 100%、频繁Full GC问题】的定位排查思路!
  6. 如何优雅的设计一个告警系统?远没有你想的那么简单!
  7. HTTP协议与TCP/IP协议的关系
  8. 结构体类型、联合体类型
  9. 重磅官宣:评职称将不做论文数量硬性要求!职称改革任务总体完成
  10. CVPR 2021 | 针对全局 SfM 的高效初始位姿图生成