From Hacking Printers

Cross-site printing (XSP) attacks empower a web attacker to access the printer device as demonstrated by [1] who use a hidden Iframe to send HTTP POST requests to port 9100/tcp of a printer within the victim's internal network. The HTTP header is either printed as plain text or discarded based on the printer's settings. The POST data however can contain arbitrary print jobs like PostScript or PJL commands to be interpreted. In the following, the idea of cross-site printing is adapted and improved which enables a web attacker to perform most attacks described in wiki obtaining captured print jobs, using the victim's web browser acts as a carrier.





Enhanced cross-site printing

Instead of Iframes, we use XMLHttpRequest (XHR) JavaScript objects as defined in [2] to perform HTTP POST requests to internal printers. A limitation of the cross-site printing approach discussed so far is that data can only be send to the device, not received because of the same-origin policy [3]. This opts out all information disclosure attacks. To bend the restrictions of the same-origin policy, cross-origin resource sharing (CORS) [4] can be used – if the web server explicitly allows it by sending a special HTTP header field. In the scenario of cross-site printing, however, we have full control of what the requested ‘web server’ – which actually is a printer RIP accessed over port 9100/tcp – sends back to the browser. By using PostScript output commands we can simply emulate an HTTP server running on port 9100/tcp and define our own HTTP header to be responded – including arbitrary CORS Access-Control-Allow-Origin fields which instruct the web browser to allow JavaScript access to this resource and therefore punch a hole into the same-origin policy. A schematic overview of the attack is given below:





In such an enhanced variant of XSP – combined with CORS spoofing – a web attacker has full access to the HTTP response which allows her to extract arbitrary information like captured print jobs from the printer device. A proof-of-concept JavaScript snipplet is shown below:

job = "\x1B %-12345X\r

" + " %!\r

" + " (HTTP/1.0 200 OK \\ n) print\r

" + " (Server: PostScript HTTPD \\ n) print\r

" + " (Access-Control-Allow-Origin: * \\ n) print\r

" + " (Connection: close \\ n) print\r

" + " (Content-Length: ) print\r

" + "product dup length dup string cvs print\r

" + " ( \\ n \\ n) print\r

" + "print\r

" + " ( \\ n) print flush\r

" + "\x1B %-12345X\r

"; var x = new XMLHttpRequest () ; x.open ("POST", "http://printer:9100") ; x.send (job) ; x.onreadystatechange = function () { if (x.readyState == 4) alert (x.responseText) ; } ;

Limitations of cross-site printing

Note that PCL as page description language is not applicable for CORS spoofing because it only allows one single number to be echoed. PJL likewise cannot be used because unfortunately it prepends @PJL ECHO to all echoed strings, which makes it impossible to simulate a valid HTTP header. This however does not mean that enhanced XSP attacks are limited to PostScript jobs: PostScript can be used to respond with a spoofed HTTP header and the UEL can further be invoked to switch the printer language. This way a web attacker can also obtain the results for PJL commands. Two implementation pitfalls exist which deserve to be mentioned: First, a correct Content-Length for the data to be responded needs determined with PostScript. If the attacker cannot predict the overall size of the response and chunked encoding as well is not an option, she needs to set a very high value and use padding. Second, adding the Connection: close header field is important, otherwise HTTP/1.1 connections are kept alive until either the web client or the printer device triggers a timeout, which means the printer will not be accessible for some time.

If the printer device supports plain text printing the HTTP request header of the XHR is printed out as hard copy – including the Origin header field containing the URL that invoked the malicious JavaScript, thus making it hard for an attacker to stay silent. This is unavoidable, as we do not gain control over the printer – and under some circumstances can disable printing functionality – until the HTTP body is processed and the HTTP header has already been interpreted as plain text by the printer device. If reducing noise is a priority, the attacker can however try to first disable printing functionality with proprietary PJL commands as proposed in PJL jobmedia using other potential XSP channels like IPP, LPD, FTP or the printer's embedded web server. While all protocols could successfully be tested to deploy print jobs using variants of cross-protocol scripting as described by [5] and [6] they have some drawbacks beyond not providing feedback using spoofed CORS headers:

Cross-protocol access to LPD and FTP ports is blocked by various web browsers

Parameters for direct printing over the embedded web server are model-specific

The IPP standard requires the Content-type for HTTP POST requests being set to application/ipp [7] which cannot be done with XHR objects – it is however up to the implementation to actually care about incorrect types

A comparison of cross-site printing channels is given in below:

Channel Port No Feedback Unsolicited printouts Standardized Blocked by Raw 9100 - ✔ ✔ - Web 80 ✔ - - - IPP 631 ✔ - ✔ - LPD 515 ✔ - ✔ FF, Ch, Op FTP 21 ✔ - ✔ FF, Ch, Op, IE

One major problem of XSP is to find out the correct address or hostname of the printer. Our approach is to abuse WebRTC [8] which is implemented in most modern browsers and has the feature to enumerate IP addresses for local network interfaces. Given the local IP address, XHR objects are further used to open connections to port 9100/tcp for all 253 remaining addresses to retrieve the printer product name using PostScript and CORS spoofing which only takes seconds in our tests. If the printer is on the same subnet as the victim's host its address can be detected solely using JavaScript. WebRTC is in development for Safari and supported by current versions of Firefox, Chrome and Microsoft Edge. Internet Explorer has no WebRTC support, but VBScript and Java can likewise be used to leak the local IP address. If the address of the local interface cannot be retrieved, we apply an intelligent brute-force approach: We try to connect to port 80 of the victim's router using XHR objects. For this, a list of 115 default router addresses from various Internet-accessible resources was compiled. If a router is accessible, we scan the subnet for printers as described before.

Proof-of-concept

A proof-of-concept implementation demonstrating that advanced cross-site printing attacks are practical and a real-world threat to companies and institutions is available at hacking-printers.net/xsp/. It was successfully tested on Firefox 48, Chrome 52, Opera 39 and Internet Explorer 10. It is worth noting that the Tor Browser blocks the attack because it tries to connect to all addresses – including local ones – through the Tor network meaning XSP requests never reach the intranet printer.

Update: To prevent cross-site printing, port 9100/tcp may be blocked in future releases of Firefox [9] and Chrome [10].

→ Related articles: Fundamentals, Attack carriers, Port 9100 printing, BeEF



