All advisories
Draft

URI-Template Route Bypass via Path Parameters

envoyproxy/envoy

Affected packages

envoy other
Affected versions= c33d01624d5b173b5d6e5c0c0474d480cab69b7b
Patched versionsNot specified

Description

URI-Template Route Bypass via Path Parameters

Affected commit: c33d01624d
Sink: source/common/router/config_impl.cc:1416

Summary

When ignore_path_parameters_in_path_matching is enabled, URI-template routes match the raw :path instead of the sanitized path. A semicolon path parameter prevents a protected URI-template route from matching, causing evaluation to fall through to an unprotected fallback route.

Reproduce

#!/bin/bash
set -e

git clone --depth 1 https://github.com/envoyproxy/envoy.git /tmp/envoy-uritemplate
cd /tmp/envoy-uritemplate
echo "HEAD: $(git rev-parse HEAD)"

# Show that UriTemplateMatcherRouteEntryImpl::matches uses path() (raw)
sed -n '1412,1421p' source/common/router/config_impl.cc

# Show that path() returns raw headers_.getPathValue() without sanitization
sed -n '81,82p' source/common/router/config_impl.h

# Show that sanitizedPath() strips after semicolon (used by other matchers)
sed -n '91,97p' source/common/router/config_impl.h

# Show another route type (e.g. ConnectRouteEntryImpl) uses sanitized path
grep -n 'sanitizePathBeforePathMatching\|sanitizedPath' source/common/router/config_impl.cc | head -5

echo ""
echo "=== Vulnerable code confirmed at HEAD ==="
echo "config_impl.cc:1417: UriTemplateMatcherRouteEntryImpl::matches"
echo "  calls path_matcher_->match(route_match_context.path())."
echo "config_impl.h:81: path() returns headers_.getPathValue() (raw)."
echo "config_impl.h:93: sanitizedPath() strips after ';' when configured."
echo ""
echo "URI-template routes use path() not sanitizedPath(), so with"
echo "ignore_path_parameters_in_path_matching enabled, the router"
echo "sanitizes the path for route selection but URI-template matching"
echo "sees the raw path with the semicolon, causing a mismatch."
echo ""
echo "Attack: GET /api/v1/resource;bypass"
echo "  URI-template '/api/v1/resource' does NOT match '/api/v1/resource;bypass'"
echo "  Falls through to a less-protected fallback route."

rm -rf /tmp/envoy-uritemplate

Observed output (verified at c33d01624d):

HEAD: c33d01624d5b173b5d6e5c0c0474d480cab69b7b
RouteConstSharedPtr
UriTemplateMatcherRouteEntryImpl::matches(const RouteMatchContext& route_match_context,
                                          const StreamInfo::StreamInfo& stream_info,
                                          uint64_t random_value) const {
  if (RouteEntryImplBase::matchRoute(route_match_context, stream_info, random_value) &&
      path_matcher_->match(route_match_context.path())) {
    return clusterEntry(route_match_context.headers(), stream_info, random_value);
  }
  return nullptr;
}
  absl::string_view path() const { return headers_.getPathValue(); }

  absl::string_view sanitizedPath() const {
    if (!sanitized_path_computed_) {
      sanitized_path_ = ignore_path_params_ ? stripPathParams(path()) : path();
      sanitized_path_computed_ = true;
    }
    return sanitized_path_;
  }
source/common/router/config_impl.cc:815:RouteEntryImplBase::sanitizePathBeforePathMatching(const absl::string_view path) const {
source/common/router/config_impl.cc:1509:  ASSERT(path_matcher_->match(sanitizePathBeforePathMatching(path)));

=== Vulnerable code confirmed at HEAD ===
config_impl.cc:1417: UriTemplateMatcherRouteEntryImpl::matches
  calls path_matcher_->match(route_match_context.path()).
config_impl.h:81: path() returns headers_.getPathValue() (raw).
config_impl.h:93: sanitizedPath() strips after ';' when configured.

URI-template routes use path() not sanitizedPath(), so with
ignore_path_parameters_in_path_matching enabled, the router
sanitizes the path for route selection but URI-template matching
sees the raw path with the semicolon, causing a mismatch.

Attack: GET /api/v1/resource;bypass
  URI-template '/api/v1/resource' does NOT match '/api/v1/resource;bypass'
  Falls through to a less-protected fallback route.

Credit

Zheng Yu @ Depthfirst