↓ Skip to main content
  1. Blog/

cups2root: privilege escalation without vulnerabilities

·3 mins·

In September, researchers from V12 demonstrated cups2root — a new local privilege escalation technique in Linux based on the built-in mechanisms of the CUPS printing system. The cupsd scheduler does not restrict the path component of the device URI for non-file backends, so the root-privileged serial backend can write print data to an arbitrary file (including the configuration of CUPS itself), and substitution of the parameters of this configuration causes the root process to execute the attacker’s files.

This behavior was initially reported as vulnerability CVE-2026-95511, however CUPS upstream rejected it: creating a serial queue requires membership in SystemGroups (lpadmin) — an administrative role assigned by the superuser, that is, “the privilege boundary is not crossed”.

But for defenders, the problem remains: on Ubuntu Desktop, an ordinary user is by default a member of lpadmin, and the group becomes a path to root.

How the attack works:

In CUPS, you can create a queue that refers to the configuration of cupsd itself. This can be done by a user belonging to the lpadmin group (by default, an ordinary user is a member of it in a clean version of Ubuntu 26.04). After the queue is created, the “desired” configuration is written to it (via lp -d PRINTER_NAME -o RAW payload), namely:

Group <attacker-primary-group>, ServerRoot/ServerBin/RequestRoot/TempDir = /etc/cups/interfaces

After forcing a restart (a malformed IPP Create-Printer-Subscriptions request is sent, and then systemd restarts the crashed cupsd), the CUPS print scheduler reads the new configuration, follows the prepared path, and does what is specified in the configuration: it grants the user’s group write permissions to the /etc/cups/interfaces folder.

Next, the attacker creates symlinks to the CUPS binaries (so that cupsd does not crash), copies the payload into this folder, and creates the cups-exec wrapper script, which is run as part of normal CUPS operation as root. This script grants the payload the required permissions via chown 0:0 + chmod 6755 (setuid-root launcher). It remains only to run the payload with root privileges.

The directory for writing /etc/cups/interfaces/ is not chosen randomly; it must satisfy the following conditions:

  • files can be executed from it;
  • it supports the suid bit on executable files;
  • the process that changes permissions on the directory to which the parameters of the fake configuration point (ServerBin) must have the right to change permissions of this directory.

Detection:

Among the artifacts recorded when this technique is used, the following can be noted:

— The start event of the serial process by cupsd itself, which clearly indicates that some work with the device is occurring via URI serial:/etc/cups/cups-files.conf:

/usr/lib/cups/backend/serial serial:/etc/cups/cups-files.conf?baud=115200 <truncated> 

— Creation of a queue for a printer with the file cups-files.conf from the lpadmin process:

/usr/sbin/lpadmin -p cups2root -v serial:/etc/cups/cups-files.conf?baud=115200 <truncated> 

— Change of ownership (chown 0:0), change of permissions and granting the suid bit (chmod 6755), checking the user (id -u), and other suspicious events from the cups-exec process.

— Presence and execution of hidden files (starting with ‘.’) from the /etc/cups/interfaces/ directory. In the original PoC, this is the /etc/cups/interfaces/.c2rsh file.

Protection:

There is no patch for this behavior because it has not been recognized as a vulnerability. The main protection recommendation is removing the users, who do not administer printers in CUPS, from the lpadmin group.

Moral:

The cups2root technique shows that the privilege boundary is determined not only by the kernel and SUID binaries but also by the “administrative” groups of application services. Root processes with user-controlled configuration (paths, URIs, filters) are a potential channel for writing and execution as root, and membership in such groups is a privilege subject to control. Even without a formal CVE, detection should rely on the behavior of the chain lpadmin → cupsd → backend → cups-exec, not only on payload signatures.

Related