Bug 573389 (CVE-2021-28169) - Jetty Utility Servlets Double Decoding Information Disclosure Vulnerability
Summary: Jetty Utility Servlets Double Decoding Information Disclosure Vulnerability
Status: RESOLVED FIXED
Alias: CVE-2021-28169
Product: Community
Classification: Eclipse Foundation
Component: Vulnerability Reports (show other bugs)
Version: unspecified   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: Security vulnerabilitied reported against Eclipse projects CLA
QA Contact:
URL:
Whiteboard:
Keywords: security
Depends on:
Blocks:
 
Reported: 2021-05-05 18:24 EDT by Steven Seeley CLA
Modified: 2021-06-08 22:12 EDT (History)
4 users (show)

See Also:


Attachments
triggering the vulnerability to leak the shiro.ini file (55.88 KB, image/png)
2021-05-05 18:24 EDT, Steven Seeley CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Steven Seeley CLA 2021-05-05 18:24:31 EDT
Created attachment 286329 [details]
triggering the vulnerability to leak the shiro.ini file

Please note: I was unable to open a case for Jetty because I was restricted as a new user. You might want to re-assign this bug to that team.

# Jetty Utility Servlets ConcatServlet Double Decoding Information Disclosure Vulnerability

Version: 10.2 (latest)
Download: https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-servlets/10.0.2
Found By: Steven Seeley of Qihoo 360 Vulcan team
Date: 29/4/2021

## Vulnerability Analysis

```java
/*     */   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*  88 */     String query = request.getQueryString();
/*  89 */     if (query == null) {
/*     */       
/*  91 */       response.sendError(204);
/*     */       
/*     */       return;
/*     */     } 
/*  95 */     List<RequestDispatcher> dispatchers = new ArrayList<RequestDispatcher>();
/*  96 */     String[] parts = query.split("\\&");
/*  97 */     String type = null;
/*  98 */     for (String part : parts) {
/*     */       
/* 100 */       String path = URIUtil.canonicalPath(URIUtil.decodePath(part)); // 1
/* 101 */       if (path == null) {
/*     */         
/* 103 */         response.sendError(404);
/*     */         
/*     */         return;
/*     */       } 
/*     */       
/* 108 */       if (startsWith(path, "/WEB-INF/") || startsWith(path, "/META-INF/")) { // 2
/*     */         
/* 110 */         response.sendError(404);
/*     */         
/*     */         return;
/*     */       } 
/* 114 */       String t = getServletContext().getMimeType(path);
/* 115 */       if (t != null)
/*     */       {
/* 117 */         if (type == null) {
/*     */           
/* 119 */           type = t;
/*     */         }
/* 121 */         else if (!type.equals(t)) {
/*     */           
/* 123 */           response.sendError(415);
/*     */           
/*     */           return;
/*     */         } 
/*     */       }
/* 128 */       RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(path); // 3
/* 129 */       if (dispatcher != null) {
/* 130 */         dispatchers.add(dispatcher);
/*     */       }
/*     */     } 
/* 133 */     if (type != null) {
/* 134 */       response.setContentType(type);
/*     */     }
/* 136 */     for (RequestDispatcher dispatcher : dispatchers)
/*     */     {
/* 138 */       dispatcher.include(request, response); // 4
/*     */     }
/*     */   }
```

At *[1]* the code does a url decode and then attempts to normalize the attacker supplied path. Then at *[2]* there is a check that the path doesn't start with "/WEB-INF/" or "/META-INF/". Later at *[3]* the RequestDispatcher is made and finally at *[4]* the `include` is triggered. 

The problem is that the check at *[2]* can be bypassed because the `RequestDispatcher` will also handle uri decoding. So an attacker that double url encodes a traversal or the `WEB-INF` string in their path which can instantiate a valid dispatcher and leak contents of an attacker controlled file from the ROOT of the web app.

## Impact 

The vulnerability is limited to a file disclosure from the webapp ROOT directory. However, in some contexts this may allow an attacker to escalate further. Let's use two examples:

1. Spring - Elevation of privilege/access

In this environment, it's possible to leak sensitive data from the `application.properties` file such as `spring.datasource.url`, `spring.elasticsearch.rest.password`, `spring.h2.console.settings.web-admin-password`, `spring.influx.password`, `spring.ldap.password`, etc

2. Apache Shiro - Remote Code Execution

In this environment, it's possible to leak the shiro.ini file which contains `securityManager.rememberMeManager.cipherKey`. This key can be used to gain remote code execution against the application via deserialization in the `rememberMe` cookie.

## Proof of Concept

Modify your web.xml to include the servlet:

```xml
  <servlet>
    <servlet-name>Concat</servlet-name>
    <servlet-class>org.eclipse.jetty.servlets.ConcatServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>Concat</servlet-name>
    <url-pattern>/concat</url-pattern>
  </servlet-mapping>
````

Now trigger the bug. Please note that the `js` directory doesn't need to exist:

```
GET /concat?/js/%252e%252e/WEB-INF/web.xml HTTP/1.1
Host: target
```

or, if you prefer to avoid traversals, because of ID$:

```
GET /concat?/%2557EB-INF/web.xml HTTP/1.1
Host: target
```

## Reference

- https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
- https://shiro.apache.org/configuration.html
Comment 1 Lachlan Roberts CLA 2021-05-06 20:42:34 EDT
For Jetty versions <= 9.4.40, <= 10.0.2, <= 11.0.2, it is possible for requests to the ConcatServlet with a doubly encoded path to access protected resources within the WEB-INF directory. For example a request to `/concat?/%2557EB-INF/web.xml` can retrieve the web.xml file. This can reveal sensitive information regarding the implementation of a web application.

CWEs
CWE-200

CVSS Score
5.3 Moderate
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

This will be resolved in Jetty 9.4.41, 10.0.3, and 11.0.3 which we are currently working on releases for.
Comment 2 Wayne Beaton CLA 2021-05-07 00:15:41 EDT
Let's use CVE-2021-28169.

Project committers: let me know when you're ready to push this.
Comment 3 Lachlan Roberts CLA 2021-05-23 19:48:53 EDT
We have discovered the vulnerability affects the WelcomeFilter as well. Here is an updated description.


For Jetty versions <= 9.4.40, <= 10.0.2, <= 11.0.2, it is possible for requests to the ConcatServlet and WelcomeFilter with a doubly encoded path to access protected resources within the WEB-INF directory. For example a request to the ConcatServlet with a path `/concat?/%2557EB-INF/web.xml` can retrieve the web.xml file. This can reveal sensitive information regarding the implementation of a web application.

Workarounds
If you cannot update to the latest version of Jetty, you can instead deploy your own version of the ConcatServlet and/or the WelcomeFilter by using the code from the latest version of Jetty.

CWEs
CWE-200

CVSS Score
5.3 Moderate
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

This is currently fixed in Jetty 9.4.41, 10.0.3, and 11.0.3.
Comment 4 Wayne Beaton CLA 2021-05-25 14:36:49 EDT
(In reply to Lachlan Roberts from comment #3)
> We have discovered the vulnerability affects the WelcomeFilter as well. Here
> is an updated description.

Is the project team ready to make this public?
Comment 5 Greg Wilkins CLA 2021-05-25 20:25:09 EDT
Wayne,

The release is out and is currently being announced without mention of the security bug.

For low/moderate CVEs our preference is to give a little time for normal updates before going public.  So can we go public Monday next week?
Comment 6 Wayne Beaton CLA 2021-06-08 12:00:48 EDT
> For low/moderate CVEs our preference is to give a little time for normal
> updates before going public.  So can we go public Monday next week?

Notwithstanding the maximum three months disclosure requirement, the timing is entirely up to you.

Are you ready to go?

Is there an advisory that you need me to push?
Comment 7 Lachlan Roberts CLA 2021-06-08 19:25:11 EDT
Wayne,

We are ready for this to go public.

The GitHub security advisory will need to be published as well:
https://github.com/eclipse/jetty.project/security/advisories/GHSA-gwcr-j4wh-j3cq
Comment 8 Wayne Beaton CLA 2021-06-08 22:12:58 EDT
I've pushed the report to the central authority.