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

VSole2021-12-15 16:12:11

 

漏洞概述

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

环境搭建

首先编写一个最简单的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用于定义路由:

        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"        "http://struts.apache.org/dtds/struts-2.0.dtd">
                        index.jsp            

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

  struts2  org.apache.struts2.dispatcher.FilterDispatcher
      struts2      /*

漏洞分析

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)

修复方案

这次漏洞修复体现在多处代码,首先就是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有它们自己的考究吧。

struts2struts2漏洞
本作品采用《CC 协议》,转载必须注明作者和本文链接
也可以输入多个域名、C段IP等,具体案例见下文。功能齐全的Web指纹识别和分享平台,内置了一万多条互联网开源的指纹信息。
首先恭喜你发现了宝藏。本项目集成了全网优秀的攻防武器项目,包含信息收集工具(自动化利用工具、资产发现工具、目录扫描工具、子域名收集工具....etc...),漏洞利用工具(各大CMS利用工具、中间件利用工具等项目),内网渗透工具、应急响应工具、甲方运维工具、等其他安全资料项目,供攻防双方使用。
S2-009是S2-003与S2-005的补丁绕过,当时的补丁是增加了正则以及相关的限制,主要的防御还是正则。
Struts2是以MVC架构为基础的WEB框架,通过WEB Filter的方式内嵌在WEB服务器中进行使用。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
避免网站显示SQL错误信息,比如类型错误、字段不匹配等,防止攻击者利用这些错误信息进行一些判断。
通过工控攻防演示让初学者学习web安全、内网渗透、工控安全相关知识技能。
VSole
网络安全专家