Struts2 高危漏洞修复方案 (S2 | 您所在的位置:网站首页 › struts2-016 › Struts2 高危漏洞修复方案 (S2 |
近期Struts2被曝重要漏洞,此漏洞影响struts2.0-struts2.3所有版本,可直接导致服务器被远程控制从而引起数据泄漏,影响巨大,受影响站点以电商、银行、门户、政府居多.
引发的威胁: 取得网站服务器主机管理权限。 CVSS:(AV:R/AC:L/Au:NR/C:C/A:C/I:C/B:N)score:10.00(最高10分,高危) 即:远程攻击、攻击难度低、不需要用户认证,对机密性、完整性、可用性均构成完全影响。
验证情况: Struts2漏洞利用工具下载:右键保存图片,重命名为后缀.zip,解压打开。
(右键保存图片,重命名为后缀.zip)
验证如下: 图1 网站目录 图2 成功执行ipconfig命令 图3成功硬盘目录
官方描述:S2-016:https://cwiki.apache.org/confluence/display/WW/S2-016S2-017:https://cwiki.apache.org/confluence/display/WW/S2-017 官方建议修复方案:升级到最新版本 struts-2.3.15.1 但通常现有系统升级,可能导致不稳定及与其他框架比如spring等的不兼容,成本较高。鉴于此csdn网友jzshmyt整理了一种既可以不用升级现有struts版本,有能完美解决这两个漏洞的方案, 分享如下: ------------------------- 第1步.右键保存图片,重命名为后缀.zip,解压打开。
(右键保存图片,重命名为后缀.zip) 第2步.解压,将src目录中的所有文件,复制到自己项目的src目录中,编译通过,形成class文件 (本例struts是Struts-core-2.1.6版本_对2.0-2.3版本都有效,实际项目需要根据struts版本做适当调整). 应用服务器会优先加载class目录中的类,自动覆盖jar包中的类. 第3步.web.xml中配置com.htht.commonweb.listener.MyServletContextListener [html] view plaincopy org.hdht.commonweb.listener.MyServletContextListener第4步.重启服务,修复完毕.
附:com.htht.commonweb.listener.MyServletContextListener.java,完整包参见struts2_S016_S017_repair.rar解压目录 启动漏洞监听 ------------------------- [java] view plaincopy package com.htht.commonweb.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.htht.commonweb.JavaEEbugRepair; /** * WEB应用程序初始化监听器 */ public class MyServletContextListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { try { JavaEEbugRepair.initRepair_S2_016(); JavaEEbugRepair.initRepair_S2_017(); } catch (Exception e) { e.printStackTrace(); } } }
附:ognl.Ognl.java,完整包参见struts2_S016_S017_repair.rar解压目录 ognl调用解决漏洞 ------------------------- [java] view plaincopy package ognl; import java.io.StringReader; import java.util.Map; import com.htht.commonweb.JavaEEbugRepair; public abstract class Ognl { public static Object parseExpression(String expression) throws OgnlException { if(JavaEEbugRepair.repair_s2_016(expression)){ return null; } try { OgnlParser parser = new OgnlParser(new StringReader(expression)); return parser.topLevelExpression(); } catch (ParseException e) { throw new ExpressionSyntaxException(expression, e); } catch (TokenMgrError e) { throw new ExpressionSyntaxException(expression, e); } } public static Map createDefaultContext(Object root) { return addDefaultContext(root, null, null, null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver) { return addDefaultContext(root, classResolver, null, null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter) { return addDefaultContext(root, classResolver, converter, null, new OgnlContext()); } public static Map createDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess) { return addDefaultContext(root, classResolver, converter, memberAccess, new OgnlContext()); } public static Map addDefaultContext(Object root, Map context) { return addDefaultContext(root, null, null, null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, Map context) { return addDefaultContext(root, classResolver, null, null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, Map context) { return addDefaultContext(root, classResolver, converter, null, context); } public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context) { OgnlContext result; if (!(context instanceof OgnlContext)) { result = new OgnlContext(); result.setValues(context); } else { result = (OgnlContext)context; } if (classResolver != null) { result.setClassResolver(classResolver); } if (converter != null) { result.setTypeConverter(converter); } if (memberAccess != null) { result.setMemberAccess(memberAccess); } result.setRoot(root); return result; } public static void setClassResolver(Map context, ClassResolver classResolver) { context.put("_classResolver", classResolver); } public static ClassResolver getClassResolver(Map context) { return (ClassResolver)context.get("_classResolver"); } public static void setTypeConverter(Map context, TypeConverter converter) { context.put("_typeConverter", converter); } public static TypeConverter getTypeConverter(Map context) { return (TypeConverter)context.get("_typeConverter"); } public static void setMemberAccess(Map context, MemberAccess memberAccess) { context.put("_memberAccess", memberAccess); } public static MemberAccess getMemberAccess(Map context) { return (MemberAccess)context.get("_memberAccess"); } public static void setRoot(Map context, Object root) { context.put("root", root); } public static Object getRoot(Map context) { return context.get("root"); } public static Evaluation getLastEvaluation(Map context) { return (Evaluation)context.get("_lastEvaluation"); } public static Object getValue(Object tree, Map context, Object root) throws OgnlException { return getValue(tree, context, root, null); } public static Object getValue(Object tree, Map context, Object root, Class resultType) throws OgnlException { OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context); Object result = ((Node)tree).getValue(ognlContext, root); if (resultType != null) { result = getTypeConverter(context).convertValue(context, root, null, null, result, resultType); } return result; } public static Object getValue(String expression, Map context, Object root) throws OgnlException { return getValue(expression, context, root, null); } public static Object getValue(String expression, Map context, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), context, root, resultType); } public static Object getValue(Object tree, Object root) throws OgnlException { return getValue(tree, root, null); } public static Object getValue(Object tree, Object root, Class resultType) throws OgnlException { return getValue(tree, createDefaultContext(root), root, resultType); } public static Object getValue(String expression, Object root) throws OgnlException { return getValue(expression, root, null); } public static Object getValue(String expression, Object root, Class resultType) throws OgnlException { return getValue(parseExpression(expression), root, resultType); } public static void setValue(Object tree, Map context, Object root, Object value) throws OgnlException { OgnlContext ognlContext = (OgnlContext)addDefaultContext(root, context); Node n = (Node)tree; n.setValue(ognlContext, root, value); } public static void setValue(String expression, Map context, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), context, root, value); } public static void setValue(Object tree, Object root, Object value) throws OgnlException { setValue(tree, createDefaultContext(root), root, value); } public static void setValue(String expression, Object root, Object value) throws OgnlException { setValue(parseExpression(expression), root, value); } public static boolean isConstant(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isConstant((OgnlContext)addDefaultContext(null, context)); } public static boolean isConstant(String expression, Map context) throws OgnlException { return isConstant(parseExpression(expression), context); } public static boolean isConstant(Object tree) throws OgnlException { return isConstant(tree, createDefaultContext(null)); } public static boolean isConstant(String expression) throws OgnlException { return isConstant(parseExpression(expression), createDefaultContext(null)); } public static boolean isSimpleProperty(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isSimpleProperty((OgnlContext)addDefaultContext(null, context)); } public static boolean isSimpleProperty(String expression, Map context) throws OgnlException { return isSimpleProperty(parseExpression(expression), context); } public static boolean isSimpleProperty(Object tree) throws OgnlException { return isSimpleProperty(tree, createDefaultContext(null)); } public static boolean isSimpleProperty(String expression) throws OgnlException { return isSimpleProperty(parseExpression(expression), createDefaultContext(null)); } public static boolean isSimpleNavigationChain(Object tree, Map context) throws OgnlException { return ((SimpleNode)tree).isSimpleNavigationChain((OgnlContext)addDefaultContext(null, context)); } public static boolean isSimpleNavigationChain(String expression, Map context) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), context); } public static boolean isSimpleNavigationChain(Object tree) throws OgnlException { return isSimpleNavigationChain(tree, createDefaultContext(null)); } public static boolean isSimpleNavigationChain(String expression) throws OgnlException { return isSimpleNavigationChain(parseExpression(expression), createDefaultContext(null)); } }
附:com.htht.commonweb.JavaEEbugRepair.java,完整包参见struts2_S016_S017_repair.rar解压目录 拦截攻击关键代码 [java] view plaincopy OgnlRuntime.setMethodAccessor(Runtime.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(System.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(ProcessBuilder.class,new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(OgnlRuntime.class, new NoMethodAccessor()); //io敏感操作 OgnlRuntime.setMethodAccessor(OutputStream.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(InputStream.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(File.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(DataOutput.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(DataInput.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(Reader.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(Writer.class, new NoMethodAccessor());
------------------------- [java] view plaincopy package com.htht.commonweb; import java.io.DataInput; import java.io.DataOutput; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.util.Map; import ognl.MethodAccessor; import ognl.MethodFailedException; import ognl.OgnlRuntime; /** * @author yanjianzhong([email protected]) 2013/08/08 * @版权所有,转载请标明出处. http://blog.csdn.net/jzshmyt * download : http://jskfs.googlecode.com/files/struts2_(016_017)_bug_repair.rar */ public class JavaEEbugRepair{ /* * 官方描述: * S2-016:https://cwiki.apache.org/confluence/display/WW/S2-016 * S2_016 bug repair */ private static S2_0XX s2_016 = new S2_0XX(); /* * 修改 ognl.Ognl#parseExpression,调用 check_s2_016 方法 * public static Object parseExpression(String expression)throws OgnlException * { * //modify point begin * if(JavaEEBug.check_s2_016(expression)){ * return null * } * //modify point end * try { * OgnlParser parser = new OgnlParser(new StringReader(expression)); * return parser.topLevelExpression(); * } catch (ParseException e) { * throw new ExpressionSyntaxException(expression, e); * } catch (TokenMgrError e) { * throw new ExpressionSyntaxException(expression, e); * } * } */ public static boolean repair_s2_016(String expression){ return s2_016.check(expression); } /* * 在servlet/struts/spring 任何一个框架的listener中调用 */ public static void initRepair_S2_016(){ OgnlRuntime.setMethodAccessor(Runtime.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(System.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(ProcessBuilder.class,new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(OgnlRuntime.class, new NoMethodAccessor()); //io敏感操作 OgnlRuntime.setMethodAccessor(OutputStream.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(InputStream.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(File.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(DataOutput.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(DataInput.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(Reader.class, new NoMethodAccessor()); OgnlRuntime.setMethodAccessor(Writer.class, new NoMethodAccessor()); s2_016 = new S2_0XX(){ public boolean check(String expression){ String evalMethod[] = {"Runtime", "ProcessBuilder","java.io.File","new File","OutputStream","InputStream"}; String methodString = null; methodString = expression.toLowerCase(); for (int i = 0; i -1) { System.out.print("|OGNL正在执行恶意语句|" + methodString + "|看到这个消息,请联系安全工程师!!!"); return true; } } return false; } }; } /* * S2-017:https://cwiki.apache.org/confluence/display/WW/S2-017 * S2_017 bug repair */ private static S2_0XX s2_017 = new S2_0XX(); /* * Call by org.apache.struts2.dispatcher.mapper.DefaultActionMapper#handleSpecialParameters * Repair Example : * public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) * { * Set uniqueParameters = new HashSet(); * Map parameterMap = request.getParameterMap(); * Iterator iterator = parameterMap.keySet().iterator(); * while (iterator.hasNext()) { * String key = (String)iterator.next(); * * if ((key.endsWith(".x")) || (key.endsWith(".y"))) { * key = key.substring(0, key.length() - 2); * } * //modify point begin * if (JavaEEBug.check_s2_017(key)) { * return; * } * //modify point end * if (!uniqueParameters.contains(key)) { * ParameterAction parameterAction = (ParameterAction)this.prefixTrie.get(key); * * if (parameterAction != null) { * parameterAction.execute(key, mapping); * uniqueParameters.add(key); * break; * } * } * } * } */ public static boolean repair_s2_017(String key){ return s2_017.check(key); } /* * 在servlet/struts/spring 任何一个框架的listener中调用 */ public static void initRepair_S2_017(){ s2_017 = new S2_0XX(){ public boolean check(String key){ return (key.contains("redirect:")) || (key.contains("redirectAction:")) || (key.contains("action:")); } }; } } /** * 漏洞验证修复之基类 * 说明: * 漏洞修复代码的实现逻辑,非侵入式设计。 * 当listener中未调用initRepair_S2_016、initRepair_S2_017进行漏洞调用初始化时, * 保持Ognl和DefaultActionMapper修复前源码等价逻辑. * */ class S2_0XX { public boolean check(String key){ return false; } } class NoMethodAccessor implements MethodAccessor { public NoMethodAccessor() { } @Override public Object callStaticMethod(Map context, Class targetClass, String methodName, Object[] args) throws MethodFailedException { if(targetClass!=null){ System.out.println("拦截并拒绝敏感操作: static "+targetClass.getName()+"#"+methodName); } throw new MethodFailedException("do not run", methodName, null); } @Override public Object callMethod(Map context, Object target, String methodName, Object[] args) throws MethodFailedException { // TODO Auto-generated method stub if(target!=null){ System.out.println("拦截并拒绝敏感操作:"+target.getClass().getName()+"#"+methodName); } throw new MethodFailedException("do not run", methodName,null); } }
附:org.apache.struts2.dispatcher.mapper.DefaultActionMapper.java,完整包参见struts2_S016_S017_repair.rar解压目录 重写Struts2核心包的DefaultActionMapper类。 每个版本的DefaultActionMapper类不一样,可以用反编译工具 取出org.apache.struts2.dispatcher.mapper.DefaultActionMapper.java的全部代码重写编译 ------------------------- 修改地方handleSpecialParameters方法上的while循环体里添加: if (JavaEEbugRepair.repair_s2_017(key)) { return; } 这样受攻击就会判断返回,返回的显示是一串html静态代码,漏洞解决成功!
[java] view plaincopy public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) { Set uniqueParameters = new HashSet(); Map parameterMap = request.getParameterMap(); Iterator iterator = parameterMap.keySet().iterator(); while (iterator.hasNext()) { String key = (String)iterator.next(); if ((key.endsWith(".x")) || (key.endsWith(".y"))) { key = key.substring(0, key.length() - 2); } if (JavaEEbugRepair.repair_s2_017(key)) { return; } if (!uniqueParameters.contains(key)) { ParameterAction parameterAction = (ParameterAction)this.prefixTrie.get(key); if (parameterAction != null) { parameterAction.execute(key, mapping); uniqueParameters.add(key); break; } } } }
参考: http://blog.csdn.net/mydwr/article/details/18727627 http://blog.csdn.net/jzshmyt/article/details/9842501 http://software.intel.com/zh-cn/blogs/2013/08/08/struts2-s2-016s2-017/?utm_campaign=CSDN&utm_source=intel.csdn.net&utm_medium=Link&utm_content=others-%20Struts2 网上的多种方案:http://star.baidu.com/forum/forum.php?mod=viewthread&tid=1945 |
CopyRight 2018-2019 实验室设备网 版权所有 |