Date: August 14, 2018
Chinese Version: https://paper.seebug.org/665/

On April 5, 2018, Pivotal released a Directory Traversal vulnerability in Spring MVC (CVE-2018-1271). Spring Framework versions 5.0 to 5.0.4, 4.3 to 4.3.14, and older unsupported versions allow applications to configure Spring MVC to serve static resources (e.g. CSS, JS, images). When static resources are served from a file system on Windows (as opposed to the classpath, or the ServletContext), a malicious user can send a request using a specially crafted URL that can lead a directory traversal attack.

### Vulnerability Impact

• Spring Framework 5.0 to 5.0.4.
• Spring Framework 4.3 to 4.3.14
• Older unsupported versions are also affected

### Exploit Conditions

• Server runs on a Windows system
• To open the resource file directory using the file protocol

### Vulnerability Recurrence

#### Environment Construction

git clone https://github.com/spring-projects/spring-mvc-showcase.git

Modify pom.xml to use Spring Framework 5.0.0.

2.Modify Spring MVC static resource configuration. You can refer to official documentation

According to the official documentation, there are two ways to configure it. Add a new resource file path here by overriding the addResourceHandlers method in WebMvcConfigurer. Add the following code to org.springframework.samples.mvc.config.WebMvcConfig. It uses the file:// protocol to specify resources as the static file directory.

  registry.addResourceHandler("/resources/**").addResourceLocations("file:./src/main/resources/","/resources/");

3.Start the Project with Jetty

Mvn jetty:run

Now the environment has been completed.

#### Recurring and the Results

http://localhost:8080/spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/..%255c/windows/win.ini

You can see the contents of the win.ini successfully read.

### Vulnerability Analysis

When the external access to the static resource, it will call org.springframework.web.servlet.resource.ResourceHttpRequestHandler:handleRequest to handle the breakpoint debugging here.

Follow up with org.springframework.web.servlet.resource.ResourceHttpRequestHandler:getResource().

The path saved in request is /spring-mvc-showcase/resources/%255c%255c..%255c/..%255c/..%255c/..%255c/..%255c/.. %255c/..%255c/..%255c/..%255c/windows/win.ini. The url decode operation is performed when the request.getAttribute() function runs. The path is %5c%5c..%5c/..%5c/..%5c/..%5c /..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini. Next, the path will be checked twice, and the decoded values of path and path will be checked by the isInvalidPath function. Let's look at this function.

When path contains .., the cleanPath function is called to handle path. Follow up with:

The purpose of this function is to convert this relative path containing .. into an absolute path. For example, /foo/bar/../ will become /foo/ after being processed by cleanPath.

The problem with cleanPath is String[] pathArray = delimitedListToStringArray(pathToUse, "/");. This allows empty elements to exist, that is, cleanPath will treat // as a directory, while the operating system does not. Quote an image from @Orange.

The above said that the path will be checked twice. When first calling isInvalidPath, the path is %5c%5c..%5c/..%5c/..% 5c/..%5c/..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini. Since there is no element equals to .. after path is separated by /, the path will not change after being processed by cleanPath. In the next judgment, the path does not contain ../, so it finally returns false, which means it passes the check.

When calling isInvalidPath(URLDecoder.decode(path, "UTF-8")) for the second time, the parameter is \\..\/..\/..\/..\/..\/ ..\/..\/..\/..\/windows/win.ini. Its value after being processed by cleanPath is //windows/win.ini. In the next judgment, the path does not contain ../. It finally returns false and also passes the check.

Continue and get a Resource object.

The path has not changed. getLocations() gets the path file:./src/main/resources/ that was previously configured in the configuration file. Follow up.

Follow the resolveResource of the ResourceResolver class.

Follow the resolveResourceInternal of PathResourceResolver.

Go to getResource() of org.springframework.web.servlet.resource.PathResourceResolver.

At this time, resourcePath is the previous path, and location is the value obtained by getLocations(). Continue to follow up with this.getResource.

Call location.createRelativ to get the absolute path of the file and return a UrlResource object.

Return to the getResource function.

Here resource is a UrlResource object, and you can see its value is file:src/main/resources/%5c%5c..%5c/..%5c/..%5c/..%5c /..%5c/..%5c/..%5c/..%5c/..%5c/windows/win.ini. Then call exists() method to check if the file exists, and call isReadable () method to detect whether the file is readable. Go into the exists() method.

Here we will call isFileURL to determine whether the url reads the file with the file:// protocol, which is why the file:// protocol is used when configuring the static directory.

After the judgment, this.getFile() will be called to get this file object. This method is in the method class of org.springframework.util.ResourceUtils. Follow up with:

Here is the judgment of whether it is the file:// protocol, and then new File(toURI(resourceUrl).getSchemeSpecificPart()); to convert resourceUrl to a URL object. Finally call getSchemeSpecificPart() of the URI class to get the file path. There is a decode operation in getSchemeSpecificPart(), which is to decode %5c into \. Follow up with:

It finally returns to exists() and returns true, which means file exists.

Later when calling the isReadable() method to check if the file is readable, the getFile will also be called. It will return true, which means the file is readable.

At this point, the judgment for resource is over and we will go back to org.springframework.web.servlet.resource.ResourceHttpRequestHandler:handleRequest(). After getting resource that has passed the check, we should start preparing the response content, including getting the type of the file (for the content of response) -type), the size of the file (for Content-length of response), etc.. Finally call this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage); to get the contents of the file and return it to the user.

Follow up with write().

Follow up with writeInternal and jump to writeContent.

Follow up with resource.getInputSream()

As you can see, here you create a URLConnection instance with openConnection. In the openConnection method, it decodes %5c into \ and returns the InputStream object of the file decode . And it finnaly reads the content and returns it to the user.

### Precautions

1. This vulnerability can be triggered under Tomcat because of the double URL encoding of the payload.

2. In the Spring Framework version greater than 5.0.1 (my test environment is 5.0.4), double URL encoding payload will not work, but a single URL encoded payload works. In this case the vulnerability can not be triggered under Tomcat, because by default when Tomcat encounters a URL containing %2f(/) and %5c(\), it directly returns http400, which can be triggered under jetty.

As for why double URL encoding is not working, it is because getResource() of org.springframework.web.servlet.resource.PathResourceResolver has an extra encode operation.

If it is a double URL encoding payload, decode it once when getting path. After the isInvalidPath judgment, then enter the getResource() of PathResourceResolver as the picture shows. There it will be coded again and returns to double encoding. Finally, when the file determines whether the exists() method exists, getSchemeSpecificPart() can only be decoded once, and then the file cannot be read, which means the file does not exist.

So here you have to use a single encoding.

### Patch Analysis

Look at the official patches which is rewritten up by processPath in getResource() of ResourceHttpRequestHandler.

Call function processPath to process path before entering isInvalidPath. Replace the backslash with a slash, and remove the extra slash, prevent it from passing the check in isInvalidPath. If you use the double encoding method, it will pass isInvalidEncodedPath, which will first decode path, then call processPath, and finally pass isInvalidPath, which will not pass the check either.

### Mitigation

• 5.0.x users should upgrade to 5.0.5
• 4.3.x users should upgrade to 4.3.15
• Older versions should upgrade to a supported branch

# About Knownsec & 404 Team

Beijing Knownsec Information Technology Co., Ltd. was established by a group of high-profile international security experts. It has over a hundred frontier security talents nationwide as the core security research team to provide long-term internationally advanced network security solutions for the government and enterprises.

Knownsec's specialties include network attack and defense integrated technologies and product R&D under new situations. It provides visualization solutions that meet the world-class security technology standards and enhances the security monitoring, alarm and defense abilities of customer networks with its industry-leading capabilities in cloud computing and big data processing. The company's technical strength is strongly recognized by the State Ministry of Public Security, the Central Government Procurement Center, the Ministry of Industry and Information Technology (MIIT), China National Vulnerability Database of Information Security (CNNVD), the Central Bank, the Hong Kong Jockey Club, Microsoft, Zhejiang Satellite TV and other well-known clients.

404 Team, the core security team of Knownsec, is dedicated to the research of security vulnerability and offensive and defensive technology in the fields of Web, IoT, industrial control, blockchain, etc. 404 team has submitted vulnerability research to many well-known vendors such as Microsoft, Apple, Adobe, Tencent, Alibaba, Baidu, etc. And has received a high reputation in the industry.

The most well-known sharing of Knownsec 404 Team includes: KCon Hacking Conference, Seebug Vulnerability Database and ZoomEye Cyberspace Search Engine.