Print job retention
The most valuable data found on printers is print jobs themselves. Even in a digital world, important documents are printed and kept as hard copies. In high security environments with encrypted hard disks and network traffic, printers might be the weakest link in the security chain. However, even with access to the file system of a printer device an attacker cannot retrieve print jobs unless they have explicitly been stored. This is because print jobs are processed on-the-fly in memory only and never touch the hard disk. This article discusses legitimate print job retention features and methods to actively capture documents to being printed.
Job Retention
Some printers have stored print jobs accessible from the web server (for example, the HP DesignJet Z6100ps). Usually however, job retention must be explicitly activated for a certain print job which can be done using standard PJL commands or proprietary PostScript code. Jobs are then kept in memory and can be reprinted from the control panel.
PJL
Legitimate job retention can be enabled for the current document by setting the PJL HOLD variable [1] as shown below:
@PJL SET HOLD=ON [actual data to be printed follows]
Hold jobs are kept in memory and can be reprinted from the printer's control panel. This feature is supported by various printers, however as it seems only some Epson devices allow permanent job retention beeing set using @PJL DEFAULT HOLD=ON
.
How to test for this attack?
Use PRET's hold command in pjl mode and to check if permanent job retention can be set:
./pret.py -q printer pjl Connection to printer established Welcome to the pret shell. Type help or ? to list commands. printer:/> hold Setting job retention, reconnecting to see if still enabled Retention for future print jobs: OFF
Who can perform this attack?
This feature can only be exploited by a physical/local attacker to reprint stored jobs.
PostScript
PostScript offers similar functionality which however is model- and vendor-specific. For the HP LaserJet 4k series and various Kyocera printers, job retention can be enabled by prepending the following commands to a PostScript document:
<< /Collate true /CollateDetails
<< /Hold 1 /Type 8 >> >> setpagedevice
While it is theoretically possible to permanently enable PostScript job retention using the startjob operator, this setting is explicitly reset by CUPS at the beginning of each print job using << /Collate false >> setpagedevice
. To counter this protection mechanism however, the attacker can permanently redefine the setpagedevice
operator to have no effect at all.
How to test for this attack?
Use PRET's hold command in ps mode:
./pret.py -q printer ps Connection to printer established Welcome to the pret shell. Type help or ? to list commands. printer:/> hold Job retention enabled.
Who can perform this attack?
This feature can only be exploited by a physical/local attacker to reprint stored jobs.
Job Capture
It is possible but uncommon to activate job retention in the printing dialog as discussed above. With PostScript however, one has complete access over the current print job and with the startjob operator, it is even possible to break out of the server loop and access future jobs. Such functionality has the potential to capture all documents if PostScript is used as a printer driver.
PostScript
With the capability to hook into arbitrary PostScript operators it is possible to manipulate and access foreign print jobs. To parse the actual datastream send to the printer, one can apply a pretty cool feature of the PostScript language: to read its own program code as data using the currentfile operator. This way, the whole datastream to be processed by the PostScript interpreter can be accessed by reading and stored to a file on the printer device. If the printer does not offer file system access, captured documents can be stored in memory, for example within permanent PostScript dictionaries. One practical problem is to decide which operator should be hooked as one does not gain access to the datastream until this operator is processed by the PostScript interpreter. As an attacker wants to capture print jobs from the very beginning, the redefined operator must be the very first operator contained in the PostScript document. Fortunately all documents printed with CUPS are pressed into a fixed structure beginning with currentfile /ASCII85Decode filter /LZWDecode filter cvx exec
. Based on the assumption of such a fixed structure, the attacker can capture documents from the beginning and execute (aka print) the file afterwards. For printing systems other than CUPS this attack should also be possible, but operators need to be adapted. Note that the PostScript header which usually includes media size, user and job names cannot be captured using this method because we first hook into at the beginning of the actual document. Another possibility may be BeginPage if supported by the printer. This vulnerability has presumably been present in printing devices for decades as solely language constructs defined by the PostScript standard are abused.
How to test for this attack?
Use PRET's capture command in ps mode:
./pret.py -q printer ps Connection to printer established Welcome to the pret shell. Type help or ? to list commands. printer:/> capture Print job operations: capture <operation> capture start - Record future print jobs. capture stop - End capturing print jobs. capture list - Show captured print jobs. capture fetch - Save captured print jobs. capture print - Reprint saved print jobs. printer:/> capture start Future print jobs will be captured in memory! printer:/> exit
Now, print arbitrary documents (make sure PRET is disconnected to not block the printing channel). Afterwards, you can list, fetch or reprint captured documents:
./pret.py -q printer ps Connection to printer established Welcome to the pret shell. Type help or ? to list commands. printer:/> capture list Free virtual memory: 16.6M | Limit to capture: 5.0M date size user jobname creator ─────────────────────────────────────────────────────────────────────────────── Jan 25 18:38 3.1M - - - Jan 25 18:40 170K - - - printer:/> capture fetch Receiving capture/printer/690782792 3239748 bytes received. Receiving capture/printer/690646210 174037 bytes received. printer:/> capture print printing... printing... 2 jobs reprinted printer:/> capture stop Stopping job capture, deleting recorded jobs
Who can perform this attack?
Anyone who can print, for example through USB drive or cable, Port 9100 printing or Cross-site printing.- ↑ Printer Job Language Technical Reference Manual, HP Inc., 1997, ch. 10-2