Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore bad percent encodings in paths during URIUtil.equalsIgnoreEncodings() #4033

Closed
wilkinsona opened this issue Aug 28, 2019 · 16 comments
Closed
Assignees
Labels
Bug For general bugs on Jetty side High Priority Sponsored This issue affects a user with a commercial support agreement

Comments

@wilkinsona
Copy link
Contributor

The problem is illustrated by the following test:

@Test
public void equalsIgnoreEncodingsHandlesPercentCorrectly() {
	assertThat(URIUtil.equalsIgnoreEncodings("test%25path", "test%path")).isTrue();
}

It fails with a NumberFormatException:

java.lang.NumberFormatException: !hex p
	at org.eclipse.jetty.util.TypeUtil.convertHexDigit(TypeUtil.java:373)
	at org.eclipse.jetty.util.URIUtil.equalsIgnoreEncodings(URIUtil.java:1028)
	…

I discovered this while investigating this Spring Boot issue to see if Jetty was affected in any way as well. A more user-facing symptom of the problem in URIUtil or TypeUtil is, I believe, that a static resource in META-INF/resources that includes certain reserved characters in its name will be inaccessible.

@joakime joakime self-assigned this Aug 28, 2019
@joakime joakime added the Bug For general bugs on Jetty side label Aug 28, 2019
@joakime
Copy link
Contributor

joakime commented Aug 28, 2019

Good catch. I'll submit a PR for this shortly.

joakime added a commit that referenced this issue Aug 28, 2019
+ Allows for preserving decoded Strings like "X%YZ"

Signed-off-by: Joakim Erdfelt <[email protected]>
@joakime
Copy link
Contributor

joakime commented Aug 28, 2019

Opened PR #4034 to address this with a lenient percent decoding solution.
This will leave bad sequences alone when decoding.

joakime added a commit that referenced this issue Aug 28, 2019
joakime added a commit that referenced this issue Aug 28, 2019
@joakime
Copy link
Contributor

joakime commented Aug 28, 2019

@wilkinsona we have a fix in place on PR #4034 that seems to address the issue you are encountering.
But without a longer stack, I can't tell where specifically your usage of URIUtil.equalsIgnoreEncodings(String,String) is coming from.

In our codebase, that method only used in test cases and FileResource which deprecated and not used by anything internally in Jetty anymore.
You could be using the FileResource improperly in your code, when you should be using it's replacement PathResource (which solves for many of these kinds of encoding issues).

@wilkinsona
Copy link
Contributor Author

wilkinsona commented Aug 29, 2019

@joakime Thanks for taking a look at this so quickly. Sorry for the context that was lost in the overly minimal example of the problem. When the problem occurs in Jetty proper, equalsIgnoreEncodings(String, String) is being called via equalsIgnoreEncodings(URI, URI). Here's a full stack of the NumberFormatException in that case:

java.lang.NumberFormatException: !hex &
	at org.eclipse.jetty.util.TypeUtil.convertHexDigit(TypeUtil.java:373)
	at org.eclipse.jetty.util.URIUtil.equalsIgnoreEncodings(URIUtil.java:1023)
	at org.eclipse.jetty.util.URIUtil.equalsIgnoreEncodings(URIUtil.java:1068)
	at org.eclipse.jetty.util.resource.PathResource.checkAliasPath(PathResource.java:74)
	at org.eclipse.jetty.util.resource.PathResource.<init>(PathResource.java:217)
	at org.eclipse.jetty.util.resource.PathResource.addPath(PathResource.java:300)
	at org.eclipse.jetty.util.resource.ResourceCollection.addPath(ResourceCollection.java:265)
	at org.eclipse.jetty.server.handler.ContextHandler.getResource(ContextHandler.java:1913)
	at org.eclipse.jetty.webapp.WebAppContext.getResource(WebAppContext.java:407)
	at org.eclipse.jetty.servlet.DefaultServlet.getResource(DefaultServlet.java:430)
	at org.eclipse.jetty.server.ResourceContentFactory.getContent(ResourceContentFactory.java:62)
	at org.eclipse.jetty.server.ResourceService.doGet(ResourceService.java:235)
	at org.eclipse.jetty.servlet.DefaultServlet.doGet(DefaultServlet.java:457)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:645)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:876)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1623)
	at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:214)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1610)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:540)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1711)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1347)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:480)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1678)
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1249)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
	at org.eclipse.jetty.server.Server.handle(Server.java:505)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:370)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:267)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:781)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:917)
	at java.lang.Thread.run(Thread.java:748)

The call to equalsIgnoreEncodings(String,String) is reached because the input URIs are not equal:

file:///private/var/folders/jj/5zwjflp915gg3gyqdkgqwnf40000gn/T/jetty-docbase.2190897992002719274.0/nested-reserved-!%23$%25&%27()*+,/:%3B=%3F@%5B%5D-meta-inf-resource.txt
file:///private/var/folders/jj/5zwjflp915gg3gyqdkgqwnf40000gn/T/jetty-docbase.2190897992002719274.0/nested-reserved-!%23$%25&'()*+,/:;=%3F@%5B%5D-meta-inf-resource.txt

The ' and ; characters are encoded in the first URI but not in the second.

@joakime
Copy link
Contributor

joakime commented Aug 29, 2019

Oh, wow, cool test.

What does that file name (and parent path with nested-reserved-....) look like on disk, from a ls -la perspective?
I need to know which characters are stored as-is, and which are escaped by the java.nio.files.FileSystem.
Meanwhile, I'll merge the current lenient decode PR, and work on a new PR that tests the PathResource better (using similar techniques like i'm seeing in your updated comment)

@wilkinsona
Copy link
Contributor Author

What does that file name (and parent path with nested-reserved-....) look like on disk, from a ls -la perspective?

In this particular case, the file isn't on disk. It's in META-INF/resources of a jar that's on the classpath. The code in Jetty that looks for that isn't being reached due to the NumberFormatException that's thrown when looking for the file on disk beneath the doc base.

joakime added a commit that referenced this issue Aug 29, 2019
…ignoreencodings

Issue #4033 - Add Lenient percent decode in URIUtil
@joakime joakime changed the title URIUtil equalsIgnoreEncodings fails with a NumberFormatException for paths containing a % that is not the beginning of an encoded character Lenient decode of bad percent encodings in URIUtil Aug 29, 2019
@joakime
Copy link
Contributor

joakime commented Aug 29, 2019

@wilkinsona there's a 9.4.21-SNAPSHOT build available with these Lenient decode changes at the standard Jetty snapshot repository.

https://oss.sonatype.org/content/repositories/jetty-snapshots/

Can you give it whirl on your tests?

@wilkinsona
Copy link
Contributor Author

I've tried the 9.4.21 snapshot and the changes look good to me, @joakime. Thanks!

@joakime
Copy link
Contributor

joakime commented Aug 29, 2019

Thanks for the feedback!
I'll consider this complete then.

@joakime joakime closed this as completed Aug 29, 2019
@joakime
Copy link
Contributor

joakime commented Aug 29, 2019

@wilkinsona this fix is on the schedule for the Jetty 9.4.21 release.

@joakime joakime changed the title Lenient decode of bad percent encodings in URIUtil Ignore bad percent encodings in paths during URIUtil.equalsIgnoreEncodings() Sep 16, 2019
@gregw gregw reopened this Nov 5, 2019
@gregw
Copy link
Contributor

gregw commented Nov 5, 2019

I am worried that this fix has gone to far.

Previously a request for /%foo/bar% would result in a 400 bad request. However we are now being lenient on the original decode so that such requests are allowed into the container! The typically receive 404 responses... but can get 200s depending on the handler. I think this is a very unintended consequence of fixing a NPE in equalsIgnoreEncodings

@gregw gregw added High Priority Sponsored This issue affects a user with a commercial support agreement labels Nov 5, 2019
@joakime
Copy link
Contributor

joakime commented Nov 5, 2019

URIUtil.equalsIgnoreEncodings() only applies to alias checking on PathResource and FileResource.

gregw added a commit that referenced this issue Nov 5, 2019
Added test to demonstrate bad percent encoded request

Signed-off-by: Greg Wilkins <[email protected]>
joakime added a commit that referenced this issue Nov 5, 2019
joakime added a commit that referenced this issue Nov 5, 2019
joakime added a commit that referenced this issue Nov 5, 2019
Signed-off-by: Joakim Erdfelt <[email protected]>
joakime added a commit that referenced this issue Nov 5, 2019
gregw added a commit that referenced this issue Nov 5, 2019
reverted decodePathBehaviour

Signed-off-by: Greg Wilkins <[email protected]>
@joakime
Copy link
Contributor

joakime commented Nov 5, 2019

Opened PR #4272

gregw added a commit that referenced this issue Nov 6, 2019
updates after review

Signed-off-by: Greg Wilkins <[email protected]>
gregw added a commit that referenced this issue Nov 11, 2019
* Modernizing testcase

Signed-off-by: Joakim Erdfelt <[email protected]>

* Issue #4033 Percent Encoded Bad Requests

Added test to demonstrate bad percent encoded request

Signed-off-by: Greg Wilkins <[email protected]>

* Issue #4033 - adding sanity test for percent paths and checkAlias()

Signed-off-by: Joakim Erdfelt <[email protected]>

* Eliminating 9.3.0.RC0 dependency

Signed-off-by: Joakim Erdfelt <[email protected]>

* Issue #4033 - More tests for Resource checkAlias() behavior

Signed-off-by: Joakim Erdfelt <[email protected]>

* Issue #4033 - Splitting badDecodePath

Signed-off-by: Joakim Erdfelt <[email protected]>

* Issue #4033 - More badDecodePath tests

Signed-off-by: Joakim Erdfelt <[email protected]>

* Issue #4033 Percent Encoded Bad Requests

reverted decodePathBehaviour

Signed-off-by: Greg Wilkins <[email protected]>

* testing pull request building

* Issue #4033

updates after review

Signed-off-by: Greg Wilkins <[email protected]>
@gregw gregw closed this as completed Nov 11, 2019
justinstoller added a commit to justinstoller/trapperkeeper-webserver-jetty9 that referenced this issue May 18, 2020
This updates us to the newest Jetty verion.

This required two changes. One was test changes around CVE-2016-2785 and
the other involves our custom SslContextFactory.

For background on CVE-2016-2785, see
[TK-343](https://tickets.puppetlabs.com/browse/TK-343) about how certain
percent encodings could be used to create a directory traversal attack.

In [#4033](jetty/jetty.project#4033), the
Jetty maintainers, in response to a user who got an NPE when attempting
to serve files from his jar by percent encoding a jar path, loosened the
percent encoding rules. Quickly after merging those loosened validation
rules, it was reverted and the rules were additionally tightened up. As
part of that tightening, some of the dangerous percent encodings Jetty
used to let through (and we explicitly tested received an error from our
middleware) no longer are allowed. Despite removing the assertion that
we see the specific error message from our middleware we still check
that the directory attacks receive a 404 and the primary handler never
sees the request.

Previously, Jetty deprecated the SslContextFactory that we extend to
provide custom CRL reloading. It also moved the
endpointIdentificationAlgorithm setting to specialized inner classes,
either SslContextFactory.Server or SslContextFactory.Client depending on
what defaults are required. With the latest updates attempting to extend
the deprecated SslContextFactory raises and we must now extend the inner
class SslContextFactory.Server. As a side effect we now no longer need
to manually set the endpointIdentificationAlgorithm.
justinstoller added a commit to justinstoller/trapperkeeper-webserver-jetty9 that referenced this issue May 18, 2020
This updates us to the newest Jetty verion.

This required two changes. One was test changes around CVE-2016-2785 and
the other involves our custom SslContextFactory.

For background on CVE-2016-2785, see
[TK-343](https://tickets.puppetlabs.com/browse/TK-343) about how certain
percent encodings could be used to create a directory traversal attack.

In [#4033](jetty/jetty.project#4033), the
Jetty maintainers, in response to a user who got an NPE when attempting
to serve files from his jar by percent encoding a jar path, loosened the
percent encoding rules. Quickly after merging those loosened validation
rules, it was reverted and the rules were additionally tightened up. As
part of that tightening, some of the dangerous percent encodings Jetty
used to let through (and we explicitly tested received an error from our
middleware) no longer are allowed. Despite removing the assertion that
we see the specific error message from our middleware we still check
that the directory attacks receive a 404 and the primary handler never
sees the request.

Previously, Jetty deprecated the SslContextFactory that we extend to
provide custom CRL reloading. It also moved the
endpointIdentificationAlgorithm setting to specialized inner classes,
either SslContextFactory.Server or SslContextFactory.Client depending on
what defaults are required. With the latest updates attempting to extend
the deprecated SslContextFactory raises and we must now extend the inner
class SslContextFactory.Server. As a side effect we now no longer need
to manually set the endpointIdentificationAlgorithm.
@wilkinsona
Copy link
Contributor Author

There seems to have been a change in this area in Jetty 10 or 11. Before opening a new issue, I thought it best to mention it here to hopefully learn if it's intentional. A test that tries to access a static resource named nested-reserved-!%23$%25&%27()*+,/:%3B=%3F@%5B%5D-meta-inf-resource.txt that passed with Jetty 9 (200 response) is failing with Jetty 11 (400 response).

@joakime
Copy link
Contributor

joakime commented Nov 23, 2021

What is the static filename (in the raw, without URI encoding/decoding) on your filesystem (be it a real filesystem, network filesystem, or jar/zip filesystem)

Is it nested-reserved-!#$%&'()*+,/:;=?@[]-meta-inf-resource.txt ?

I use this tiny project to test encoding/decoding and static resources ...
https://github.com/joakime/maniacal-servlet-encoding

So far, I can access all of the content in the jar file.

@wilkinsona
Copy link
Contributor Author

wilkinsona commented Nov 23, 2021

Thanks, @joakime. The file's in a jar file and it's named META-INF/resources/nested-reserved-!#\$%&()*+,:=?@[]-meta-inf-resource.txt. I'm working on a branch of Spring Boot that upgrades to Jakarta EE 9 and, among many other things, Jetty 11 is part of that. Now that I know that this should still work, let me dig a bit and see if I can pinpoint what's changed. I'll open a new issue with a reproducer once I've confirmed that something in Jetty is the cause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side High Priority Sponsored This issue affects a user with a commercial support agreement
Projects
None yet
Development

No branches or pull requests

3 participants