浅谈CVE-2022-22965漏洞成因(三)

admin 2022年6月30日01:55:20浅谈CVE-2022-22965漏洞成因(三)已关闭评论63 views字数 12391阅读41分18秒阅读模式

前言:记录一篇自己入门java安全的故事,捋一下思路,轻量知识 ,重在调试 !

.

这篇文章四个部分:

引入篇:整理一下CVE-2022-22965漏洞的来龙去脉

基础篇:回顾Java中一些基础的内容

调试篇:阅读Spring MVC部分源码

分析篇:分析篇:分析CVE-2010-1622、CVE-2022-22965的漏洞成因

.

调试篇

( 紧接 "浅谈CVE-2022-22965漏洞成因(二)",开始Spring MVC的调试过程 )

Spring
MVC框架内部的具体实现多且复杂,没有办法完全的记录到位,另外调试的乐趣只有亲自动手才有意思,而干巴巴的看是很枯燥的,所以这里会尽可能多记录一些自己调试过程中的记录,旨在为想要自己调试的小伙伴们提供一份参考。再者,由于本人功力有限,没办法做到面面俱到,所以本着学习的原则,自己会尽可能的多记录一些,如有分析不当的地方,欢迎“水”评,敬请斧正!

1、Spring MVC框架执行流程

@RequestMapping注解标注的是处理前端请求的最末端,在controller包的FirstController的22行打下一个断点,此时用户访问 localhost:8090/SpringMVC5/level/info?nick=isee&hobby[0]=travel&job=mbrick,我们将会获得一个大致范围的函数调用栈,从函数调用栈中,可以看出从前端用户请求再到后端服务器执行处理逻辑大概要经历如下5个过程:

-> jvm创建处理线程
-> Tomcat封装http请求
-> 进入FrameworkServlet框架层
-> DispatcherServlet调用MVC组件
-> 反射调用FirstController的info方法

浅谈CVE-2022-22965漏洞成因(三)

完整的函数调用栈如下:

info:22, FirstController (controller)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:62, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
doInvoke:190, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:105, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:878, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:792, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)
doDispatch:1040, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
processRequest:1006, FrameworkServlet (org.springframework.web.servlet)
doGet:898, FrameworkServlet (org.springframework.web.servlet)
service:622, HttpServlet (javax.servlet.http)
service:883, FrameworkServlet (org.springframework.web.servlet)
service:729, HttpServlet (javax.servlet.http)
internalDoFilter:230, ApplicationFilterChain (org.apache.catalina.core)
doFilter:165, ApplicationFilterChain (org.apache.catalina.core)
doFilter:53, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:192, ApplicationFilterChain (org.apache.catalina.core)
doFilter:165, ApplicationFilterChain (org.apache.catalina.core)
invoke:199, StandardWrapperValve (org.apache.catalina.core)
invoke:108, StandardContextValve (org.apache.catalina.core)
invoke:522, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:140, StandardHostValve (org.apache.catalina.core)
invoke:79, ErrorReportValve (org.apache.catalina.valves)
invoke:620, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
service:349, CoyoteAdapter (org.apache.catalina.connector)
service:1110, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:785, AbstractProtocol\$ConnectionHandler (org.apache.coyote)
doRun:1419, NioEndpoint\$SocketProcessor (org.apache.tomcat.util.net)
run:44, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1128, ThreadPoolExecutor (java.util.concurrent)
run:628, ThreadPoolExecutor\$Worker (java.util.concurrent)
run:61, TaskThread\$WrappingRunnable (org.apache.tomcat.util.threads)
run:834, Thread (java.lang)

```

.

从DispatcherServlet开始,才真正进入了Spring MVC框架的处理逻辑中(后面的调试也会从DispatcherServlet开始)

.

2、Spring MVC组件调用关系

DispatcherServlet作为MVC框架的核心类,会拦截所有的请求并将请求分发到各个处理组件,以及在这个过程中调用到相应的处理器去处理请求

浅谈CVE-2022-22965漏洞成因(三)

3、从DispatcherServlet开始调试

DispatcherServlet类中的doService方法会调用doDispatch方法,重点看一下doDispatch方法

```

392 protected void doService(HttpServletRequest request, HttpServletResponse response)

470 protected void doDispatch(HttpServletRequest request, HttpServletResponse response)

```

DispatcherServlet.doDispatch方法

```

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;

            try {
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }

                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }

            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

```

简化后的逻辑就是获取Handler,获取Handler的适配器,调用HandlerAdapter的handle方法以及处理响应报文

(接下我们重点调试这4个过程,并穿插着作一些补充)

```
......

获取handel

484 mappedHandler = this.getHandler(processedRequest)

获取HandlerAdapter

490 HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler())

调用HandlerAdapte的handle

504 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

510 mappedHandler.applyPostHandle(processedRequest, response, mv);

......
```

.

后面的调试将会从上面归纳的逻辑中的前三条展开

.

4、调试获取handel的过程

#484 mappedHandler = this.getHandler(processedRequest)

DispatcherServlet的getHandler方法遍历handlerMappings获取到一个RequestMappingHandlerMapping并将它转成一个HandlerMapping,之后会调用HandlerMapping的getHandler方法获得一个HandlerExecutionChain(处理器与拦截器的组合)并返回

浅谈CVE-2022-22965漏洞成因(三)

浅谈CVE-2022-22965漏洞成因(三)

在上面这一步中,我们可以看到,handlerMappings是调用前就存在的,一共保存了两个handlerMapping,分别是RequestMappingHandlerMapping和BeanNameUrlHandlerMapping

浅谈CVE-2022-22965漏洞成因(三)

也就是说handlerMappings在很早之前就被初始化了,这里,我们先来看看handlerMapping是什么

浅谈CVE-2022-22965漏洞成因(三)

handlerMapping是一个接口,HandleMapping本身用于建立并保存URI和Handler的对应关系,Handler是@RequestMapping注解标记的类或方法,handlerMapping返回的Handler通常是一个HandlerExecutionChain

也就是说,前端传过来的请求需要查找HandleMapping后才能找到对应的处理器方法

浅谈CVE-2022-22965漏洞成因(三)

handlerMappings是handlerMapping的集合,Spring MVC初始化时会事先加载一些默认的handlerMapping并保存到handlerMappings里

浅谈CVE-2022-22965漏洞成因(三)
handlerMappings的初始化从HttpServletBean开始

浅谈CVE-2022-22965漏洞成因(三)
.

servlet中有三个非常重要的方法,也是servlet的生命周期,即init、service和destroy。

.

HttpServletBean的主要功能就是将Servlet的配置信息作为Bean的属性自动赋值给 Servlet 的属性,且HttpServletBean中重写了init方法,会进行如下操作:

  • 获取到servletconfig 中 init-param的spring mvc 配置,并且封装成PropertyValues对象
  • 将servlet类转换成BeanWrapper,让spring更好的操作javabean的属性
  • 调用initServletBean方法后面会被其子类FrameworkServlet去实现,FrameworkServlet初始化了webApplicationContext容器

接下来让我们从HttpServletBean开始,来看看handlerMappings最终是如何被初始化的

.

HttpServletBean的init方法

```java
public final void init() throws ServletException {
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}

            throw var4;
        }
    }

    this.initServletBean();
}

```

HttpServletBean的init方法会调用this.initServletBean并进入到框架层

浅谈CVE-2022-22965漏洞成因(三)

FrameworkServlet重写了initServletBean

```
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring " + this.getClass().getSimpleName() + " '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("Initializing Servlet '" + this.getServletName() + "'");
}

    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = this.initWebApplicationContext();
        this.initFrameworkServlet();
    } catch (RuntimeException | ServletException var4) {
        this.logger.error("Context initialization failed", var4);
        throw var4;
    }

    if (this.logger.isDebugEnabled()) {
        String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data";
        this.logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value);
    }

    if (this.logger.isInfoEnabled()) {
        this.logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
    }

}

```

FrameworkServlet的initServletBean调用this.initWebApplicationContext进行web容器的初始化

浅谈CVE-2022-22965漏洞成因(三)

FrameworkServlet的initWebApplicationContext方法主要用于初始化容器上下文

```
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}

                this.configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }

    if (wac == null) {
        wac = this.findWebApplicationContext();
    }

    if (wac == null) {
        wac = this.createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
        synchronized(this.onRefreshMonitor) {
            this.onRefresh(wac);
        }
    }

    if (this.publishContext) {
        String attrName = this.getServletContextAttributeName();
        this.getServletContext().setAttribute(attrName, wac);
    }

    return wac;
}

```

之后会调用this.onRefresh方法刷新web容器环境

浅谈CVE-2022-22965漏洞成因(三)
在FrameworkServlet中可以找到onRefresh方法会找到onRefresh并没有具体的实现过程

浅谈CVE-2022-22965漏洞成因(三)

对onRefresh方法ctrl+Alt+B直接查看它的具体实现类将进入DispatcherServlet,DispatcherServlet具体实现了FrameworkServlet中的onRefresh方法

浅谈CVE-2022-22965漏洞成因(三)

从这里开始就是Spring MVC框架的处理逻辑了,DispatcherServlet的initStrategies方法会进行各种组件的初始化,其中就有我们要找的与Mapping相关的初始化this.initHandlerMappings

浅谈CVE-2022-22965漏洞成因(三)
DispatcherServlet的initHandlerMappings

```
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException var3) {
}
}

    if (this.handlerMappings == null) {
        this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");
        }
    }

}

```

重点关注这里,handlerMappings从默认的资源列表中获取

浅谈CVE-2022-22965漏洞成因(三)

具体的看一下getDefaultStrategies

```
protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value == null) {
return new LinkedList();
} else {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List strategies = new ArrayList(classNames.length);
String[] var7 = classNames;
int var8 = classNames.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            String className = var7[var9];

            try {
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                Object strategy = this.createDefaultStrategy(context, clazz);
                strategies.add(strategy);
            } catch (ClassNotFoundException var13) {
                throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);
            } catch (LinkageError var14) {
                throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var14);
            }
        }

        return strategies;
    }
}

```

也就是说我们我们从默认定义的文件中获取handlerMapping并在最后通过反射去将handlerMapping加入到执行流程中

浅谈CVE-2022-22965漏洞成因(三)
DispatcherServlet的defaultStrategies是一个静态方法,最终会读取DispatcherServlet.properties这个资源文件

浅谈CVE-2022-22965漏洞成因(三)

浅谈CVE-2022-22965漏洞成因(三)

这也就是为什么我们前面提到的会有一些默认的handlerMapping

浅谈CVE-2022-22965漏洞成因(三)

浅谈CVE-2022-22965漏洞成因(三)

(由于篇幅原因,调试获取HandlerAdapte以及HandlerAdapte调用handle的过程将放在下篇)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月30日01:55:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅谈CVE-2022-22965漏洞成因(三)http://cn-sec.com/archives/1148879.html