Table of Contents

1) Introduction

LACT is a daemon and graphical user interface for controlling GPU devices on Linux. Beyond providing access to device information, features like GPU overclocking and cooler control are included. In a recent update of LACT a Polkit policy appeared, which triggered a review for the corresponding package in openSUSE Tumbleweed.

During the review we identified a Polkit authentication bypass and a predictable temporary file name issue, resulting in potential local root exploits. The following sections describe the security issues in detail. This report is based on release v0.10.0 of LACT.

2) The LACT Daemon

LACT contains a systemd service unit which runs the lact program as a daemon with full root privileges. No systemd service hardening is in place. The daemon exposes a UNIX domain socket in /run/lactd.sock. Upstream intends this socket to be accessible by members of either the wheel or the sudo group. In openSUSE Tumbleweed a stricter opt-in model is used instead: only members of a dedicated lact group are allowed to access the socket.

The socket is used to exchange LACT-specific messages based on the Rust serde serialization format. In version 0.10.0 of LACT, some of the message types supported by the daemon have been additionally protected by Polkit authentication checks.

3) Security Issues

3.1) Polkit Authentication Bypass due to PID Race (CVE-2026-75037)

The Polkit authentication in the LACT daemon relies on function check_auth(), which authenticates the client solely based on its PID, which is a known misuse in Polkit authentication. A malicious client can attempt to send out the request, then cycle PIDs in an attempt to replace its own PID by a privileged process to alter the outcome of the Polkit authentication check.

Polkit authentication in LACT is used to prevent unprivileged users from adding so-called profile hooks to the LACT configuration. These hooks are basically scripts that will be executed with full root privileges as soon as a LACT profile is (de)activated. As a result, a Polkit authentication bypass in this context allows to gain full root access. Due to the access restrictions to the LACT UNIX domain socket, the privilege escalation is only possible for users that already own a certain level of privilege (i.e. membership in the wheel, sudo or lact group).

We assigned CVE-2026-75037 to track this issue. Upstream fixed this flaw in commit d0478fe4 by additionally passing the caller’s UID to the Polkit daemon, preventing race conditions from influencing the outcome of the authorization.

Shortly before publication of this report the upstream author informed us about a more deeply rooted issue in Rust crate zbus_polkit which affects various Rust applications that attempt to pass client UIDs to Polkit. Due to a D-Bus data type mismatch the UID seems to be silently dropped from the authentication data, resulting again in the same weakness. An update of the zbus_polkit crate, which is part of Rust vendor sources of various packages is thus strongly recommended to developers of affected applications and distributors. For LACT a bugfix commit is already available.

3.2) Predictable Temporary File Creation in Snapshot API (CVE-2026-75038)

The generate_snapshot() function is accessible without Polkit authentication. It creates a tarball in paths of the pattern /tmp/LACT-v{DAEMON_VERSION}-snapshot-{datetime}.tar.gz, which are predictable. The system calls used by the daemon to create these files are as follows (strace excerpt):

openat(AT_FDCWD, "/tmp/LACT-v0.10.0-snapshot-20260819-101819.tar.gz" O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 17
[...]
fchmod(17, 0775) = 0

As can be seen there is no O_NOFOLLOW and no O_EXCL flag passed here. On systems without the protected_symlinks and protected_regular sysctls enabled this allows various attack vectors:

  • local Denial-of-Service: by pointing symbolic links to vital system files, the target files will be overwritten, breaking the system. The content of the tarball is not attacker-controlled (or at best partly and indirectly), therefore further privilege escalation should not be possible this way.
  • Denial-of-Service against the LACT daemon: by placing a special file like a FIFO named pipe in this location the daemon will block forever, trying to write data to it.
  • local information leak: the tarball contains data about GPU devices and LACT configuration; mostly information that is available to all users in the system anyway. By placing a symlink to a private file, however, the target file will end up with world-readable permissions, due to the fchmod(). The content of the file will be lost due to the O_TRUNC flag during openat(), but the file might be re-populated with sensitive data by privileged processes at a later time, without restoring the original safe file permissions.

With the kernel hardenings protected_symlinks and protected_regular enabled, which is the default on most systems, these issues are fortunately not exploitable.

A side effect of how the tarball creation in LACT works at the moment is that the snapshot tarball cannot be deleted by the client that asked for it, since it is owned by root. As there is already a UNIX domain socket available, we suggested to upstream to use file descriptor passing instead: the client passes to the daemon an already open file where the tarball data will be written. This way neither open() nor chmod() calls will be necessary in the privileged daemon, resulting in a much cleaner design.

We assigned CVE-2026-75038 to track this issue. Upstream fixed this flaw in commit 2aae677d0 by invoking File::create_new() instead of plain File::create(). This results in the O_EXCL flag to be passed to the openat() system call shown above, preventing both opening already existing files and following symbolic links.

4) Coordinated Disclosure and Upstream Bugfix Release

We offered coordinated disclosure to upstream, who declined and quickly pushed bugfixes to the LACT GitHub project instead. The bugfixes (including the zbus_polkit fix) are included in the recently published release v0.10.1.

5) Timeline

2026-08-19 We reached out privately to the owner of the LACT GitHub project, offering coordinated disclosure.
2026-08-19 We received a reply in which upstream declined coordinated disclosure, pointing out two public bugfix commits instead.
2026-08-24 Due to a lost email on our end we only noticed at this time that there was an upstream reply and started acting on it.
2026-08-25 We assigned CVEs for the issues in this report and shared them with upstream.
2026-08-25 Our LACT packager backported the upstream bugfixes allowing us to progress with the openSUSE Tumbleweed LACT update to version 0.10.0.
2026-08-28 We received a follow-up email from upstream pointing out that an issue in Rust’s zbus_polkit causes the subject’s UID information to be silently dropped from Polkit authentication calls.
2026-08-28 Publication of this report.

6) References