`openvt -u` is intended to identify the owner of the current VT and then execute `login` as that user from a privileged context. In the documented `kbrequest`/init usage, the ownership test in `authenticate_user()` relies on `stat("/proc/<pid>/fd/0")`. `stat()` on `/proc/<pid>/fd/0` follows the symlink to the underlying TTY device node. As a result, `buf.st_uid` reflects the owner of the TTY node rather than the owner of the process holding the file descriptor. If the TTY owner returns to `root` or the getty owner after logout while an unprivileged process still has `fd 0` attached to that TTY, the check can incorrectly treat that process as belonging to the privileged console owner. Once that check succeeds, the `-u` path executes a passwordless login as the selected user. In the documented `kbrequest`/init deployment using `openvt -us`, this can result in passwordless `login -f root` on the spawned VT. This report establishes that privilege escalation path for that documented deployment; it does not claim equivalent reachability for deployments that do not use `openvt -u` from a privileged `kbrequest`/init path.
AI_ONLY_REPORT
package: kbd-2.8.0-3.fc43
------
Summary: Local Privilege Escalation in openvt via Incorrect Process Owner
Verification: `openvt -u` trusts the ownership of the TTY referenced by
`/proc/<pid>/fd/0` instead of the owning process, allowing passwordless
root login in documented `kbrequest`/init deployments.
Requirements to exploit: Local unprivileged access to a system where
`openvt -u` is invoked with root privileges from a `kbrequest`/init
configuration such as `kb::kbrequest:/usr/bin/openvt -us`, plus the ability
to keep a process alive with `fd 0` attached to the originating VT and then
trigger the configured keyboard request.
Component affected: `kbd-2.8.0-3.fc43`: `src/openvt.c`,
`authenticate_user()`, and the privileged `openvt -u` login path.
Version affected: `kbd-2.8.0-3.fc43` when `openvt -u` is used in a
privileged `kbrequest`/init deployment such as
`kb::kbrequest:/usr/bin/openvt -us`
Patch available: no released package fix established; proposed patch
included below
Version fixed: unknown
Upstream coordination: Not notified.
CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - 7.8 (HIGH)
AV:L - Exploitation requires local access to the affected host and an
interactive VT/session.
AC:L - In the affected deployment, exploitation is straightforward: keep
a process attached to the VT, log out, and trigger the configured keyboard
request.
PR:L - The attacker needs only normal unprivileged local access.
UI:N - No separate victim interaction is required; the attacker performs
the triggering action directly.
S:U - The vulnerable check and resulting privilege change occur within
the same system security scope.
C:H - Successful exploitation yields root access and exposure of
system-wide data.
I:H - Successful exploitation yields root authority to modify system
state arbitrarily.
A:H - Successful exploitation yields root authority to disrupt services
or otherwise fully impact availability.
Impact: Important. Red Hat rates flaws that allow a local user to gain
additional privileges as Important. In the affected deployment, this issue
can bypass normal authentication and yield root access through `login -f`.
It is not Critical because exploitation is local and depends on a specific
privileged configuration, but the resulting compromise is still full system
compromise where that configuration is present.
Embargo: no
Reason: The issue is local-only and configuration-dependent, and there
is a straightforward operational workaround: stop using `openvt -u` in the
`kbrequest`/init path or disable the keyboard request binding until a fix
is available.
Acknowledgement: Aisle Research
Vulnerability Details: `openvt -u` is intended to identify the owner of the
current VT and then execute `login` as that user from a privileged context.
In the documented `kbrequest`/init usage, the ownership test in
`authenticate_user()` relies on `stat("/proc/<pid>/fd/0")`:
```c
while ((dentp = readdir(dp))) {
sprintf(filename, "/proc/%s/fd/0", dentp->d_name);
if (stat(filename, &buf))
continue;
if (buf.st_dev == console_dev && buf.st_ino == console_ino &&
buf.st_uid == console_uid)
goto got_a_process;
}
```
`stat()` on `/proc/<pid>/fd/0` follows the symlink to the underlying TTY
device node. As a result, `buf.st_uid` reflects the owner of the TTY node
rather than the owner of the process holding the file descriptor. If the
TTY owner returns to `root` or the getty owner after logout while an
unprivileged process still has `fd 0` attached to that TTY, the check can
incorrectly treat that process as belonging to the privileged console owner.
Once that check succeeds, the `-u` path executes a passwordless login as
the selected user:
```c
if (as_user)
execlp("login", "login", "-f", username, NULL);
```
In the documented `kbrequest`/init deployment using `openvt -us`, this can
result in passwordless `login -f root` on the spawned VT. This report
establishes that privilege escalation path for that documented deployment;
it does not claim equivalent reachability for deployments that do not use
`openvt -u` from a privileged `kbrequest`/init path.
Steps to reproduce:
1. Configure the documented init usage with `kb::kbrequest:/usr/bin/openvt
-us`.
2. Load a keyboard mapping such as `echo "alt keycode 103 = Spawn_Console"
loadkeys`.
3. Log in as an unprivileged user on `tty1`.
4. Start a background process that keeps standard input attached to that
TTY: `nohup sh -c 'sleep 1000000' </dev/tty1 >/dev/null 2>&1 &`.
5. Log out from `tty1` so the TTY ownership returns to `root` or the getty
owner.
6. Trigger the configured keyboard request, for example with Alt+Up Arrow
in the documented setup.
7. Observe passwordless `login -f root` execution on the new VT.
Mitigation: Until a fix is available, avoid `openvt -u` in privileged
`kbrequest`/init deployments. If console spawning is required, configure
the keyboard request to start a normal authenticated login on the new VT
instead of pre-authenticating with `login -f`, or disable the keyboard
request binding entirely.
Proposed Fix: Validate the actual process owner from `/proc/<pid>` before
matching its `fd/0` against the target TTY, and stop using the TTY node
owner as proof of process ownership.
```diff
diff --git a/src/openvt.c b/src/openvt.c
— a/src/openvt.c
+++ b/src/openvt.c
@@ -120,13 +120,21 @@ authenticate_user(int curvt)
/* check to make sure that user has a process on that tty */
/* this will fail for example when X is running on the tty */
while ((dentp = readdir(dp))) {
+ struct stat pbuf;
+
+ sprintf(filename, "/proc/%s", dentp->d_name);
+ if (stat(filename, &pbuf))
+ continue;
+ if (pbuf.st_uid != console_uid)
+ continue;
+
sprintf(filename, "/proc/%s/fd/0", dentp->d_name);
-
if (stat(filename, &buf))
continue;
if (buf.st_dev == console_dev && buf.st_ino == console_ino
&& buf.st_uid == console_uid)
+ if (buf.st_dev == console_dev && buf.st_ino == console_ino)
goto got_a_process;
}
```
------
This report was generated using AI technology. Always review AI-generated
content prior to use
AI_ONLY_REPORT package: kbd-2.8.0-3.fc43 ------ Summary: Local Privilege Escalation in openvt via Incorrect Process Owner Verification: `openvt -u` trusts the ownership of the TTY referenced by `/proc/<pid>/fd/0` instead of the owning process, allowing passwordless root login in documented `kbrequest`/init deployments. Requirements to exploit: Local unprivileged access to a system where `openvt -u` is invoked with root privileges from a `kbrequest`/init configuration such as `kb::kbrequest:/usr/bin/openvt -us`, plus the ability to keep a process alive with `fd 0` attached to the originating VT and then trigger the configured keyboard request. Component affected: `kbd-2.8.0-3.fc43`: `src/openvt.c`, `authenticate_user()`, and the privileged `openvt -u` login path. Version affected: `kbd-2.8.0-3.fc43` when `openvt -u` is used in a privileged `kbrequest`/init deployment such as `kb::kbrequest:/usr/bin/openvt -us` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - 7.8 (HIGH) AV:L - Exploitation requires local access to the affected host and an interactive VT/session. AC:L - In the affected deployment, exploitation is straightforward: keep a process attached to the VT, log out, and trigger the configured keyboard request. PR:L - The attacker needs only normal unprivileged local access. UI:N - No separate victim interaction is required; the attacker performs the triggering action directly. S:U - The vulnerable check and resulting privilege change occur within the same system security scope. C:H - Successful exploitation yields root access and exposure of system-wide data. I:H - Successful exploitation yields root authority to modify system state arbitrarily. A:H - Successful exploitation yields root authority to disrupt services or otherwise fully impact availability. Impact: Important. Red Hat rates flaws that allow a local user to gain additional privileges as Important. In the affected deployment, this issue can bypass normal authentication and yield root access through `login -f`. It is not Critical because exploitation is local and depends on a specific privileged configuration, but the resulting compromise is still full system compromise where that configuration is present. Embargo: no Reason: The issue is local-only and configuration-dependent, and there is a straightforward operational workaround: stop using `openvt -u` in the `kbrequest`/init path or disable the keyboard request binding until a fix is available. Acknowledgement: Aisle Research Vulnerability Details: `openvt -u` is intended to identify the owner of the current VT and then execute `login` as that user from a privileged context. In the documented `kbrequest`/init usage, the ownership test in `authenticate_user()` relies on `stat("/proc/<pid>/fd/0")`: ```c while ((dentp = readdir(dp))) { sprintf(filename, "/proc/%s/fd/0", dentp->d_name); if (stat(filename, &buf)) continue; if (buf.st_dev == console_dev && buf.st_ino == console_ino && buf.st_uid == console_uid) goto got_a_process; } ``` `stat()` on `/proc/<pid>/fd/0` follows the symlink to the underlying TTY device node. As a result, `buf.st_uid` reflects the owner of the TTY node rather than the owner of the process holding the file descriptor. If the TTY owner returns to `root` or the getty owner after logout while an unprivileged process still has `fd 0` attached to that TTY, the check can incorrectly treat that process as belonging to the privileged console owner. Once that check succeeds, the `-u` path executes a passwordless login as the selected user: ```c if (as_user) execlp("login", "login", "-f", username, NULL); ``` In the documented `kbrequest`/init deployment using `openvt -us`, this can result in passwordless `login -f root` on the spawned VT. This report establishes that privilege escalation path for that documented deployment; it does not claim equivalent reachability for deployments that do not use `openvt -u` from a privileged `kbrequest`/init path. Steps to reproduce: 1. Configure the documented init usage with `kb::kbrequest:/usr/bin/openvt -us`. 2. Load a keyboard mapping such as `echo "alt keycode 103 = Spawn_Console" loadkeys`. 3. Log in as an unprivileged user on `tty1`. 4. Start a background process that keeps standard input attached to that TTY: `nohup sh -c 'sleep 1000000' </dev/tty1 >/dev/null 2>&1 &`. 5. Log out from `tty1` so the TTY ownership returns to `root` or the getty owner. 6. Trigger the configured keyboard request, for example with Alt+Up Arrow in the documented setup. 7. Observe passwordless `login -f root` execution on the new VT. Mitigation: Until a fix is available, avoid `openvt -u` in privileged `kbrequest`/init deployments. If console spawning is required, configure the keyboard request to start a normal authenticated login on the new VT instead of pre-authenticating with `login -f`, or disable the keyboard request binding entirely. Proposed Fix: Validate the actual process owner from `/proc/<pid>` before matching its `fd/0` against the target TTY, and stop using the TTY node owner as proof of process ownership. ```diff diff --git a/src/openvt.c b/src/openvt.c — a/src/openvt.c +++ b/src/openvt.c @@ -120,13 +120,21 @@ authenticate_user(int curvt) /* check to make sure that user has a process on that tty */ /* this will fail for example when X is running on the tty */ while ((dentp = readdir(dp))) { + struct stat pbuf; + + sprintf(filename, "/proc/%s", dentp->d_name); + if (stat(filename, &pbuf)) + continue; + if (pbuf.st_uid != console_uid) + continue; + sprintf(filename, "/proc/%s/fd/0", dentp->d_name); - if (stat(filename, &buf)) continue; if (buf.st_dev == console_dev && buf.st_ino == console_ino && buf.st_uid == console_uid) + if (buf.st_dev == console_dev && buf.st_ino == console_ino) goto got_a_process; } ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use