Finding the blind spot: How Canonical hunts logic flaws with AI

The recent unveiling of Anthropic’s Claude Mythos preview has radically shifted the cybersecurity landscape. We are now in an era where AI can autonomously discover and exploit zero-day vulnerabilities in mature codebases at machine speed. Perhaps the most exciting revelation from the Mythos preview was the demonstration that frontier models can now successfully reason about complex, domain-specific business logic bugs – a class of vulnerabilities historically reserved for human security researchers.

Earlier this year, I began developing an internal AI-powered auditing agent called Redhound to proactively hunt for these exact blind spots. Built on frontier models, Redhound puts that reasoning to work against our own codebases at Canonical.

Redhound has already proven its value, recently uncovering three critical logic vulnerabilities in LXD, our container and virtual machine manager. These bugs had survived years of manual review and static analysis. Redhound found them in under a day of unsupervised analysis.

Below, I break down the mechanics of this adversarial pipeline, the technical details of the three zero-days (now patched and disclosed), and how agentic auditing changes the way we secure infrastructure.

The bugs that fall through every other tool

Static analysis handles pattern-matching problems well: injection sinks, unsafe API calls, and dangerous concatenations. Modern scanners were built to find these problems, and they do that work reliably.

What these scanners cannot do is reason about what is missing: for example, a checklist that names three fields when the data structure has four; or a validation that reads one file while the operation it gates uses a different one. These are not sloppy code errors; they are exploitable gaps in code that reads correctly to a careful reviewer. Because the line that would close the gap does not exist in the source, a tool looking for patterns has nothing to match against.

Dynamic analysis and fuzzing fail for a related reason: they need a runtime signal – a crash, a panic, a sanitizer trip. A request that should have been denied but succeeds looks identical to a legitimate one. There is nothing for the fuzzer to trip on.

Manual review and penetration testing catch these bugs, but the work is time-consuming and demands substantial domain expertise. Finding the vulnerabilities by hand means combing through hundreds of thousands of lines that are correct, waiting to notice the one that isn’t. Mature codebases survive years of this and still ship logic bugs.

These are the bugs Redhound goes after: the code does exactly what it was written to do, but that does not map to the intent of the security model.

How Redhound works

Redhound audits our codebases the way a determined human attacker would: reading a project end-to-end, generating adversarial hypotheses, dispatching agents to investigate each one, and running a separate round of agents to refute them.

The pipeline runs in five conceptual phases:

  1. Deterministic Recon: Before any agent reads a single line, static-analysis tooling maps the codebase. This includes every function, type, and call edge; every HTTP, gRPC, and CLI entry point; and categorized security signals (auth, crypto, deserialization, injection).
  1. Threat Modeling: An agent reads the recon graph and identifies what an attacker would want (e.g., host root, cross-tenant access, a cluster admin certificate) and maps the trust boundaries between an outside attacker and each target.
  1. Iterative Loop: Each cycle, a red-team agent generates a batch of attack hypotheses. Each hypothesis is dispatched to a separate investigator agent given the relevant slice of the call graph. The investigator either finds a concrete exploit path or reports the hypothesis as false.
  1. Debunking: This is the most critical design decision. Every confirmed-looking finding is handed to a “debunker” agent with the exact opposite objective. Its job is to independently read the source and find the runtime guard that kills the attack – with no anchoring from the investigator’s confidence.
  1. Impact Assessment: A technical fault in the code does not automatically equate to a security vulnerability. This final agent cross-references any finding that survives the debunker against the original threat model and the application’s defined trust boundaries. By evaluating the precise attacker prerequisites and calculating the actual privilege gain, the assessor ensures that the pipeline escalates only verifiable, high-impact exploits rather than purely theoretical issues.

Only findings that survive the debunker reach a human reviewer. Redhound then generates a draft report and a runnable proof-of-concept (PoC) exploit to streamline the validation process.

Three classes of bug

The three findings are a useful sample because they represent three different classes of logic flaws. All three were assigned a final CVSS 3.1 score of 9.1 during coordinated disclosure.

VulnerabilityCWE / ClassAttacker gainsWhy hard to find
Certificate type escalation (CVE-2026-34179)CWE-915 (mass assignment)Restricted certificate user to host rootA missing authorization check – no pattern marks what is not there
VM low-level option bypass (CVE-2026-34177)CWE-184 (incomplete denylist)Restricted project user to host rootAn unlisted key is indistinguishable from an intentionally permitted one
Backup restore desync (CVE-2026-34178)CWE-20 (improper input validation)Restricted project user to host rootTwo data flows from one input diverge across four files

Each finding below shows what Redhound actually produced: the structured metadata, the title verbatim, and the concrete trace generated by the investigator agent.

Certificate type escalation (CVE-2026-34179)

This flaw resides in the certificate update logic where the system fails to validate the certificate “type”. A restricted certificate user can effectively grant themselves Cluster Admin privileges by bypassing type checks during a certificate update.

Finding details:

  • Title: “Restricted user can change certificate type to ‘server’ via legacy API, escalating to ProtocolCluster admin”
  • Finding ID: thread-041
  • Severity: Critical (confidence: exploitable)
  • Privilege gain: 10/10
  • Ease of exploitation: 9/10
  • Prerequisite prevalence: 8/10
  • Attacker profile: ap-002 (Authenticated restricted user)
  • Source agent: thread-follower; survived the debunker
  • CVSS 3.1: 9.1 (assigned during disclosure)

Exploitation trace on LXD 6.7 (eight steps, generated by the investigator):

  1. certificates.go:49 – restricted TLS user sends PUT /1.0/certificates/<own-fingerprint> with type=’server’, keeping name / restricted / projects identical. Passes allowAuthenticated.
  2. certificates.go:960 – caller-supplied type converted to TypeServer.
  3. certificates.go:975 – EntitlementCanEdit check denies (user is restricted).
  4. certificates.go:992 – guard checks Restricted / Name / Projects against the original record. Type is not checked.
  5. certificates.go:1003-1009 – rebuilds the struct with original Restricted / Name / Certificate, but uses caller-supplied reqDBType.
  6. certificates.go:1073 – UpdateCertificate writes Type=TypeServer to the database.
  7. certificates.go:1099 – identity cache refreshed via s.UpdateIdentityCache().
  8. daemon.go:587 – next handshake matches as ProtocolCluster, Trusted=true. The restricted user is now cluster admin.

Also produced for this finding: full code-location evidence and a debunker review that found no defense.

VM low-level option bypass (CVE-2026-34177)

This bypass allows for arbitrary QEMU configuration injection by exploiting an incomplete blocklist in restricted projects. In combination with another finding, which identified that raw.apparmor is also not restricted, this allows a restricted user to escape to host root.

Finding details:

  • Title: “raw.qemu.conf bypasses restricted.virtual-machines.lowlevel project restriction, allowing arbitrary QEMU config injection”
  • Finding ID: thread-009
  • Severity: High (confidence: exploitable)
  • Privilege gain: 8/10
  • Ease of exploitation: 8/10
  • Prerequisite prevalence: 8/10
  • Attacker profile: ap-002 (Authenticated restricted user with a VM in a restricted project)
  • Source agent: thread-follower; survived the debunker
  • CVSS 3.1: 9.1 (assigned during disclosure)

Exploitation trace on LXD 6.7 (four steps, generated by the investigator):

  1. lxd/project/limits/permissions.go:779 – restricted user in a restricted project (restricted.virtual-machines.lowlevel=block by default) sets raw.qemu.conf on a VM via PUT /1.0/instances/{name}. entityConfigChecker calls isVMLowLevelOptionForbidden(‘raw.qemu.conf’), which returns false because the key is not in the four-element blocklist.
  2. lxd/instance/instancetype/instance.go:1140 – config-key validation accepts the value (validate.IsAny).
  3. lxd/instance/drivers/driver_qemu.go:3905 – on VM start, generateQemuConfigFile calls qemuRawCfgOverride as the last config transformation; the attacker’s content is parsed and appended to the QEMU config file.
  4. lxd/instance/drivers/driver_qemu.go:1535 – QEMU is launched with -readconfig pointing at the modified file. The injected [chardev] and [mon] sections create a QEMU monitor socket on the host filesystem.

Also produced for this finding: full code-location evidence and a debunker review that searched for a runtime guard on the unlisted key and found none.

Backup restore desynchronization (CVE-2026-34178)

This vulnerability exploits the discrepancy between how LXD validates a backup index and how it actually imports the internal backup configuration. This desynchronization allows an attacker to sneak forbidden security configurations past the project’s restriction checks.

Finding details:

  • Title: “Backup restore config injection via index.yaml/backup.yaml desynchronization bypasses all project restriction checks”
  • Finding ID: thread-086
  • Severity: High (confidence: exploitable)
  • Privilege gain: 8/10
  • Ease of exploitation: 7/10
  • Prerequisite prevalence: 8/10
  • Attacker profile: ap-002 (Authenticated restricted user with CanCreateInstances in a restricted project)
  • Source agent: thread-follower; survived the debunker
  • CVSS 3.1: 9.1 (assigned during disclosure)

Exploitation trace on LXD 6.7 (seven steps, generated by the investigator):

  1. instances_post.go:1170 – restricted user sends POST /1.0/instances with a crafted backup tar. index.yaml has Instance.Config={}; backup/container/backup.yaml has Instance.Config={‘security.privileged’: ‘true’, ‘raw.lxc’: ‘lxc.mount.auto.proc=rw:mixed’}.
  2. backup/backup_info.go:69 – GetInfo reads index.yaml from the tar and decodes into Info.Config. Clean configuration; no forbidden keys.
  3. instances_post.go:885 – limits.AllowInstanceCreation checks bInfo.Config.Instance.Writable() (from index.yaml) against project restrictions. Check passes.
  4. storage/drivers/generic_vfs.go:952 – CreateInstanceFromBackup extracts the tar, writing the tampered backup/container/backup.yaml to mountPath/backup.yaml.
  5. backup/backup_config_utils.go:236 – UpdateInstanceConfig reads backup.yaml, updates only Name and Project, writes back. security.privileged=true and raw.lxc survive.
  6. api_internal.go:784 – internalImportFromBackup reads backup.yaml from disk; backupConf.Instance.Config now holds the forbidden keys.
  7. api_internal.go:946 – instance.CreateInternal writes the instance to the database with security.privileged=true and raw.lxc. A privileged container exists inside a restricted project; UID 0 inside maps to UID 0 on the host.

Also produced for this finding: full code-location evidence across four source files and a debunker review that searched for a missing reconciliation step and found none.

None of these findings is exotic. Missing fields in allowlists, short denylists, divergent validation paths – these exist in every mature codebase. The difficulty has always been identifying where to focus across a few hundred thousand lines of code.

What this changes in practice

Redhound does not replace the tools we already run. SAST, fuzzing, dependency scanning, and human review keep doing what they do well, and Redhound feeds into the same review pipeline.

What changes is what each review can reach. Audits begin from an attack-surface map, candidate findings with full exploitation traces, and a record of hypotheses already debunked. Logic bugs that have historically survived years of expert scrutiny become tractable, and reviewer judgment is spent where it matters most: assessing real-world impact, and engineering architectural fixes.

What’s next

Internally at Canonical, tools like Redhound are now becoming a part of how we work every day, not only as a single audit but introducing it as a recurring practice. Our goal is to incorporate agentic security auditing into our existing processes to elevate the security posture of Canonical’s products across the board.

Disclosure

All three findings were disclosed to the LXD team, fixed in coordinated releases, and assigned CVEs CVE-2026-34177, CVE-2026-34178, and CVE-2026-34179. Thanks to the LXD team for triaging and patching all three.

Talk to us today

Interested in running Ubuntu in your organisation?

Newsletter signup

Get the latest Ubuntu news and updates in your inbox.

By submitting this form, I confirm that I have read and agree to Canonical's Privacy Policy.

Related posts

Fragnesia Linux kernel local privilege escalation vulnerability mitigations

A local privilege escalation (LPE) vulnerability affecting the Linux kernel has been publicly disclosed on May 13, 2026. The vulnerability does not have a CVE...

Dirty Frag Linux kernel local privilege escalation vulnerability mitigations

Two local privilege escalation (LPE) vulnerabilities affecting the Linux kernel have been publicly disclosed on May 7, 2026. The vulnerabilities have been...

Fixes available for CVE-2026-31431 (Copy Fail) Linux Kernel Local Privilege Escalation Vulnerability

A local privilege escalation (LPE) vulnerability affecting the Linux kernel has been publicly disclosed on April 29, 2026. The vulnerability has been assigned...