Bootstrap

struts2安全漏洞及解决办法

          7月17日,世界知名开源软件struts 2爆出了2个高危漏洞,这些漏洞可使黑客取得网站服务器的“最高权限”,从而使企业服务器变成黑客手中的“肉鸡”。

详细漏洞信息:
  http://struts.apache.org/release/2.3.x/docs/s2-016.html

  http://struts.apache.org/release/2.3.x/docs/s2-017.html
  http://struts.apache.org/release/2.3.x/docs/version-notes-23151.html

目前,官方已经发布高危安全漏洞补丁升级(最新版本为:2.3.15.1,下载地址:http://struts.apache.org/download.cgi#struts23151),升级修补了多个安全漏洞,其中包括这个远程任意代码的高危安全漏洞。


关于此漏洞,最好的解决方法当然是将struts2的jar包更新到最新版本。不过对于不同struts2版本,升级会引起一些包冲突。

升级步骤:
1.下载到的更新包中主要用到以下几个替换掉旧版本的:
commons-lang3-3.1.jar (保留原有的commons-lang.jar,因为升级后,org.apache.commons.lang.StringUtils类被org.apache.commons.lang3.StringUtils替换了,所有要保留原有包)
javassist-3.4.GA.jar (新加包)
ognl-3.0.6.jar (替换旧版本)
struts2-core-2.3.15.1.jar (替换旧版本)
xwork-core-2.3.15.1.jar (替换旧版本)
struts2-spring-plugin-2.3.15.1.jar(替换旧版本)


2.如果原有spring包版本太低,如2.0,还需要升级spring包到2.5版本。
struts2.1以下版本,需要检查struts的xml配置文件,type="redirect" 改为 type="redirectAction"。


3.修改 web.xml文件

<filter>
<filter-name>struts-cleanup</filter-name>
<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<filter-mapping>
<filter-name>struts-cleanup</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
替换为
<filter>  
     <filter-name>struts2</filter-name>  
     <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
     <init-param>  
         <param-name>actionPackages</param-name>  
         <param-value>com.mypackage</param-value>  
     </init-param>  
     </filter>  
 <filter-mapping>  
     <filter-name>struts2</filter-name>  
     <url-pattern>/*</url-pattern>

   <dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>  

 </filter-mapping>
原因:
从Struts2.1.3开始,废弃了ActionContextCleanUp过滤器,而在StrutsPrepareAndExecuteFilter过滤器中包含相应的功能。  
 需要注意因为新加的过滤器StrutsPrepareAndExecuteFilter不支持forward跳转导致。
必需将原过滤器配置文件增加

<dispatcher>REQUEST</dispatcher>

<dispatcher>FORWARD</dispatcher>

才能支持reponse.sendRedirect和request.getRequestDispatcher().forward操作

当然,还有一种更简便的方法,使用filter(过滤器)来实现。

代码如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
/**
 * 防脚本注入、非法攻击过滤器,使用时需要注意可能会过滤掉正常访问
 * <p>Copyright: Copyright (c) 2011</p>
 * <p>Company:</p>
 * @author
 * @version 1.0
 */
public class SqlFilter implements Filter {
public static final Logger logger = Logger.getLogger(sun.reflect.Reflection.getCallerClass(1));
//需要过滤的post字符
private static String sqlStr="',<,>,and,exec,insert,select,%20,delete,update,count,*,%,chr,mid,master,truncate,char,like,declare,&,#,(,),/**/,=,script,<script,\\u0023,redirect:,xwork2,getRuntime,getWriter";
//需要过滤的url字符
private static String urlStr="',<,>,exec,master,truncate,char,script,java.lang.ProcessBuilder,java.lang.String,\\u0023,redirect:,xwork2,getRuntime,getWriter";
public void destroy() {
// TODO Auto-generated method stub
   }
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
Enumeration names = req.getParameterNames();//获取所有的post参数名
String gotoUrl=req.getRequestURI();  //获取访问的url
String queryString = req.getQueryString();
while(names.hasMoreElements()){
String st=names.nextElement().toString();
  if(strInj(st,sqlStr)||strInj2(st,urlStr)){
  req.getSession().setAttribute("msgStr", "请不要输入非法参数:"+req.getParameter(st)+" !");
  res.sendRedirect(req.getContextPath()+"/jsp/common/error.jsp");
      return;
  }  

Iterator values = req.getParameterMap().values().iterator();//获取所有的post参数值
while(values.hasNext()){
  String[] value = (String[])values.next();
  for(int i = 0;i < value.length;i++){
  if(strInj(value[i],sqlStr)){
  request.setAttribute("msgStr", "请不要输入非法参数:"+value[i]+" !");
  res.sendRedirect(req.getContextPath()+"/jsp/common/error.jsp");
      return;
  }
  }

//判断访问的url中是否有非法参数
if(queryString!=null&&strInj2(queryString,urlStr)){
                req.getSession().setAttribute("msgStr", "请不要输入非法参数 !");
res.sendRedirect(req.getContextPath()+"/jsp/common/error.jsp");
return;
}
chain.doFilter(request, response);
}
/**
* 判断字符是否包含非法字符
* @param str
* @return
*/
public boolean strInj(String str,String standStr){
if(str==null||str.length()==0)return false;
String[] inj_stra=standStr.split(",");
for (int i=0 ; i < inj_stra.length ; i++ ){
if (str.toLowerCase().indexOf(" "+inj_stra[i]+" ")>=0){
  return true;
}
}
return false;
}
/**
* 判断字符是否包含非法字符,没有空格
* @param str
* @return
*/
public boolean strInj2(String str,String standStr){
if(str==null||str.length()==0)return false;
String[] inj_stra=standStr.split(",");
for (int i=0 ; i < inj_stra.length ; i++ ){
if (str.toLowerCase().indexOf(inj_stra[i])>=0){
  return true;
}
}
return false;
}
public void init(FilterConfig cfg) throws ServletException {

}
}

该过滤器通过了网络上多个版本的Struts终极漏洞利用工具及代码的检测。

;