Paranoids’ Vulnerability Research: PrinterLogic Issues Security Alert
Note: Verizon Media is now known as Yahoo.
Summary:
- On Friday, January 21, 2022, PrinterLogic published a security notification with links to software updates that resolve several vulnerabilities the Paranoids’ Vulnerability Research Team (VRT) found in their product.
- In order to exploit the vulnerabilities in the unpatched version, attackers would need a privileged network position — such as access through a VPN or another (SSRF, perhaps) vulnerability in an appliance on the edge.
- If you haven't already, please update to the latest version of PrinterLogic, 19.1.1.13-SP10.
In Summer 2021, the Paranoids’ Vulnerability Research Team (VRT) discovered a series of weaknesses in a popular software suite that centralizes the management of printer assignment.
Soon after, the team reached out to start working with the vendor, PrinterLogic, to issue CVEs and fixes for those vulnerabilities.
It’s a reminder that as your company grows in size, it’s only natural to reach for enterprise products that simplify management of key functionality that many of your employees use (in this case, printers). A byproduct of centralized management, however, is centralized risk.
To be clear, a majority of installations of PrinterLogic are not internet-facing.
That means attackers would first need a privileged network position — such as access through a VPN or another (SSRF, perhaps) vulnerability in an appliance on the edge — to exploit the PrinterLogic Web Stack server.
Regardless, it’s important to patch because of the severity of the vulnerabilities the Paranoids VRT disclosed to the vendor last year.
Patches were issued on Friday, January 21. And all versions previous to 19.1.1.13-SP10 are vulnerable.
Additional information about these security advisories, from PrinterLogic, can be found at their security bulletins page at https://www.printerlogic.com/security-bulletin/.
Introduction
Now to the nitty-gritty. Last Friday, January 21, 2022, an update was released for PrinterLogic Web Stack that fixed a collection of vulnerabilities identified by our Vulnerability Research Team, some of which allowed for remote code execution on that Web Stack server:
- CVE-2021-42631: Object Injection leading to RCE
- CVE-2021-42635: Hardcoded APP_KEY leading to RCE
- CVE-2021-42638: Misc command injections leading to RCE
- CVE-2021-42633: SQLi may disclose audit logs
- CVE-2021-42637: Blind SSRF
- CVE-2021-42639: Misc reflected XSS
- CVE-2021-42640: Driver assignment IDOR
- CVE-2021-42641: Username/email info disclosure
- CVE-2021-42642: Printer console username/password info disclosure
If you haven’t already, please update to the latest version, 19.1.1.13-SP10. These vulnerabilities were identified through a combination of reverse engineering and manual code review (aided by Semgrep).
In this post, we’ll briefly break down one of the vulnerabilities, showcase what initial exploitation looks like, and finally, show how an attacker going the extra mile can significantly increase the impact to an enterprise target. Additionally, we’ll take a look at why PrinterLogic, and other products like it, may serve as a strategic target for attackers.
PrinterLogic Primed
PrinterLogic is an enterprise software product that enables simplified, centralized management of a fleet of printers. Using the self-service portal, end users who have the PrinterInstallerClient application installed can quickly find and install their own printers with a single click. Companies typically run a PrinterLogic Web Stack server internally, and get the PrinterInstallerClient application installed on their endpoints.
There are a few qualities that make products like PrinterLogic stand out as valuable footholds for an attacker:
- It has an agent that is designed to run on many or all endpoints
- That agent can install components or configure the endpoint in some way with administrative level permissions
- Finally, the vendor publishes a customer list, which shows exactly what the return on investment is for attackers. If an attacker finds one vulnerability, extensively compromise over 140 high-profile organizations.
For these reasons, we decided to audit the PrinterLogic Web Stack for vulnerabilities.
Time to 0day
As part of the vulnerability research setup, we installed PrinterLogic Web Stack in a virtual machine. A client VM running macOS was also created, which ran the PrinterInstallerClient endpoint software.
Our timeline was as follows:
- April 19th - Set up a testing environment
- May 4th - Decrypted the Web Stack server code, which allowed us to begin performing source auditing
- May 6th - Identified initial pre-auth object injection vulnerability (CVE-2021-42631)
- May 19th - Began Work on developing a full exploitation toolkit around the vulnerability
- June 4th - Full exploit was completed and ready for operational use
We continued auditing code and identified additional vulnerabilities of varying types and severity. These vulnerabilities were shared with PrinterLogic as part of a coordinated disclosure process.
Vulnerability Analysis
While we won’t disclose the exact location of the vulnerability in order to give customers some time to patch, we can still take a look at a snippet that is close to the real code:
This is a pretty classic example of PHP Object Injection. Attacker controlled input is taken from a GET parameter (param) and base64 decoded, then that input is used as the input to unserialize.The particular endpoint this code is based on is accessible without requiring authentication.
PrinterLogic is not alone, object injection has historically been a problem bug class in PHP applications, including even big names like ConcreteCMS, Magento, and Moodle.
Object injection vulnerabilities allow an attacker to instantiate an object of an arbitrary class in the target, with the values of the object’s properties being attacker controlled. Some classes may implement what are called magic methods, which can be triggered in specific ways during the lifecycle of that object. Some of the often abused magic methods include (but are not limited to):
- __wakeup - Triggered just after instantiation
- __destruct - Triggered when the object is cleaned up
- __call - Triggered a method is called that is not implemented in the class
- __toString - Triggered when the object is converted to a string
If a class implements a magic method such as __wakeup, leveraging properties from the class (which in this case are attacker controlled), and calls into or instantiates objects from other libraries that perform unsafe operations with the input, or directly uses the input in an unsafe way (e.g. as part of a command string passed to exec), dangerous exploitation primitives can arise. These call chains that are useful for exploitation, where the attacker controls the properties, are called POP (property oriented programming) chains. A few examples of useful primitives that can result include command execution, arbitrary file write, and arbitrary file read.
An important caveat is that the definition for the class being instantiated must be loaded by the time unserialize is called. In the case of our target, things are a little easier. Laravel supports autoloading of classes in dependencies, significantly increasing the number of classes available for object instantiation.
Initial Compromise
In the case of this particular vulnerability, we can use off the shelf tooling to get initial code execution. phpggc implements gadget chains for a variety of frameworks including Laravel, CakePHP, WordPress, and others. We can use the Laravel/RCE2 chain, which is triggered through __destruct:
By supplying the payload as input to the API endpoint which exposes the vulnerable unserialize call, we achieve code execution as the IIS APPPOOL\DefaultAppPool user.
Taking Code Execution to the Next Level
We’ll assume that by leveraging the aforementioned vulnerability, we’ve managed to drop a webshell or other implant that grants persistent access to the Web Stack server. Now we’ll discuss how an attacker can ratchet up the impact to move laterally into arbitrary workstations by leveraging PrinterLogic’s own features.
One of PrinterLogic’s core features is single-click printer installation. Installing a printer for an endpoint properly sometimes requires supplemental software or configurations being deployed to their system for proper setup. This is solved for with driver packages, which can be associated with a printer in the inventory. When a user clicks to install a printer from the self-service portal, the PrinterLogic workstation client (PrinterInstallerClient) will pull down the associated driver package and install it. But what actually happens behind the curtain to install the driver, and how can it be abused for exploitation?
Driver Package Installation
For MacOS and Linux, the driver package format is fairly simple. All of the contained resources are wrapped as a bzip2 compressed tarball. At the heart of the driver package is the path_map file, which specifies the list of resources to install, and where they’ll be installed to:
In the above example, the driver package contains only one resource to install, which is the printer’s PPD (PostScript Printer Description). A more complicated driver package may contain supplementary applications or libraries that must be installed on the target. PrinterInstallerClient will decompress and unpack the tarball to a working directory, then inspect the contents of path_map. It will copy the file or folder named by the json key(s) from the working directory to the path specified in the map. In the above example, the file named 0 from the archive will be placed at /etc/cups/ppd/Xerod.ppd). You can include any number of resources to place at arbitrary locations, so long as each has a unique number and is stored alongside the path_map file.
On Windows, a different driver packaging format is used. Targeting Windows hosts also requires the attacker to code-sign their payload for it to be unpacked successfully on the target.
The bytes for these driver packages are stored in the MySQL database attached to the PrinterLogic Web Stack server, in the table ppp_driver_package_fragments as a base64 encoded blob. Each driver will have a corresponding metadata entry in ppp_driver_packages, specifying all the relevant properties of the driver such as the name, version, release date, and size. To associate a driver package with the appropriate printer, an entry is set in the ppp_printer_os_settings table which ties a printer ID, an OS ID, and a driver ID together.
If we can control the contents of a driver package that is delivered to an endpoint by manipulating the database, we effectively have an arbitrary filesystem write on connected endpoints. The added bonus is that PrinterInstallerClient runs as root.
Achieving Code Execution
With an arbitrary write in hand, now we need to make the jump to executing code on the endpoint that receives the driver package. Luckily, we have a few options across each platform that aren’t particularly difficult. Let’s take a look at one.
On macOS, the periodic service runs scheduled maintenance scripts. The /etc/periodic/ directory holds daily, monthly, and weekly directories, under which a collection of scripts sit. If you place a script under any of these directories, the periodic service will execute it (roughly) once per respective period (daily, monthly, or weekly).
The periodic service can sometimes miss its execution window, at which point it reschedules to run at another time. By writing a shell script marked executable to /etc/periodic/daily/hax.sh, we could run arbitrary commands (roughly) once per day. As part of our exploit we’ll craft a driver package that, when installed, writes a shell script or other implant to the periodic service’s daily directory. When the service fires, we’ll achieve code execution on the endpoint.
Door is Push, Not Pull
With our knowledge thus far, we can backdoor existing driver packages in the PrinterLogic Web Stack server. When a user visits the self service portal to install a printer, the backdoored driver would be installed and grant the attacker code execution. But this has the downside of requiring the user to be enticed to install the malicious printer. This makes it difficult to perform targeted exploitation of individual users, since we can’t guarantee that they’ll download our driver. Is it possible to switch the model around, so that we can push drivers to arbitrary endpoints, rather than waiting for users to choose to install them? It turns out there’s a feature for just that.
Printers can be deployed to endpoints running PrinterInstallerClient according to a set of scoping criteria. Some of the scoping criteria you can use to specify the installation group include:
- Active Directory User, Computer, Group, or OU
- IP address range
- Hostname
- MAC Address
A hostname can even be specified as a wildcard (*), which would scope the printer to all endpoints that are clients of the Web Stack server. These deployment settings are stored in the ppp_tree_associations table, which matches up an association between a printer node (specified in the ppp_tree table) and an endpoint (specified in the ppp_hostnames table).
By default, PrinterInstallerClient will check in with the Web Stack server every 4 hours to request the latest printer configuration for that endpoint. If there is an updated configuration or new printers have been assigned, it will download the relevant driver packages and install them as appropriate.
With all pieces in place, we now understand that an attacker who compromises the Web Stack server itself can get remote code execution on all connected endpoints that are clients of the Web Stack server, or compromise select endpoints as they see fit, without requiring further user interaction.
Place in the Oven, Bake at 350º
All the required capabilities for our exploit are now identified, and just need to be wrapped up nicely and baked into a clean exploit:
- Directly compromise the PrinterLogic Web Stack server by leveraging the pre-authentication object injection vulnerability
- Manipulate the database to insert a new driver package in the ppp_driver_package and ppp_driver_package_fragments tables
- Create a new printer within the ppp_printers and ppp_tree tables, and associate it with our malicious driver package via the ppp_printer_os_settings table
- Assign the malicious printer to arbitrary (or all) workstations in the enterprise that are running PrinterInstallerClient by creating association entries in ppp_tree_associations.
- Wait patiently for shells to rain across the enterprise
In order to make some of the database operations easier to handle, we ended up reusing some of the existing PrinterLogic PHP code in our full exploit to manipulate these tables using DAO objects. Check out the video shown below to see the full exploit working. In order to keep the video length short (less than 24 hours), we manually triggered the periodic service. But, this service typically runs on its own as appropriate.
About the Author
Blaine Herro is a member of the Paranoids' Vulnerability Research Team and Yahoo’s Red Team. He conducts research to uncover vulnerabilities in products used by Yahoo and the greater industry, as well as develops tooling and capability for use in full scale Red Team operations.