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

admin 2022年6月30日01:58:09浅谈CVE-2022-22965漏洞成因(四)已关闭评论398 views字数 12505阅读41分41秒阅读模式

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

.

这篇文章四个部分:

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

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

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

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

.

调试篇

( 紧接" 浅谈CVE-2022-22965漏洞成因(三)”,继续调试Spring MVC框架的执行流程 )

5、调试获取HandlerAdapter的过程

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

浅谈CVE-2022-22965漏洞成因(四)
mappedHandler是一个处理器和拦截器的封装,调用HandlerExecutionChain的getHandler会把HandlerExecutionChain的Handler转成Object

浅谈CVE-2022-22965漏洞成因(四)
getHandlerAdapter最终会返回一个RequestMappingHandlerAdapter

浅谈CVE-2022-22965漏洞成因(四)
HandlerAdapter用于调用handler中的方法处理请求并返回一个ModelAndview,我们从handlerMappings获取到相应的handler后,都是通过HandlerAdapter来执行handler

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

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

程序执行时默认会初始化三个HandlerAdapter实现类(也就是我们上面提到)

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

浅谈CVE-2022-22965漏洞成因(四)
以及RequestMappingHandlerAdapter(@RequestMapping等注解标注专用),主要有两个功能:

  • 调用HttpMessageConverter、转换器、构造器将Http请求参数转换成java类型
  • 使用HandlerMethodArgumentResolver进行参数绑定

HandlerAdapters是HandlerAdapter的集合(参考前面的handlerMappings)

HandlerMapping返回给DispatcherServlet一个HandlerExcutionChain时,DispatcherServlet就会调用getHandlerAdapter方法遍历HandlerAdapters,并调用每个HandlerAdapter的support方法,判断具体使用哪个HandlerAdapter。

6、调试HandlerAdapte调用handle的过程

下面我们带着目的进行调试:对于前端传过来的参数Spring MVC 是如何绑定到控制器方法参数上的,即:

nick=isee&hobby[0]=travel&job=mbricks
=>
FirstController info(People people)
=>
pojo.People

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

浅谈CVE-2022-22965漏洞成因(四)
调用HandlerAdapter处理器适配器的handle方法,HandlerAdapter是一个接口,实际调用的是AbstractHandlerMethodAdapter的handle方法

浅谈CVE-2022-22965漏洞成因(四)
可以看到handel方法里传入的handel是一个 controller .FirstController#info(People),接着调用this.handleInternal方法进行处理,同时在这里将handel强转成一个HandlerMethod,也就是说@RequestMapping标记的方法最终都是HandlerMethod类型的

浅谈CVE-2022-22965漏洞成因(四)
对于HandlerMethod,形如@Controller、@RequestMapping这些注解标注的控制器方法最终的handeler是一个HandlerMethod

浅谈CVE-2022-22965漏洞成因(四)
HandlerMethod中存放了控制器类以及其对应的方法,也就是说获得一个HandlerMethod就可以调用我们定义的class FirstController里的处理方法,如welcome()或info(People people)

public class HandlerMethod {
protected final Log logger = LogFactory.getLog(this.getClass());
private final Object bean;
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
private HttpStatus responseStatus;
private String responseStatusReason;
private HandlerMethod resolvedFromHandlerMethod;
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;
...
}

handleInternal是一个抽象方法,接下来将调用其子类实现的handleInternal方法

浅谈CVE-2022-22965漏洞成因(四)
RequestMappingHandlerAdapter类实现了handleInternal方法,并调用invokeHandlerMethod方法进行处理,也就是说HandlerAdapter的handle方法最终是通过子类适配器通过实现handleInternal方法来实现的

浅谈CVE-2022-22965漏洞成因(四)
invokeHandlerMethod方法比较长,会进行返回值处理器以及请求响应封装,WebDataBinderFactory,ModelFactory,ModelAndViewContainer等的创建与配置,可大致归纳为:

```
......

将handlerMethod封装ServletInvocableHandlerMethod

510 ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod)

设置ServletInvocableHandlerMethod

包括配置已注册的参数解析列表、返回值处理器以及BinderFactory、ModelAndViewContainer相关
...

调用ServletInvocableHandlerMethod的invokeAndHandle方法

543 invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);

......
```

将handlerMethod封装ServletInvocableHandlerMethod,后面的许多操作都是通过ServletInvocableHandlerMethod来做的

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

其他配置项

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

.

在RequestMappingHandlerAdapter中执行invocableMethod.invokeAndHandle后进入ServletInvocableHandlerMethod。

.

在ServletInvocableHandlerMethod的invokeAndHandle方法中,有handel最终执行前参数解析与绑定和handel执行后返回值解析与处理的逻辑

```

51 Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);

...

68 this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);

```

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

我们这里关注它的 invokeForRequest方法

ServletInvocableHandlerMethod继承自InvocableHandlerMethod,ServletInvocableHandlerMethod调用this.invokeForRequest方法会调用到InvocableHandlerMethod的invokeForRequest方法

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

InvocableHandlerMethod.invokeForRequest方法调用this.getMethodArgumentValues方法

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

getMethodArgumentValues方法中会遍历参数解析器列表,实际上是一个HandlerHethodArgumentResolverComposite,并调用它们的supportsParameter判读参数类型与参数解析器是否匹配,最后通过resolveArgument方法进行参数处理,实际上都是基于HandlerMethodArgumentResolver接口来做的

```
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter var1);

@Nullable
Object resolveArgument(MethodParameter var1, @Nullable ModelAndViewContainer var2, NativeWebRequest var3, @Nullable WebDataBinderFactory var4) throws Exception;

}

```

我们看一下InvocableHandlerMethod.getMethodArgumentValues中这两个下断点的地方:

```

74 if (!this.resolvers.supportsParameter(parameter)) {

...

79 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)

```

#74 this.resolvers.supportsParameter(parameter)

调用HandlerMethodArgumentResolverComposite的supportsParameter方法

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

supportsParameter方法调用getArgumentResolver方法获得了一个ServletModelAttributemethodProcessor参数解析器

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

#79 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)

调用HandlerMethodArgumentResolverComposite的resolveArgument方法

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

HandlerMethodArgumentResolverComposite再次调用getArgumentResolver方法获得ServletModelAttributemethodProcessor参数解析器

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

此时的resolver为ServletModelAttributemethodProcessor

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

ServletModelAttributemethodProcessor继承自ModelAttributeMethodProcessor

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

调用ServletModelAttributemethodProcessor.resolveArgument方法会调用到ModelAttributeMethodProcessor.resolveArgument方法

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

重点关注如下地方:

```

62String name = ModelFactory.getNameForParameter(parameter)

89 WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name)

92 this.bindRequestParameters(binder, webRequest)

105 bindingResult = binder.getBindingResult()

```

#62 String name = ModelFactory.getNameForParameter(parameter)

#89 WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name)

#92 this.bindRequestParameters(binder, webRequest)

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

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

从这里开始才真正进入参数解析与绑定阶段

#92 this.bindRequestParameters(binder, webRequest)

ServletModelAttributeMethodProcessor.bindRequestParameters具体实现lModelAttributeMethodProcessor.bindRequestParameters方法,之后调用ServletRequestDataBinder.bind方法

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

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

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

ServletRequestDataBinder.bind方法里开始获取并设置请求参数并执行this.doBind方法(父类WebDataBinder的doBind方法)

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

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

WebDataBinder的doBind方法,请求参数的信息之前已经被存储在PropertyValue和MutablePropertyValues相关类中

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

doBind之前检查请求参数是否合法

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

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

调用父类DataBinder的doBind方法,两次参数检查后调用this.applyPropertyValues方法

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

DataBinder.applyPropertyValues方法

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

调用this.getPropertyAccessor方法获取的PropertyAccessor是ConfigurablePropertyAccessor类型,该类的父类是PropertyAccessor

setPropertyValues方法是一个抽象方法,setPropertyValues方法具体的实现类为AbstractPropertyAccessor,因此DataBinder调用this.getPropertyAccessor().setPropertyValues最终会执行到AbstractPropertyAccessor.setPropertyValues方法

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

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

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

从这里起,将开始遍历参数并一个一个进行处理

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

在AbstractPropertyAccessor.setPropertyValues方法,调用this.setPropertyValue方法,该方法的具体实现类在AbstractNestablePropertyAccessor

浅谈CVE-2022-22965漏洞成因(四)
进入AbstractNestablePropertyAccessor,AbstractNestablePropertyAccessor开始每个参数的解析

浅谈CVE-2022-22965漏洞成因(四)
下断点的这两个地方非常重要,一个用来解析前端传过来的参数是否是递归的形式,一个用来对Bean属性赋值(这里是pojo.people)

```

153 nestedPa = this.getPropertyAccessorForPropertyPath(propertyName)

163 nestedPa.setPropertyValue(tokens, pv)

```

#153 nestedPa = this.getPropertyAccessorForPropertyPath(propertyName)

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

如果参数是hobby1.hobby2.hobby3=eat这种形式,将会进入pos的逻辑

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

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

#163 nestedPa.setPropertyValue(tokens, pv)

我们接着看看对于数组类型的参数,它是如何赋值的

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

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

AbstractNestablePropertyAccessor.processKeyedProperty

该方法里具体实现了根据请求参数获取Bean属性以及设置Bean属性的逻辑,对于数组类型的Bean属性,这里是直接通过数组底层赋值进行设置的,并不是通过反射调用Bean的Setter方法来进行设置

```

获取对应参数的Bean的实际属性值

180 Object propValue = this.getPropertyHoldingValue(tokens)

获取PropertyHandler用来操作Bean属性

181 AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName)

参数到Bean属性的类型转换

200 convertedValue = this.convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(), requiredType, ph.nested(tokens.keys.length))

```

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

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

#180 Object propValue = this.getPropertyHoldingValue(tokens)

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

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

AbstractNestablePropertyAccessor的getLocalPropertyHandler是一个接口,BeanWrapperImpl实现了该接口方法

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

PropertyDescriptor是通过CachedIntrospectionResults获取的,BeanWrapperImpl.getLocalPropertyHandler获取PropertyDescriptor后把它转成了一个BeanWrapperImpl.BeanPropertyHandler类型

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

BeanWrapperImpl.BeanPropertyHandler继承自AbstractNestablePropertyAccessor.PropertyHandler,里面的getValue/setValue具体实现了操作Bean属性的过程

AbstractNestablePropertyAccessor类下的PropertyHandler

```
protected abstract static class PropertyHandler {
private final Class<?> propertyType;
private final boolean readable;
private final boolean writable;

    public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
        this.propertyType = propertyType;
        this.readable = readable;
        this.writable = writable;
    }

    public Class<?> getPropertyType() {
        return this.propertyType;
    }

    public boolean isReadable() {
        return this.readable;
    }

    public boolean isWritable() {
        return this.writable;
    }

    public abstract TypeDescriptor toTypeDescriptor();

    public abstract ResolvableType getResolvableType();

    @Nullable
    public Class<?> getMapKeyType(int nestingLevel) {
        return this.getResolvableType().getNested(nestingLevel).asMap().resolveGeneric(new int[]{0});
    }

    @Nullable
    public Class<?> getMapValueType(int nestingLevel) {
        return this.getResolvableType().getNested(nestingLevel).asMap().resolveGeneric(new int[]{1});
    }

    @Nullable
    public Class<?> getCollectionType(int nestingLevel) {
        return this.getResolvableType().getNested(nestingLevel).asCollection().resolveGeneric(new int[0]);
    }

    @Nullable
    public abstract TypeDescriptor nested(int var1);

    @Nullable
    public abstract Object getValue() throws Exception;

    public abstract void setValue(@Nullable Object var1) throws Exception;
}

```

BeanWrapperImpl类

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

BeanWrapperImpl是BeanWrapperI的实现类,BeanWrapperI是Bean的包装类,BeanWrapperImpl通过PropertyDescriptor(实际上是通过Spring自己的PropertyDescriptor)具体实现了Bean属性的获取和读取,BeanWrapperImpl获取或修改Bean的Property最终是通过反射调用Bean自己的getter/setter方法实现

```
private class BeanPropertyHandler extends PropertyHandler {
private final PropertyDescriptor pd;

    public BeanPropertyHandler(PropertyDescriptor pd) {
        super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null);
        this.pd = pd;
    }

    public ResolvableType getResolvableType() {
        return ResolvableType.forMethodReturnType(this.pd.getReadMethod());
    }

    public TypeDescriptor toTypeDescriptor() {
        return new TypeDescriptor(BeanWrapperImpl.this.property(this.pd));
    }

    @Nullable
    public TypeDescriptor nested(int level) {
        return TypeDescriptor.nested(BeanWrapperImpl.this.property(this.pd), level);
    }

    @Nullable
    public Object getValue() throws Exception {
        Method readMethod = this.pd.getReadMethod();
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(() -> {
                ReflectionUtils.makeAccessible(readMethod);
                return null;
            });

            try {
                return AccessController.doPrivileged(() -> {
                    return readMethod.invoke(BeanWrapperImpl.this.getWrappedInstance(), (Object[])null);
                }, BeanWrapperImpl.this.acc);
            } catch (PrivilegedActionException var3) {
                throw var3.getException();
            }
        } else {
            ReflectionUtils.makeAccessible(readMethod);
            return readMethod.invoke(BeanWrapperImpl.this.getWrappedInstance(), (Object[])null);
        }
    }

    public void setValue(@Nullable Object value) throws Exception {
        Method writeMethod = this.pd instanceof GenericTypeAwarePropertyDescriptor ? ((GenericTypeAwarePropertyDescriptor)this.pd).getWriteMethodForActualAccess() : this.pd.getWriteMethod();
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(() -> {
                ReflectionUtils.makeAccessible(writeMethod);
                return null;
            });

            try {
                AccessController.doPrivileged(() -> {
                    return writeMethod.invoke(BeanWrapperImpl.this.getWrappedInstance(), value);
                }, BeanWrapperImpl.this.acc);
            } catch (PrivilegedActionException var4) {
                throw var4.getException();
            }
        } else {
            ReflectionUtils.makeAccessible(writeMethod);
            writeMethod.invoke(BeanWrapperImpl.this.getWrappedInstance(), value);
        }

    }
}

```

可以看出PropertyHandler实际就是操作Property的封装

获取到ph之后要再获取People的hobby属性的值就好办了,先判断hobby属性是否可读,可读直接ph.getValue拿值

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

浅谈CVE-2022-22965漏洞成因(四)
#181 AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName)

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

浅谈CVE-2022-22965漏洞成因(四)
#200 convertedValue = this.convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(), requiredType, ph.nested(tokens.keys.length))

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

浅谈CVE-2022-22965漏洞成因(四)
对于数组类型的变量,之后直接调用底层赋值,第一个参数解析与绑定完成

浅谈CVE-2022-22965漏洞成因(四)
第二参数解析与绑定开始

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

与第一个参数不同,此时的tokens.keys是一个null值,程序走的是this.processLocalProperty这个流程

浅谈CVE-2022-22965漏洞成因(四)
对于AbstractNestablePropertyAccessor.processLocalProperty这个方法,正常的执行流程是

```

获取PropertyHandler

295 AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName)

获取Bean属性原始值

309 oldValue = ph.getValue()

参数类型转换

valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor())

对Bean赋值

ph.setValue(valueToApply)

```

但是由于People的job属性不可写,后面会直接异常捕捉并退出

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

最后进行第三个参数解析

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

此时的tokens.keys是一个null值,程序走的是this.processLocalProperty这个流程,之所以这样,可能是AbstractNestablePropertyAccessor.getPropertyNameTokens负责数组类型变量的多次解析因此给了个tokens值

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

再后面就很顺利了,AbstractNestablePropertyAccessor.processLocalProperty正常执行

```

获取PropertyHandler

295 AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName)

获取Bean属性原始值

309 oldValue = ph.getValue()

参数类型转换

valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor())

对Bean赋值

ph.setValue(valueToApply)

```

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

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

参数绑定完成后就是返回值封装与视图解析的过程,即返回ModelAndview并由DispatcherServlet调用ViewReslver作视图解析

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

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

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

这里我们就不作过多讨论,另外就是参数处理器调用resolveArgument处理请求时通常用这个接口进行HTTP参数到java类型的转换

.

最后再通过转换器或是格式化器就能将请求参数转为更加丰富类型

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

综上,我们对于Spring MVC的参数绑定过程有了一定认识,以及Spring MVC框架的变量覆盖BUG的成因有了一定的了解。即,对于数组类型的参数在绑定到pojo属性时,并不是通过getter/setter方法,而是直接进行了数组底层赋值。

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