Hack The Box: Evil CUPS
What will you learn? The "EvilCUPS" machine is a Medium-level Linux challenge involving a command injection flaw in CUPS (Common UNIX Printing System). This vulnerability (CVE-2024-47176) allows anyone, even without prior access, to remotely install a compromised printer over UDP port 631. This printer is set up to run commands during the document printing process, which is where the vulnerability is triggered.
The CUPS web interface, accessible on TCP port 631, lets users print a test page on the malicious printer. By doing so, an attacker gains access as the "lp" user—a printer admin. With this access, the attacker can view previous print jobs, one of which contains the root password, ultimately unlocking full control of the system.
ENUMERATION:
The following ports are open on the target: 631 (CUPS 2.4.2) and 22 (SSH) is open but did not resolve. There are UDP ports open in addition for CUPs.
CUPS uses a web interface running on the localhost where its being hosted typically, so we can navigate in our web browser to the IP@631 to find the following. We can also see that the version is CUPS 2.4.2.
Searching for exploits for version 2.4.2 reveals that according to this source, there are 4 CVEs (CVE-2024-47176, CVE-2024-47076, CVE-2024-47175, CVE-2024-47177) that in conjunction make us the capability to get remote code execution on the server hosting this version of CUPS: https://github.com/MalwareTech/CVE-2024-47176-Scanner.
That GitHub page ends with: https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/ which explains all of this in depth.
CVE-2024-47176 (CUPS-BROWSED)
The cups-browsed
service helps computers automatically find printers on a network. Modern systems do this by sending a general request to nearby printers, which then respond to let the computer know they're available. However, older systems would have printers periodically send out messages to any connected computers, announcing their presence.
One thing to note is that the service listens on a specific network port (631) and doesn’t distinguish between different types of requests. This means we could send just one message to this service and have CUPS (the printer software) respond, allowing us to add a printer. The simplest way to manage printer setup requests in code is by using the Python library ippserver
.
CVE-2024-47076 (LIBCUPSFILTERS) and CVE-2024-47175 (LIBPPD)
When CUPS (the printer software) tries to install a printer, it expects a list of specific attributes from ippserver
. Here’s what the Python code might look like:
class MaliciousPrinter(behaviour.StatelessPrinter): def __init__(self, command): self.command = command super(MaliciousPrinter, self).__init__() def printer_list_attributes(self): attr = { # Sets up necessary printer details (SectionEnum.printer, b'printer-uri-supported', TagEnum.uri): [self.printer_uri], (SectionEnum.printer, b'printer-more-info', TagEnum.uri): [f'"\n*FoomaticRIPCommandLine: "{self.command}"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'.encode()], }
These attributes are used to create a PPD (Postscript Printer Description) file, which is just a list of settings formatted as “*attribute: "value".” The libcupsfilter
library reads these values to set up the printer, but it doesn't check for potentially harmful inputs. In this code, a special character (\n
) is added to insert a new line and allow a command (FoomaticRIPCommandLine
) to be included in the file. Normally, this command couldn’t be added over the network, which is why it’s done this way. Proper input checks could prevent this from happening.
CVE-2024-47177 (Foomatic-RIP)
FoomaticRIP works like a translator that converts common file formats like JPEG or PDF into something printers can understand, such as Postscript. Usually, printers can't handle these file types directly, so FoomaticRIP steps in to make them compatible. In the CUPS system, this conversion is controlled by a setting called "FoomaticRIPCommandLine," which runs a command based on what’s provided in this key.
Exploiting FoomaticRIP for Command Execution
A public script, available here on GitHub created by IPPSEC, automates this process. The script starts a server (on port 12345) that sends specially crafted printer details to the target, including a Foomatic-RIP command. Next, it sends a message to the cups-browsed
service, telling it to add our "printer."
To execute a command, all you need to do is send a print job to this printer. Because printing test pages isn’t restricted, anyone can do this without needing to log in. To test, go to http://10.10.11.40:631
, select "Printers," choose the printer you set up, and from the "Maintenance" dropdown, pick "Print Test Page." This triggers the command.
The command, embedded as part of FoomaticRIPCommandLine
, includes code that executes the desired action when the print job is run.
INITIAL ACCESS:
We can fetch IPPSEC’s exploit to automate the process of these CVEs together and install the requirements with the following below.
We are able to use his exploit to specify our localhost, the target, and the command we want to execute which in this case will be a reverse shell. We will wait about 30 seconds and then it should take effect. I am running a reverse shell in the background.
We can go to the printers page and see that we’ve added a printer to the list.
When we click on the printer that was added we are able to print a test page. Select “Maintenance” and then under the drop down select test page. That will return a reverse shell to us as the lp
service account.
Now we catch a shell as the LP user account and have an initial foothold on the target, due to the EvilCUPS exploit chain.
The reverse shell will die after a certain amount of time because it is linked to the process used by the printer service. Once that request is complete / cleaned up it will terminate the shell and you will have to renew it by replicating the previous step with the test page- work fast.
PRIVILEGE ESCALATION:
We are running as the LP user who does not typically have many permissions. We are able to read cached printing jobs in the /var/spool/cups/
directory though. When we look at the completed jobs in the web interface we are able to see jobs ending with 1…2… and so on. The first job being ‘1’. Knowing where these jobs are stored by default, with execute permissions (no read permissions hence the failure of listing on the directories), we can read files if the names are known inside of the directory.
The default format for completed jobs are d<print job>-<page number>
and the print job needs to be 5 digits and the page field 3 digits. For job 1, page 1, the filename would be d00001-001
. The name consists of two parts: job data and job instance.
Below is the location where we have the jobs printing to by default inside of ‘cups’.
When we attempt to print the first job by the default naming convention we’re able to successfully print the contents of that job out.
We actually will get data exposure such as a clear-text password through doing this. We can switch user (su
) to the root account using these credentials. A bit higher on the output we actually see ‘pass.txt’ for the filename in the print job. This is why it is paramount to understand the threat of having this type of information revealed.
Beyond ROOT Comments:
We could also exfiltrate the files we’re able to read due to the predictable naming convention of jobs. There we should be able to run ps2pdf d00001-001 job.pdf
and we can see what is on the printed page. You can use python3
, netcat, or other utilities to transfer the file to your attacker machine.
Now we can see the PDF file and view what was waiting for someone to find in plain text.