Описание
lxd's non-recursive certificate listing bypasses per-object authorization and leaks all fingerprints
Summary
The GET /1.0/certificates endpoint (non-recursive mode) returns URLs containing fingerprints for all certificates in the trust store, bypassing the per-object can_view authorization check that is correctly applied in the recursive path. Any authenticated identity — including restricted, non-admin users — can enumerate all certificate fingerprints, exposing the full set of trusted identities in the LXD deployment.
Affected Component
lxd/certificates.go—certificatesGet(lines 185–192) — Non-recursive code path returns unfiltered certificate list.
CWE
- CWE-862: Missing Authorization
Description
Core vulnerability: missing permission filter in non-recursive listing path
The certificatesGet handler obtains a permission checker at line 143 and correctly applies it when building the recursive response (lines 163-176). However, the non-recursive code path at lines 185-192 creates a fresh loop over the unfiltered baseCerts slice, completely bypassing the authorization check:
Inconsistency with other list endpoints confirms the bug
Five other list endpoints in the same codebase correctly filter results in both recursive and non-recursive paths:
| Endpoint | File | Filters non-recursive? |
|---|---|---|
| Instances | lxd/instances_get.go — instancesGet | Yes — filters before either path |
| Images | lxd/images.go — doImagesGet | Yes — checks hasPermission for both paths |
| Networks | lxd/networks.go — networksGet | Yes — filters outside recursion check |
| Profiles | lxd/profiles.go — profilesGet | Yes — separate filter in non-recursive path |
| Certificates | lxd/certificates.go — certificatesGet | No — unfiltered |
The certificates endpoint is the sole outlier, confirming this is an oversight rather than a design choice.
Access handler provides no defense
The endpoint uses allowAuthenticated as its AccessHandler (certificates.go:45), which only checks requestor.IsTrusted():
The comment explicitly states that allowAuthenticated should be "used in conjunction with further access control within the handler" — which the non-recursive path fails to do.
Execution chain
- Restricted authenticated user sends
GET /1.0/certificates(norecursionparameter) allowAuthenticatedaccess handler passes because user is trusted (daemon.go:263)certificatesGetcreates permission checker forEntitlementCanViewonTypeCertificate(line 143)- Loop at lines 163-176 filters
baseCertsby permission — but only populatescertResponsesfor recursive mode - Since
!recursion, control reaches lines 185-192 - New loop iterates ALL
baseCerts(unfiltered) and builds URL list with fingerprints - Full list of certificate fingerprints returned to restricted user
Proof of Concept
Impact
- Identity enumeration: A restricted user can discover the fingerprints of all trusted certificates, revealing the complete set of identities in the LXD trust store.
- Reconnaissance for targeted attacks: Fingerprints identify specific certificates used for inter-cluster communication, admin access, and other privileged operations.
- RBAC bypass: In deployments using fine-grained RBAC (OpenFGA or built-in TLS authorization), the non-recursive path completely bypasses the intended per-object visibility controls.
- Information asymmetry: Restricted users gain knowledge of the full trust topology, which the administrator explicitly intended to hide via per-certificate
can_viewentitlements.
Recommended Remediation
Option 1: Apply the permission filter to the non-recursive path (preferred)
Replace the unfiltered loop with one that checks userHasPermission, matching the pattern used in the recursive path and in all other list endpoints:
Option 2: Build both response types in a single filtered loop
Restructure the function to build both the URL list and the recursive response in the same permission-checked loop, eliminating the possibility of divergent filtering:
Option 2 is structurally safer as it prevents the two paths from diverging in the future.
Credit
This vulnerability was discovered and reported by bugbunny.ai.
Пакеты
github.com/canonical/lxd
< 0.0.0-20260224152359-d936c90d47cf
0.0.0-20260224152359-d936c90d47cf
Связанные уязвимости
Improper authorization in the API endpoint GET /1.0/certificates in Canonical LXD 6.6 on Linux allows an authenticated, restricted user to enumerate all certificate fingerprints trusted by the lxd server.
Improper authorization in the API endpoint GET /1.0/certificates in Canonical LXD 6.6 on Linux allows an authenticated, restricted user to enumerate all certificate fingerprints trusted by the lxd server.
Improper authorization in the API endpoint GET /1.0/certificates in Ca ...