漏洞:CVE-2021-31805——升级struts版本2.0.6-2.5.30

漏洞:CVE-2021-31805——升级struts版本2.0.6-2.5.30

漏洞描述

Apache struts2部分版本存在漏洞,对不受信任的用户输入使用强制OGNL可能导致远程代码执行,黑客可利用该漏洞控制服务器。

一、切换jar包更改配置

切换jar包:

struts2-core-2.5.30.jar
struts2-spring-plugin-2.5.30.jar
删除 xwork-2.0.4  这个包已经包含在了struts2-core-2.5.30.jar中
commons-fileupload-1.4.jar
commons-io-2.6.jar

修改web.xml配置

<filter>
		<filter-name>struts2</filter-name>
		<filter-class>
			org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
		</filter-class>
</filter>

启动报错:java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager

引入jar包:log4j-api-2.12.4.jar

启动报错:java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils

引入jar包:commons-lang3-3.8.1.jar

启动报错:java.lang.NoSuchMethodError: ognl.SimpleNode.isEvalChain(Lognl/OgnlContext;)Z

升级jar包:ognl-2.6.11.jar ---> ognl-3.1.29.jar

启动报错:java.lang.NoClassDefFoundError: freemarker/template/Version

升级jar包:freemarker-2.3.10.jar ---> freemarker-2.3.31.jar

启动报错:Caused by: java.lang.ClassNotFoundException: javassist.ClassPool

引入jar包:javassist-3.20.0-GA.jar

访问主页面报错,时而好使时而不好使

ERROR DefaultDispatcherErrorHandler Exception occurred during processing request: null
 java.lang.NullPointerException
	at com.opensymphony.xwork2.validator.DelegatingValidatorContext.makeTextProvider(DelegatingValidatorContext.java:212)
	at com.opensymphony.xwork2.validator.DelegatingValidatorContext.<init>(DelegatingValidatorContext.java:65)
	at com.opensymphony.xwork2.validator.AnnotationActionValidatorManager.validate(AnnotationActionValidatorManager.java:127)
	at com.opensymphony.xwork2.validator.AnnotationActionValidatorManager.validate(AnnotationActionValidatorManager.java:123)
	at com.opensymphony.xwork2.validator.ValidationInterceptor.doBeforeInvocation(ValidationInterceptor.java:227)
<!--修改validators.xml 的头信息-->
<!DOCTYPE validators PUBLIC
        "-//OpenSymphony Group//XWork Validator Config 1.0//EN"
        "http://www.opensymphony.com/xwork/xwork-validator-config-1.0.dtd">

---改为--->

<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator Definition 1.0//EN"
        "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">

如果还不好使,检查validator配置的自定义类中是否引入了某些已被删除的包
<!-- 修改struts.xml  开启允许动态方法调用  -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />

<!-- 修改struts 配置文件 -->
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
改为:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
        "http://struts.apache.org/dtds/struts-2.5.dtd">


<!-- 修改struts 配置文件 加入配置 -->
<!--  strict-method-invocation="false" 拒绝所有未通过method属性配置或者<allowed-methods>标签标明的方法 -->
<!--  <global-allowed-methods>regex:.*</global-allowed-methods> 允许使用通配符调用方法-->
<package name="xx" extends="struts-default"  strict-method-invocation="false"  namespace="/xxx">
    <global-allowed-methods>regex:.*</global-allowed-methods>
	<action name="xxx" class="xxx" method="xxx">
			<result name="success">
				/pages/xxx/xxx.jsp
			</result>
	 </action>
</package>

如果页面访问有问题可以尝试进行下面修改


<!-- 修改web.xml -->
    <filter-mapping>
            <filter-name>struts2</filter-name>
            <url-pattern>/struts/*</url-pattern>
        </filter-mapping>
    <filter-mapping>
改为:     
	<filter-name>struts2</filter-name>
		<url-pattern>/struts/*.action</url-pattern>
	</filter-mapping>
	<filter-mapping>
		<filter-name>struts2</filter-name>
		<url-pattern>/struts/*.jsp</url-pattern>
	</filter-mapping>

菜单无法加载或显示html标签问题

<s:property value="menu" escape="false" /></td>
改为:
<s:property value="menu" escapeHtml="false" /></td>
<!--
escapeHtml默认为true, menu按照文本格式原样输出,带有标签
escapeHtml为false, menu的内容会解析为HTML内容,在显示的时候有换行效果
-->

二、页面标签修改

两个版本的ognl标签有很多改动,需要调整页面

<!--没什么用暂时删掉-->
<%@page import="org.apache.derby.impl.sql.catalog.SYSCHECKSRowFactory"%>

常用标签用法

<s:hidden name="editType" id="editType" value="%{editType}"></s:hidden>

<!--grade.id 后面不要加空格,否则生成的id会多一个下划线-->
<s:hidden name="grade.id"/> 

<s:if test="editType=='insert'"></s:if>

<s:if test="%{editType=='insert' }"></s:if>
    
<c:if test="${editType=='insert' }"></c:if>

<c:set var="index" value="0" />
    <s:if test="%{index+1==1}">不好使</s:if>   
    <a href="#tab${index+1 }"/>
    <div id="tab${indexi+1 }"></div>
    <iframe id="taskIframe${indexi+1 }"></iframe>
<c:set var="index" value="${index+1 }" />

<s:set var="index" value="0"/>
    <div style="display: none">${index+1 }</div>
    <s:property value='%{#index + 1}' />
<s:set var="index" value="%{#index+1 }" />

<s:iterator value="systemMap.keySet()" id="system">   
--改为-->  
<s:iterator value="systemMap.keySet()" var="system">
    
<s:iterator value="systemTasks" status="stuts">
    <!--id 复制拼接-->
    <div id="tab<s:property value="#stuts.count" />"></div>
    
    <s:hidden name="userGrades[%{#stuts.index}].gradeCode" value="%{#aaa.gradeCode}" /></td>
    
	<s:if test="%{#stuts.index +1 ==1 }"></s:if>
    
	      <s:property value="%{systemTasks[#stuts.index].taskCName}" />
    	  <iframe id="taskIframe%{#stuts.index + 1 }" 
                  src="${ctx}/xxxx/xxxx.do?xxx=<s:property value="%{xxx[#stuts.index].xxx}"/>
                       &editType=${editType}&gradeID=${grade.id}" 
                  frameborder="0" width="100%" height="580"></iframe>
    
</s:iterator>

<s:iterator 中的status 使用方法

<s:iterator 中的status 使用方法

1:#status.odd 是否奇数行

2:#status.count 当前行数

3:#status.index 当前行的序号,从0开始『#status.count=#status.index+1』

4:#status.first 是否第一行

5:#status.last 是否最后一行

6:#status.modules(int) 当前行数取模

三、异常原因分析

ERROR DefaultDispatcherErrorHandler Exception occurred during processing request: null

跟源码发现:AnnotationActionValidatorManager中的属性textProviderFactory为null
继续跟进发现:

  1. 项目启动时,strut2 使用@Inject注入依赖,而且属性注入顺序完全随机且在for循环中处理,
  2. 当某个属性注入失败时,会停止该类的属性注入,也就是说,这个类只有注入失败前的属性有值,后面的属性直接初始化为null,而且没有异常抛出
//源码:construct
class ContainerImpl implements Container {
    ...
    Object construct(InternalContext context, Class<? super T> expectedType) {
        ... 
        //injectors: @Inject注解标识的属性
        for (Injector injector : injectors) {
              injector.inject(context, t);
        }
        ...
    }
    ...
}
  1. 反复debug发现 AnnotationActionValidatorManager在 ```validatorFactory``这个属性注入时总会莫名其妙的跳出循环,开始处理加载其他类信息

  2. ValidatorFactory这个类是一个接口,只有一个实现类 DefaultValidatorFactory,这个类有个比较坑的初始化方法init

    public class DefaultValidatorFactory implements ValidatorFactory, Initializable {
        ...
        protected ValidatorFileParser validatorFileParser;
        @Override
        public void init() {
            parseValidators();
        }
        private void parseValidators() {
            ...
            // Parse default validator configurations
            String resourceName = "com/opensymphony/xwork2/validator/validators/default.xml";
            retrieveValidatorConfiguration(resourceName);
    
            // Overwrite and extend defaults with application specific validator configurations
            resourceName = "validators.xml";
            retrieveValidatorConfiguration(resourceName);
            ...
        }
        private void retrieveValidatorConfiguration(String resourceName) {
            InputStream is = ClassLoaderUtil.getResourceAsStream(resourceName, DefaultValidatorFactory.class);
            if (is != null) {
                validatorFileParser.parseValidatorDefinitions(validators, is, resourceName);
            }
        }
    }
    
    public class DefaultValidatorFileParser implements ValidatorFileParser {
        ...
     	public void parseValidatorDefinitions(Map<String, String> validators, InputStream is, String resourceName) {
    
            InputSource in = new InputSource(is);
            in.setSystemId(resourceName);
    
            Map<String, String> dtdMappings = new HashMap<>();
            dtdMappings.put("-//Apache Struts//XWork Validator Config 1.0//EN", "xwork-validator-config-1.0.dtd");
            dtdMappings.put("-//Apache Struts//XWork Validator Definition 1.0//EN", "xwork-validator-definition-1.0.dtd");
    		/*********************/
            Document doc = DomHelper.parse(in, dtdMappings);
    
            if (doc != null) {
                NodeList nodes = doc.getElementsByTagName("validator");
    
                for (int i = 0; i < nodes.getLength(); i++) {
                    Element validatorElement = (Element) nodes.item(i);
                    String name = validatorElement.getAttribute("name");
                    String className = validatorElement.getAttribute("class");
    
                    try {
                        // catch any problems here
                        objectFactory.buildValidator(className, new HashMap<String, Object>(), ActionContext.getContext().getContextMap());
                        validators.put(name, className);
                    } catch (Exception e) {
                        throw new ConfigurationException("Unable to load validator class " + className, e, validatorElement);
                    }
                }
            }
        }
        ...
    }
    
    1. 这个方法中加载了两个配置文件com/opensymphony/xwork2/validator/validators/default.xmlvalidators.xml

      第一个文件是jar包中自己的配置文件,没问题

      第二个文件就比较坑了,他在系统的resources目录下查找文件,找到则加载

      因为我的文件是老版本的配置文件,因此在Document doc = DomHelper.parse(in, dtdMappings);这一步格式化时,格式化报错了,这个时候抛出一个有意思的异常java.net.ConnectException: Connection timed out: connect

      接着一路上抛,在这个位置打印了一个LOG.warn, 而我的后台风平浪静。。。。

      public class InterceptorBuilder {
          ...
          public static List<InterceptorMapping> constructInterceptorReference(InterceptorLocator interceptorLocator,
                                                                               String refName, Map<String,String> refParams, Location location, ObjectFactory objectFactory) throws ConfigurationException {
              ...
              if (referencedConfig instanceof InterceptorConfig) {
                      InterceptorConfig config = (InterceptorConfig) referencedConfig;
                      Interceptor inter;
                      try {
                          inter = objectFactory.buildInterceptor(config, refParams);
                          result.add(new InterceptorMapping(refName, inter, refParams));
                      } catch (ConfigurationException ex) {
                    	    LOG.warn(new ParameterizedMessage("Unable to load config class {} at {} probably due to a missing jar, which might be fine if you never plan to use the {} interceptor",
                                  config.getClassName(), ex.getLocation(), config.getName()), ex);
                      }
      
                  }
              ...
          }
          ...
      }
      
    2. struts2-core-2.5.30版本依赖了log4j-api-2.12.4, 这与老版本的log4j-1.2.13貌似不兼容,引入log4j-core-2.12.4.jar是否会打印,有待验证,log4j-core在2.16版本前的都有漏洞,暂时不处理

    3. 对比com/opensymphony/xwork2/validator/validators/default.xmlvalidators.xml两个文件发现头部标签不一致,修改后加载正常

    4. 如果还不好使,检查validator配置的自定义类中是否引入了某些已被删除的包

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CVE-2021-31805是一个远程命令执行漏洞,该漏洞是由于对CVE-2020-17530的修复不完整而引起的。CVE-2020-17530是由于Struts2对某些标签属性的属性值进行二次表达式解析而导致的漏洞。当这些标签属性中使用了%{x}并且x的值可被用户控制时,用户可以传入%{payload}来执行OGNL表达式。在CVE-2021-31805漏洞中,仍然存在部分标签属性会导致攻击者构造的恶意OGNL表达式执行,从而导致远程代码执行。该漏洞可能影响的版本有待进一步确认。 根据给出的引用信息,无法提供关于漏洞的具体细节或利用方式。请注意,这是一个安全漏洞,为了保护系统安全,建议及时修复该漏洞或采取相应的安全防护措施。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [apache stucts 2远程命令执行漏洞 (CVE-2021-31805) poc检测工具](https://download.csdn.net/download/weixin_59679023/85194448)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [S2-062 远程命令执行漏洞复现(cve-2021-31805)](https://blog.csdn.net/qq_44110340/article/details/124279481)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值