Bug 2479230 (CVE-2026-91147)

Summary: CVE-2026-91147 cockpit: Cockpit: Denial of Service in `cockpit-ws` due to URL-root handling without a trailing slash
Product: [Other] Security Response Reporter: OSIDB Bzimport <bzimport>
Component: vulnerabilityAssignee: Product Security DevOps Team <prodsec-dev>
Status: NEW --- QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: rhel-process-autobot, sdawley, security-response-team, watson-tool-maintainers
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: ---
Doc Text:
A flaw was found in `cockpit-ws`. This vulnerability allows a remote, unauthenticated attacker to cause a Denial of Service (DoS) by sending a specially crafted request. When the `WebService.UrlRoot` is configured and a request is made to the exact URL-root prefix without a trailing slash, `cockpit-ws` can terminate unexpectedly. This issue leads to the unavailability of the Cockpit web service.
Story Points: ---
Clone Of: Environment:
Last Closed: Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description OSIDB Bzimport 2026-05-18 03:59:35 UTC
AI_ONLY_REPORT
package: cockpit-356-1.el10
------
Summary: Reachable Assertion DoS in `cockpit-ws` when `WebService.UrlRoot`  
is set and requested without trailing slash: an unauthenticated request to  
the exact configured URL-root prefix can yield an empty internal path and  
trigger a reachable `g_assert()`, aborting `cockpit-ws`.
Requirements to exploit: The target must run `cockpit-356-1.el10` with a  
non-empty `[WebService] UrlRoot`, and the attacker must be able to send an  
exact-prefix request without a trailing slash through a route that does not  
normalize or redirect it first. No authentication or user interaction is  
required. The availability impact depends on a build where `g_assert()` is  
active.
Component affected: `cockpit-356-1.el10`, specifically  
`src/ws/cockpitwebserver.c` in `path_has_prefix()`,  
`cockpit_web_request_process()`, and  
`cockpit_web_server_default_handle_stream()`.
Version affected: `cockpit-356-1.el10` when `[WebService] UrlRoot` is  
configured to a non-empty prefix
Patch available: no released package fix established; proposed patch  
included below
Version fixed: unknown
Upstream coordination: Notified via this report.
CVSS: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H - 5.9 (MEDIUM)
AV:N - The issue is triggered by a network request to the Cockpit web  
service.
AC:H - Exploitation requires a non-default but documented `UrlRoot`  
deployment, the exact configured prefix to be requested without a trailing  
slash, and a request path that is not normalized away before `cockpit-ws`  
processes it. The trigger also depends on an assertion-enabled build.
PR:N - No authentication is required.
UI:N - No user interaction is required.
S:U - The impact is confined to the Cockpit web service process.
C:N - No confidentiality impact is established.
I:N - No integrity impact is established.
A:H - A successful trigger reaches a `g_assert()` and aborts  
`cockpit-ws`, interrupting service availability.
Impact: Moderate. Assuming the shipped build retains `g_assert()`, this is  
a remote unauthenticated availability issue. Under Red Hat's guidance,  
remote DoS would normally lean Important, but this case depends on a  
non-default yet documented `UrlRoot` deployment and on the exact prefix  
request reaching `cockpit-ws` without front-end normalization or redirect,  
which makes it less easily exploitable and a better fit for Moderate.
Embargo: no
Reason: This is an availability-only issue in a configuration-dependent  
deployment, and there are straightforward operational mitigations such as  
redirecting or normalizing the exact `UrlRoot` request before it reaches  
`cockpit-ws`.
Acknowledgement: Aisle Research
Vulnerability Details: Non-empty `url_root` handling accepts an exact  
prefix match because `path_has_prefix()` treats either `'\0'` or `'/'`  
after the prefix as valid. `cockpit_web_request_process()` then strips  
`url_root` by pointer arithmetic, so a request such as `/cockpit` becomes  
an empty string rather than `/`:
```c
static gboolean
path_has_prefix (const gchar *path,
GString *prefix)
{
return prefix->len > 0 &&
strncmp (path, prefix->str, prefix->len) == 0 &&
(path[prefix->len] == '\0' || path[prefix->len] == '/');
}
```
```c
if (self->web_server->url_root->len &&
!path_has_prefix (path, self->web_server->url_root))
{
self->delayed_reply = 404;
}
...
self->original_path = path_copy;
self->path = path_copy + self->web_server->url_root->len;
```
The default stream handler assumes the resulting path always begins with  
`/` and aborts otherwise:
```c
g_assert (request->path[0] == '/');
gsize component_end = 1 + strcspn (request->path + 1, "/");
```
`[WebService] UrlRoot` is wired into `url-root` in `src/ws/main.c`, and the  
registered `handle-stream` callbacks only claim their expected `/socket` or  
`/channel/` paths. As a result, an empty path can fall through to the  
default stream handler before authentication. Some front-end proxies may  
normalize or redirect the exact prefix path before `cockpit-ws` sees it, so  
reachability depends on the deployed request path rather than being  
universal across all installations.
Steps to reproduce:
1. Configure Cockpit with a non-empty URL root:
```ini
[WebService]
UrlRoot=/cockpit
```
2. Restart `cockpit-ws` or the service stack that launches it.
3. Send a request to the exact configured prefix without a trailing slash  
using a route that reaches `cockpit-ws` without rewriting the request  
first. For example, in a deployment where the request reaches the service  
directly:
```bash
curl -i http://<host>:9090/cockpit
```
Raw request equivalent:
```http
GET /cockpit HTTP/1.1
Host: <host>:9090
```
If the deployment redirects plain HTTP to HTTPS before `cockpit-ws` handles  
the request, issue the same exact-path request through the front-end HTTPS  
endpoint instead.
4. Observe a process abort at `g_assert (request->path[0] == '/')`.  
Deployments whose front-end normalizes `/cockpit` to `/cockpit/` before  
forwarding may not reproduce through that route.
Mitigation: Until a code fix is available, either avoid deploying a  
non-empty `[WebService] UrlRoot` where feasible, or ensure the front-end  
proxy or TLS terminator rewrites or redirects the exact configured prefix,  
for example `/cockpit`, to the trailing-slash form before forwarding to  
`cockpit-ws`.
Proposed Fix: Normalizing an empty post-prefix path to `/` avoids the  
reachable assertion for exact-root requests, including query-bearing  
variants, while keeping handler behavior otherwise stable.
```diff
diff --git a/src/ws/cockpitwebserver.c b/src/ws/cockpitwebserver.c
@@ -780,6 +780,10 @@ cockpit_web_request_process (CockpitWebRequest *self,
self->path = path_copy + self->web_server->url_root->len;
self->headers = headers;
self->host = host;
@@ -791,6 +795,10 @@ cockpit_web_request_process (CockpitWebRequest *self,
else
self->query = "";
+
+  /* Exact UrlRoot request (e.g. "/cockpit") yields empty suffix;  
normalize */
+  if (self->path[0] == '\0')
+    self->path = "/";
```
Optional follow-up hardening: add a regression test covering `GET /path`  
when `url-root=/path`.
------
This report was generated using AI technology. Always review AI-generated  
content prior to use