【Java代码审计】S2-045 远程代码执行漏洞分析复现

【Java代码审计】S2-045 远程代码执行漏洞分析复现

  • 1.漏洞原理
  • 2.靶场复现

1.漏洞原理

官方对该漏洞的描述是这样的:Struts2 默认处理 multipart 上传报文的解析器为 Jakarta 插件(org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest类),但是 Jakarta 插件在处理文件上传(multipart)的请求时会捕捉异常信息,并对异常信息进行 OGNL 表达式处理。当 Content-Type 错误时会抛出异常并附带上 Content-Type 属性值,这里可通过构造附带 OGNL 表达式的请求导致远程代码执行漏洞

根据官方通告,该漏洞的影响范围为:Struts 2.3.5~Struts 2.3.31Struts 2.5~Struts 2.5.10

通过 diff 补丁信息我们可以看到,程序主要针对 Struts2 的 FileUploadInterceptor, 也就是对处理文件上传的拦截器进行了修改删除并重载了findText 函数:

在这里插入图片描述

通过跟进 LocalizedTextUtil.findText,可以发现以下关键代码:

/**
 * 在给定的类中查找文本,并返回本地化后的消息字符串。
 *
 * @param aClass 给定的类,用于定位资源文件
 * @param aTextName 要查找的文本名称或键
 * @param locale 本地化信息,用于确定要使用的语言环境
 * @param defaultMessage 默认消息,如果未找到指定的文本,则返回此消息
 * @param args 要格式化到消息中的参数
 * @return 本地化后的消息字符串
 */
public static String findText(Class aClass, String aTextName, Locale locale, String defaultMessage, Object[] args) {
    // 获取 ActionContext 中的值栈
    ValueStack valueStack = ActionContext.getContext().getValueStack();
    // 调用重载方法,传递给定的值栈
    return findText(aClass, aTextName, locale, defaultMessage, args, valueStack);
}

通过 findText 我们可以看到, ActionContext.getContext().getValueStack() 获得了valueStack 的值。继续跟进 getValueStack 发现,注释中明确表示了这里将返回 OGNL,也就是说这里应该与我们的 OGNL 执行息息相关:

/**
 * 获取 OGNL 值栈。
 *
 * @return OGNL 值栈。
 */
public ValueStack getValueStack() {
    return (ValueStack) get(VALUE_STACK);
}

接下来继续跟进 findText()方法,这里 findText()方法的代码较长,我们只贴出重要部分以便于阅读。上一步我们已知 valueStack 和 OGNL 相关,因此这里也以其为主线跟进,直接搜索关键字定位到关键代码段:

在这里插入图片描述

可以看到 findMessage 会使用 valueStack,继续跟进 findMessage 方法。可以看到无论如何判断最终都会调用 getMessage 方法:

在这里插入图片描述

getMessage 的代码较少,跟进可以发现其通过 buildMessageFormat()方法来对消息进行格式化,被格式化的消息则由 TextParseUtil.translateVariables()来生成

同样,我们可以发现 getDefaultMessage() 方法也调用了 valueStack , 并且与getMessage()一样都存在 buildMessageFormat 方法用来对消息进行格式化,且格式化的消息都是由 TextParseUtil.translateVariables()进行生成的

在这里插入图片描述

值得注意的是,getMessage 方法是需要一个 bundleName 参数的,在上层函数中我们可以知道该参数是由 aClass 赋值的,在整个触发流程中 aClass 是一个 File 异常类,并且这个类在 Collections 中是找不到的。也就是说,在所有的执行流程中 getMessage 和findMessage 都将返回 null。那么,整个流程中也就只有 getDefaultMessage 能够被触发

在这里插入图片描述

接着我们可以继续跟进 TextParseUtil.translateVariables,看一下被格式化的消息是怎么生成的:

在这里插入图片描述

/**
 * 将表达式中的变量转换为相应的值,并返回结果。
 *
 * @param openChars 表达式中的变量开放字符数组
 * @param expression 要解析的表达式字符串
 * @param stack 值栈对象,用于查找变量对应的值
 * @param asType 期望的结果类型
 * @param evaluator 自定义的值解析器,用于对查找到的值进行进一步处理
 * @param maxLoopCount 解析循环的最大次数,以避免无限循环
 * @return 解析后的表达式结果对象
 */
public static Object translateVariables(char[] openChars, String expression, final ValueStack stack, final Class asType, final ParsedValueEvaluator evaluator, int maxLoopCount) {
    // 创建一个 ParsedValueEvaluator 对象,用于评估解析后的值
    ParsedValueEvaluator ognlEval = new ParsedValueEvaluator() {
        public Object evaluate(String parsedValue) {
            // 在值栈中查找变量对应的值
            Object o = stack.findValue(parsedValue, asType);
            // 如果自定义的值解析器存在且找到的值不为null,则对其进行进一步处理
            if (evaluator != null && o != null) {
                o = evaluator.evaluate(o.toString());
            }
            return o;
        }
    };
    // 获取 TextParser 对象,用于解析表达式中的变量
    TextParser parser = ((Container) stack.getContext().get(ActionContext.CONTAINER)).getInstance(TextParser.class);
    // 使用 TextParser 对象解析表达式并返回结果
    return parser.evaluate(openChars, expression, ognlEval, maxLoopCount);
}

可以看到这里构造并执行了 OGNL 表达式,也就是说,我们的有效载荷将在这里被执行。那么, 我们该如何去触发呢?根据开始的 diff 信息,我们知道:只要触发FileUploadInterceptor.java 下 intercept 的错误流程,并且 validation 的值不为空即可触发该漏洞。因此我们的方向十分明确:首先找到在哪里调用了 FileUploadInterceptor.java 下的intercept 方法

通过上面的判断可以知道,只有在产生异常时才会调用,我们便可以直接通过搜索异常状态的处理方法来进行定位,如搜索:FileUploadBase.SizeLimitExceededExceptione

/**
 * 解析上传的文件并保存到指定目录。
 *
 * @param request HTTP 请求对象
 * @param saveDir 文件保存目录路径
 * @throws IOException 如果在解析或保存文件时发生 IO 异常
 */
public void parse(HttpServletRequest request, String saveDir) throws IOException {
    try {
        // 设置请求的本地化信息
        setLocale(request);
        // 处理上传的文件
        processUpload(request, saveDir);
    } catch (FileUploadException e) {
        // 捕获文件上传异常
        LOG.warn("Request exceeded size limit!", e);
        LocalizedMessage errorMessage;
        // 根据不同的文件上传异常类型构建相应的错误消息
        if (e instanceof FileUploadBase.SizeLimitExceededException) {
            FileUploadBase.SizeLimitExceededException ex = (FileUploadBase.SizeLimitExceededException) e;
            errorMessage = buildErrorMessage(e, new Object[]{ex.getPermittedSize(), ex.getActualSize()});
        } else {
            errorMessage = buildErrorMessage(e, new Object[]{});
        }
        // 将错误消息添加到错误列表中(如果不重复)
        if (!errors.contains(errorMessage)) {
            errors.add(errorMessage);
        }
    } catch (Exception e) {
        // 捕获其他异常
        LOG.warn("Unable to parse request", e);
        LocalizedMessage errorMessage = buildErrorMessage(e, new Object[]{});
        // 将错误消息添加到错误列表中(如果不重复)
        if (!errors.contains(errorMessage)) {
            errors.add(errorMessage);
        }
    }
}

根据以上代码的逻辑,我们跟进 processUpload()方法:

在这里插入图片描述

我们继续跟 进 createRequestContext ,可以看到其返回了一个实例化的RequestContext,并且拥有以下四种内置方法:getCharacterEncoding、getContentType、getContentLength、getInputStream

/**
 * 创建一个请求上下文对象,用于包装 HttpServletRequest 对象的请求信息。
 *
 * @param req HTTP 请求对象
 * @return 请求上下文对象
 */
protected RequestContext createRequestContext(final HttpServletRequest req) {
    return new RequestContext() {
        /**
         * 获取请求的字符编码。
         *
         * @return 请求的字符编码
         */
        public String getCharacterEncoding() {
            return req.getCharacterEncoding();
        }

        /**
         * 获取请求的内容类型。
         *
         * @return 请求的内容类型
         */
        public String getContentType() {
            return req.getContentType();
        }

        /**
         * 获取请求的内容长度。
         *
         * @return 请求的内容长度
         */
        public int getContentLength() {
            return req.getContentLength();
        }

        /**
         * 获取请求的输入流。
         *
         * @return 请求的输入流
         * @throws IOException 如果获取输入流失败
         */
        public InputStream getInputStream() throws IOException {
            InputStream in = req.getInputStream();
            if (in == null) {
                throw new IOException("Missing content in the request");
            }
            return req.getInputStream();
        }
    };
}

继续跟进 commons-fileupload 中的 parseRequest

在这里插入图片描述

继续跟进getItemIterator

在这里插入图片描述

跟进 FileItemIteratorImpl 可以发现程序首先调用了 RequestContext 实例的 getContentType()方法来返回请求的 ContentType 字段,然后校验 ContentType 是否为空或是否以 multipart 开头。若判断条件成立,则抛出一个错误,并将错误的 ContentType 加到报错信息中

在这里插入图片描述

这里的 InvalidContentTypeException 类是继承于 FileUploadException 的,也就是说,会抛出一个 FileUploadException 的错误

那么,parse又是在哪里被调用的呢?我们继续往上看可以发现,在MultiPartRequestWrapperMultiPartRequestWrapper 中存在调用:

在这里插入图片描述

通过搜索我们发现 MultiPartRequestWrapperDispatcher 中被实例化,并在这里判断了Content-Type。只要在 Content-Type 中存在 multipart/form-data,request 便能够进入到MultiPartRequestWrapper 中直接调用 multi.parse(request, saveDir)

在这里插入图片描述

我们只需将 Content-Type 设置成 123multipart/form-data 等类似形式,其便能作为异常信息进入到 buildErrorMessage,进而触发 OGNL 表达式注入。接下来便是构造 Payload


2.靶场复现

vulhub地址

搭建漏洞环境:

在这里插入图片描述

打开8080端口,进入漏洞页面:

在这里插入图片描述

上传文件抓包,输入payload:

Content-Type:%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',521*2)}.multipart/form-data

命令执行成功:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于opencv的SVM算法的车牌识别系统设计与实现

基于opencv的SVM算法的车牌识别系统设计与实现 车牌识别技术是智能交通系统中的一项关键技术,它能够自动识别车辆的车牌号码。本文将详细介绍如何使用Python编程语言结合OpenCV库和SVM算法来实现车牌识别系统。 系统架构 车牌识别系统主要包括以下几个模块&…

外贸建站:WordPress搭建外贸独立站零基础自建站完整教程(2024)

对于做外贸来说,拥有自己的外贸独立网站真的非常重要。在外贸领域,如今各平台竞争激烈,规则多,成本高,价格战、政策变化快,还存在封店风险等等因素。在这种情况下,拥有外贸独立站就能很好规避上…

JavaSE:抽象类和接口

目录 一、前言 二、抽象类 (一)抽象类概念 (二)使用抽象类的注意事项 (三)抽象类的作用 三、接口 (一)接口概念 (二)接口语法规则 (三&a…

八口快速以太网交换机芯片方案分享/JL5110

以太网交换机(switch)是一种网络设备,用于在局域网中连接多个计算机和其他网络设备。它可以实现多个端口之间的同时传输,并根据MAC地址进行帧过滤和转发。交换机通过自学习的方式,将MAC地址与相应的接口关联起来,以便将数据帧准确…

Termius for Mac v8.4.0激活版下载

Termius for Mac是一款功能强大的多协议远程管理软件,专为开发人员、系统管理员和网络专业人士设计。它支持多种远程连接协议,如SSH、Telnet、RDP、VNC和RFB等,使得用户可以轻松连接到不同类型的远程服务器和设备。 软件下载:Term…

湖北兴发MES和用友NCC单据接口对接

湖北兴发MES和用友NCC单据接口对接 对接系统用友NCC 用友NCCloud,为客户提供面向大型企业集团、制造业、消费品、建筑、房地产、金融保险等14个行业大类,68个细分行业,涵盖数字营销、智能制造、财务共享、数字采购等18大解决方案,…

为什么要用能量回馈式负载

能量回馈式负载是一种能够将电能转化为其他形式能量的装置,同时还能将其他形式的能量转化为电能。这种负载在许多应用中具有重要的意义,因为它可以提高能源利用效率,减少能源浪费,降低环境污染,提高系统的稳定性和可靠…

Python3.10 - 列表的常用方法

01 列表的切片 lst [a, b, 1, 2, [c1, 1]]# 01 列表的切片 lst1 lst[0:2] # 从0开始切到1(顾头不顾尾), 切得长度超过1时, 切出来为list类型 print(lst1, type(lst1))lst2 lst[0] # 从中切某个元素时, 切出来的类型即元素本身, 以下同理 print(lst2, type(lst2))lst3 ls…

“由于找不到opencv_world3413.dll,无法继续执行代码”的解决方法

问题 在Windows系统中,编译完涉及到opencv的项目后,提示, 由于找不到opencv_world3413.dll,无法继续执行代码 解决方法 在编译好的opencv的bin文件内(如:D:\code\vs2017\opencv\build\x64\vc15\bin&…

Spring Boot 学习(2)——HelloWorld

HelloWorld!全宇宙码农的第一个(行)程序(代码)。 1、创建项目 打开idea,新建一个maven项目。 1)选择项目sdk(本例是1.8) 2)输入GroupId(co…

FME学习之旅---day20

我们付出一些成本,时间的或者其他,最终总能收获一些什么。 教程:AutoCAD 入门 FME使用四种主要格式来读取和写入AutoCAD图形文件;初级教程重点介绍AutoDesk AutoCAD DWG\DXF(ACAD) AutoCAD中常用的术语 实体:AutoCAD 图元表示 D…

超强命令行解析工具Apache Commons CLI

概述 为什么要写这篇文章呢?因为在读flink cdc3.0源码的时候发现了这个工具包,感觉很牛,之前写过shell命令,shell是用getopts来处理命令行参数的,但是其实写起来很麻烦,长时间不写已经完全忘记了,现在才发现原来java也有这种工具类,所以先学习一下这个的使用,也许之后自己在写…

封装一个vue3的公共组件

在Vue 3中,封装公共组件的场景包括但不限于以下几种情况: 重复使用的组件:如果你发现某个组件在多个地方重复使用,那么将其封装成公共组件是很有意义的。比如,页面中的各种表单控件(输入框、下拉框、日期选…

梵宁教育春日主题大奖赛

梵宁教育春日主题大赛正式拉开帷幕,我们诚挚邀请各位同学积极参与,用你们的创意和才华诠释春天的魅力。 本次大赛以春日为主题,参赛作品形式多样,包括海报设计和剪辑创作。我们鼓励同学们发挥想象力,将春天的美好瞬间、…

Golang和Java的对决:从设计理念到工具链的全面比较

文章目录 使用率排名Golang和Java设计理念语法和类型系统并发处理资源消耗生态系统和工具链 结语 使用率排名 据最新的2024年3月 Tiobe 编程语言排行榜,目前 Golang 的使用率排名为第8呈上升趋势,Java 的使用率排名为第4呈下降趋势 2024年3月2023年3月…

Go——函数

一. 函数定义 1.1 特点 无需声明原型支持不定变参支持多返回值支持命名返回参数支持匿名函数和闭包函数也是一种类型,一种函数可以赋值给变量不支持嵌套,一个包不能有两个名字一样的函数不支持重载不支持默认参数 1.2 函数声明 函数声明包含一个函数名&…

谈谈考研数学几个常见误区

25考研数学,一定一定要吃透基础,练好计算 我之所以要强调这个,是因为现在的考研数学,越来越重视基础和计算的考察,题海战术已经过时,如果想要有效的提升自己,要进行针对性的学习。我去年考研的…

【.NET全栈】.NET全栈学习路线

一、微软官方C#学习 https://learn.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/ C#中的数据类型 二、2021 ASP.NET Core 开发者路线图 GitHub地址:https://github.com/MoienTajik/AspNetCore-Developer-Roadmap/blob/master/ReadMe.zh-Hans.md 三、路线…

常州威雅:「西陵氏奖学金」项目,只为寻找优秀的你!

栀子花开,凤凰花落,又是一年中考季。站在深耕“全人教育”的第十年,常州威雅现今面向全社会的优秀初三学子,首次推出「西陵氏奖学金」项目,助力梦想起航。 西陵氏女嫘祖,华夏文明的奠基人。她所发现、发明的…

Flutter iOS上架指南

本文探讨了使用Flutter开发的iOS应用能否上架,以及上架的具体流程。苹果提供了App Store作为正式上架渠道,同时也有TestFlight供开发者进行内测。合规并通过审核后,Flutter应用可以顺利上架。但上架过程可能存在一些挑战,因此可能…