# Red Hat Quay CVE-2026-110697: robot listing LIKE wildcard (CWE-89), detection and review pack

CybaVerse pack for CVE-2026-110697 in Red Hat Quay. It holds Microsoft Sentinel and Defender XDR hunting KQL, Sigma rules for
web server and proxy logs, and a read-only checker in bash and PowerShell (identical output). Sources were read on
11 Nov 2026 between 18:09 and 18:30 GMT.

**Patch now.** Move to the fixed release Red Hat names for your stream, multi-tenant and customer-shared registries first,
and apply Red Hat's interim steps where you cannot. **The KQL in this pack has NOT been run against a live tenant.** It parses
offline against an assumed schema subset; test and tune it before you rely on it.

Credit: Red Hat. Red Hat's records say the issue was discovered by Red Hat; this pack names no individual.

## What Red Hat says

Red Hat's description (CVE record, Red Hat CNA): "A flaw was found in Quay where the robot account listing function constructs
a SQL LIKE pattern from user-controlled namespace names without escaping wildcard metacharacters. An attacker who creates a
namespace containing an underscore character can match and retrieve robot accounts and their decrypted authentication tokens
from other tenants' namespaces, leading to cross-tenant credential theft and unauthorized image operations."

| Item | As published |
|---|---|
| Weakness | CWE-89 (CVE record and NVD) |
| Rating | Red Hat rates it Critical, CVSS 3.1 9.9, AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H. Red Hat's page marks the score DRAFT: "The following CVSS metrics and score provided are preliminary and subject to review." |
| NVD | Received 11 Nov 2026 17:17 GMT, status Awaiting Analysis. The only score is Red Hat's 9.9 (secalert@redhat.com, type Primary); NVD has not scored it. |
| Red Hat's statement | Rated Critical by Red Hat because an authenticated attacker can break tenant isolation in multi-tenant Quay; no elevated privileges or victim interaction are needed. Red Hat adds that in OpenShift environments stolen robot credentials can be used to inject images into CI/CD pipelines. |
| Dates | Reported to Red Hat 14 Oct 2026 (14:00 UTC, 15:00 BST). Made public 11 Nov 2026 14:00 GMT. CVE record published 17:06 GMT. NVD received 17:17 GMT. Red Hat Bugzilla 2552237 (opened 14 Oct 2026, 15:12 BST) is NEW, urgent, with no "fixed in" value. |
| KEV | Not in CISA KEV catalog 2026.11.10 (released 10 Nov 2026 19:03 GMT), which predates the CVE. This pack makes no claim either way about use in attacks. |
| Quay.io | Red Hat says it has already fixed Quay.io. |

### Affected products (Red Hat VEX and the CVE page)

| Product | Package | Red Hat state | Advisory listed |
|---|---|---|---|
| Red Hat Quay 3 | quay/quay-rhel8 | Affected | none |
| Red Hat Quay 3 | quay/quay-rhel9 | Affected | none |
| Red Hat Quay 5 | quay/quay-rhel9 | Affected | none |
| Red Hat OpenShift Update Service (CPE openshift_update_service:5) | openshift-update-service/openshift-update-service-rhel8 | Not affected (Vulnerable Code not Present) | none |

### Fixed releases and the conflict in Red Hat's records (recorded as published, not resolved)

Red Hat's mitigation text says: "Update to the fixed Red Hat Quay release for your version: 3.9.28, 3.10.28, 3.12.24, 3.14.10,
3.15.9, 3.16.7, 3.17.7, or 3.18.3." In this pack's own words: Red Hat names 3.9.28, 3.10.28, 3.12.24, 3.14.10, 3.15.9, 3.16.7, 3.17.7 and 3.18.3.

At the same time Red Hat's packages table still shows Quay 3 (both packages) and Quay 5 as Affected with no advisory, and the
VEX file lists no vendor fix for any product; its only remediations are the workaround and "none_available". Both statements
were live when this pack was written. This pack records the conflict and does not resolve it: every verdict says
"fixed release named by Red Hat; no errata listed".

| Stream | Fixed release named by Red Hat | Errata (RHSA) listed | Newest public upstream tag (github.com/quay/quay, 11 Nov 2026) |
|---|---|---|---|
| 3.9 | 3.9.28 | none | v3.9.27 |
| 3.10 | 3.10.28 | none | v3.10.27 |
| 3.11 | none named | none | v3.11.13 |
| 3.12 | 3.12.24 | none | v3.12.23 |
| 3.13 | none named | none | v3.13.11 |
| 3.14 | 3.14.10 | none | v3.14.9 |
| 3.15 | 3.15.9 | none | v3.15.8 |
| 3.16 | 3.16.7 | none | v3.16.6 |
| 3.17 | 3.17.7 | none | v3.17.6 |
| 3.18 | 3.18.3 | none | v3.18.2 |
| Quay 5 | none named | none | no Quay 5 tag upstream |

What we checked: none of the named releases exists yet as an upstream git tag; each is the next patch after the newest tag.
Red Hat's CSAF advisory index (fetched 18:10 GMT, newest entry 15:26 GMT) has no advisory for CVE-2026-110697 among the 1638
advisories released since 14 Oct 2026. The newest Quay advisories there are RHSA-2026:83822 (Quay 3.17.6, 16 Oct) and
RHSA-2026:85980 (Quay 3.18.2, 20 Oct), both for other CVEs. Upstream master (7a361d8c, 11 Nov 2026 14:32 GMT) still builds the
pattern without escaping.

## How it works (Quay source and Red Hat Bugzilla 2552237)

* `_list_entity_robots` in `data/model/user.py` selects robots with `User.username ** (entity_name + "+%")`, a LIKE pattern
  built from the namespace name. Robot user names are `namespace+robot` (`util/names.py`).
* Red Hat Bugzilla 2552237 notes that Quay already has an escaping helper (`prefix_search` in `data/text.py`) that this path
  does not use.
* Namespace names must match `^([a-z0-9]+(?:[._-][a-z0-9]+)*)$`, 2 to 255 characters (`util/validation.py`). So `_` is the only
  LIKE wildcard a name can hold (`%` and backslash are not allowed), and `.` and `-` are literal.
* `_` matches any single character, and the literal `+` pins the length, so a name with `_` matches only namespaces of the same
  length that agree on every other character. Example from Bugzilla: `ab_c` matches `abxc` and `ab-c`.
* Listing endpoints: `GET /api/v1/organization/ORG/robots` and `GET /api/v1/user/robots` (`endpoints/api/robot.py`). Tokens
  are returned unless `token=false` is passed. For an organization, tokens are returned to its admins.
* Bugzilla adds that renaming the attacker's namespace renames (takes over) the matched robots, and deleting it deletes them.
  `change_username` renames robots through the same listing (`data/model/user.py`).
* Related audit log kinds (`initdb.py`): org_create, user_create, org_change_name, user_change_name, org_delete, user_delete,
  create_robot, delete_robot, regenerate_robot_token, create_robot_federation, delete_robot_federation, push_repo, pull_repo,
  create_tag, move_tag, delete_tag. These go to Quay's audit log, not to container output.
* Self-registration is controlled by FEATURE_USER_CREATION (default on in `config.py`) and FEATURE_INVITE_ONLY_USER_CREATION;
  user renames by FEATURE_USER_RENAME (default off).

## Red Hat's interim steps (credited to Red Hat)

Quoted from Red Hat's mitigation: "If you cannot update immediately, restrict who can create organizations or namespaces
containing an underscore, rotate robot tokens for any namespace that collides with an underscore-containing one, and monitor
registry activity for pulls or pushes that don't match expected robot usage.
These steps reduce exposure but do not fix the underlying flaw." Red Hat also says "There is no other complete workaround" than updating.

## Our advice (OUR ADVICE, CybaVerse)

1. Patch multi-tenant and customer-shared registries first, then everything else. For 3.11, 3.13 and Quay 5 Red Hat names no
   fixed release: move to a stream that has one, or ask Red Hat.
2. Check internet exposure of the Quay API and UI. Anything an outside party can sign up to is the highest risk.
3. Review self-registration: if FEATURE_USER_CREATION is on, consider invite-only or off until you are patched.
4. Export every organization and user name and run the checker to find underscore names and the namespaces they match.
5. If you find misuse: rotate the affected robot tokens after patching (a token rotated while the release is still affected and
   the underscore namespace still exists can be listed again), verify image digests against what your build system pushed,
   and review downstream deployments that pulled from those repositories.

## Hunts (kql/redhat-quay-cve-2026-110697-hunting.kql)

| ID | Label | Table | What it finds |
|---|---|---|---|
| Q1 | INVENTORY | ContainerInventory | Quay containers and image tags |
| Q1b | INVENTORY | KubePodInventory | Quay pods |
| Q1c | INVENTORY | ContainerLogV2 | Pods writing Quay output |
| Q1d | INVENTORY | DeviceProcessEvents | Quay processes on hosts |
| Q1e | INVENTORY | DeviceTvmSoftwareInventory | Defender software entries for Quay |
| Q1f | INVENTORY | DeviceTvmSoftwareVulnerabilitiesKB | Has Defender ingested the CVE |
| Q1g | INVENTORY | ContainerInventory | Per-stream verdict from the image tag |
| Q2 | OUR PATTERN | ContainerLogV2 | Organization names with an underscore, first seen in API paths |
| Q2b | OUR PATTERN | CommonSecurityLog | The same at the proxy or WAF |
| Q2c | OUR PATTERN | ContainerLogV2 | Rename or delete of a namespace with an underscore |
| Q3 | OUR PATTERN | ContainerLogV2 | Robot listing calls, underscore organizations first, then hourly bursts |
| Q3b | OUR PATTERN | CommonSecurityLog | The same at the proxy or WAF |
| Q4 | OUR PATTERN | ContainerLogV2 | Robot logins from addresses new for that robot |
| Q4b | OUR PATTERN | ContainerLogV2 | Robots asking for repositories new for them, push first |
| Q4c | OUR PATTERN | ContainerLogV2 | Robot logins outside working hours (UTC, tunable) |
| Q4d | OUR PATTERN | CommonSecurityLog | Robot logins from new addresses at the proxy or WAF |
| Q5 | OUR PATTERN | ContainerLogV2 | Tag replacements from a different client address |
| Q5b | OUR PATTERN | CommonSecurityLog | The same at the proxy or WAF |
| Q6 | OUR PATTERN | ContainerLogV2 | Robot token regeneration and federation changes |

Logging caveats: request bodies and Authorization headers are usually not logged, so a create request does not show the new
name, and a robot pull or push is only tied to a robot through the account parameter on /v2/auth, which Docker and Podman send
(UNVERIFIED for every client). Behind a load balancer Quay's nginx sees the balancer's address.

## Sigma rules (sigma/)

| Rule | Category | Level |
|---|---|---|
| web_quay_robot_listing_underscore_org.yml | webserver | high |
| proxy_quay_robot_listing_underscore_org.yml | proxy | high |
| web_quay_robot_listing_with_tokens.yml | webserver | low |
| web_quay_underscore_namespace_rename_delete.yml | webserver | medium |
| proxy_quay_underscore_namespace_rename_delete.yml | proxy | medium |

All are OUR PATTERN. `sigma/converted/` holds Kusto conversions. Known gaps: the Microsoft pipelines have no table for the
webserver category, and microsoft_xdr, microsoft_365_defender and azure_monitor have none for proxy; sentinel_asim converts the
two proxy rules to imWebSession. Use the KQL hunts where a rule does not convert.

## Checker (scripts/)

`check-quay-cve-2026-110697.sh` and `Check-QuayCve2026110697.ps1` give identical output and exit codes. Report only: they read
exported files, use no network, write nothing, run nothing and never rotate or change anything.

```
check-quay-cve-2026-110697.sh --version 3.17.6,registry.redhat.io/quay/quay-rhel9:v3.18.2
check-quay-cve-2026-110697.sh --namespaces orgs_and_users.json --robots robots.json --name site-a
```

* `--version`: per-stream verdict. PATCHED or VULNERABLE against the fixed release Red Hat names for that stream (with "no
  errata listed"), NO FIX NAMED for Quay 3 streams Red Hat does not name and for Quay 5, OUT OF RANGE for other majors, UNKNOWN
  for tags such as latest or a digest.
* `--namespaces`: text (one per line) or JSON (an array, or the superuser API's organizations and users lists). Flags every
  name with `_` that matches another namespace (COLLISION).
* `--robots` (needs `--namespaces`): robots API output (export with `token=false`; token values are never read or printed), a
  JSON array or a text list. Lists robots of matched namespaces (ROTATE).
* Exit codes: 0 NO FINDINGS, 1 ACTION NEEDED, 3 UNKNOWN, 4 usage or read error (stdout stays empty).

## Tests and gates

`bash tests/run_all.sh` runs ShellCheck, PSScriptAnalyzer, the bash tests, Pester (with byte-for-byte parity), guards for
network and write primitives, fixture hygiene (FIXTURE_ONLY, RFC 5737 addresses and example.com only), wording guards,
sandboxed runs with no network and a read-only filesystem (sha256 of the pack before and after), the offline KQL parse, and
sigma check and convert.

## Limits and unverified items

* The KQL has not been run against a live tenant; table and column names are an assumed subset.
* The fixed releases come only from Red Hat's mitigation text; no errata or upstream tags existed when this was written.
* Whether Red Hat will ship fixes for 3.11, 3.13 or Quay 5 is not stated.
* The checker can only see names in the export it is given; a namespace created later can still collide.
* Quay's own audit log is the best record of org_create, user_change_name and pull_repo by robots; this pack does not parse it.

## Sources

* https://access.redhat.com/security/cve/CVE-2026-110697
* https://bugzilla.redhat.com/show_bug.cgi?id=2552237
* https://nvd.nist.gov/vuln/detail/CVE-2026-110697
* https://www.cve.org/CVERecord?id=CVE-2026-110697
* https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-110697.json
* https://github.com/quay/quay (master 7a361d8c)
