"踩坑"S2-061复现与思考

lock丶念拥
2020-12-20 / 0 评论 / 23 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2020年12月20日,已超过141天没有更新,若内容或图片失效,请留言反馈。

起因

struts官方照例更新补丁,这次更新了061,从官方通告上来看与059相似,059的通告类似029与036,既然这样,那大胆猜测一下,059如果修复了,061大概率会是补丁绕过,这里贴一个官方通告地址,方便大家自行查阅:

https://cwiki.apache.org/confluence/display/WW/Security+Bulletins

跟踪

进一步跟踪struts版本改动,得到以下可能存在的漏洞点以及类似黑名单:

根据官方描述,上述为黑名单机制,匹配其中的包名,当然还有类名,由于太长了,这里就不贴了。OK,既然看到类似的漏洞点了,不分析一下怎么可以呢,到此打开idea,maven导入struts2.5.26版本,分析过029的对此类应该不陌生,属于二次ognl表达式执行,看一下官方对此方法的修复情况如何

public static boolean containsExpression(String expr) {
        return expr != null && expr.contains("%{") && expr.contains("}");
    }

对应上图可以看到如果expr中存在%{}时会返回true,就不会对expr进行嵌套。更一步确定了,这次修复肯定还与二次ognl表达式有关。由此可知,这次061为059的补丁绕过。接下来开始惊心动魄的构造poc阶段

构造

首先搭建环境,这里为了给广大白帽子一个福利,啥下载官方all包了,啥maven导版本然后再创建xml了,通通不用,直接使用idea官方maven create from archetype,如下图所示,直接一键操作搭建

创建完成后,需要对index.jsp进行一点点改造,写入特定标签

<!DOCTYPE html PUBLIC
    "-//W3C//DTD XHTML 1.1 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%@taglib prefix="s" uri="/struts-tags" %>
<%
    String payload = request.getParameter("payload");
    request.setAttribute("payload",payload);
%>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title>Index</title>
    <s:head />
</head>
<body>
<s:a id="%{#request.payload}">testurl</s:a>
    <s:form action="helloWorld">
        <s:textfield label="What is your name?" name="name" />
        <s:textfield label="What is the date?" name="dateNow" />
        <s:submit />
    </s:form>
</body>
</html>

这时如果不了解struts框架的人就会踩到第一个坑了,包括我(菜鸡哭泣),此时传入payload字符会显示无法处理,这是因为此处关联了index.action,需要在index.action中增加一些方法,这里直接贴出

import com.opensymphony.xwork2.ActionSupport;
import java.util.Date;
import com.opensymphony.xwork2.conversion.annotations.Conversion;
import com.opensymphony.xwork2.conversion.annotations.TypeConversion;

@Conversion()
public class IndexAction extends ActionSupport {
    private String payload;
    private Date now = new Date(System.currentTimeMillis());

    @TypeConversion(converter = "Struts2.DateConverter")
    public Date getDateNow() { return now; }

    public String getPayload(String payload){
        return payload;
    }

    public void setPayload(String payload){
        this.payload=payload;
    }

    public String execute() throws Exception {
        now = new Date(System.currentTimeMillis());
        return SUCCESS;
    }
}

由此环境搭建完毕,可以开始愉快的调试之旅啦。这里需要对之前的漏洞有一定了解,推荐天融信的文章,分析的很好,链接放到最后。第一步,获取struts.valueStack,通过request全局变量。这一步,没有问题,第二步,开始正儿八经绕过获取context对象的限制,采用application根变量,获取org.apache.tomcat.InstanceManagerDefaultInstanceManager类,虽然推荐文章分析的很好了,但是这边啰嗦一下,贴出对应需要调用的方法

public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, ClassNotFoundException {
        Class<?> clazz = this.loadClassMaybePrivileged(className, this.classLoader);
        return this.newInstance(clazz.newInstance(), clazz);
    }

当然光写,不贴图,也感受不到调试的乐趣,于是这里贴图,如何获取需要的类和调用它的方法

看到上述回显,高高兴兴开始下一阶段,进一步通过org.apache.commons.collections.BeanMapsetBeanget方法获取context,这时第二个坑遇到了,这个坑会让你甚至以为之前做的全都有错,同样用贴图,表述问题

既然内存溢出了,这简直是懵逼,没办法源代码调试吧,通过跟踪setBean方法,确认数据正确压入Map中,问题出在get方法中,观察调用链,太长具体就不贴了,可以观察到在获取context时首先会是root对象,root对象同时又是根对象,这样就会导致,root对象获取root对象,产生死循环,导致内存溢出。所以我们需要将目前获取到的context再次压入Map中,获取其他属性。所以,接着构造,这时已经拿到了context对象,ok,接下来就是操作context对象去覆盖strust的黑名单。首先获取到黑名单类

看到响应体,离成功已经很接近啦,接下来只需将安全沙箱置空,直接调用一波struts黑名单中的类,完成代码执行,接下来的就不操作了,太多公众号有写了。

参考文章:
Struts2 S2-061漏洞分析(CVE-2020-17530): https://mp.weixin.qq.com/s/RD2HTMn-jFxDIs4-X95u6g

0

评论 (0)

取消