Skip to content

CUPS Heap-based buffer overflow

Low
zdohnal published GHSA-4f65-6ph5-qwh6 Sep 20, 2023 · 1 comment

Package

libppd (Linux)

Affected versions

2.0rc2

Patched versions

None

Description

Copying the same vulnerability from CUPS to cover the fix in libppd, reported by @todb

CVE-2023-4504: OpenPrinting CUPS/libppd Postscript Parsing Heap Overflow

AHA! has discovered an issue with CUPS and libppd from OpenPrinting, and is publishing this disclosure in accordance with AHA!’s standard disclosure policy today, on Thursday, September 21, 2023. CVE-2023-4504 has been assigned to this issue.

Any questions about this disclosure should be directed to cve@takeonme.org.

Executive Summary

Due to failure in validating the length provided by an attacker-crafted CUPS document, CUPS version v2.5b1 and prior, by default, is susceptible to a heap-based buffer overflow, and possibly code execution. CVE-2023-4504 appears to be an instance of CWE-122, a heap-based buffer overflow.

Technical Details

The scan_ps function in the CUPS codebase provides functionality that scans through a string looking for the next Postscript object. When iterating through a string which contains an open parenthesis and ends with a single backslash (0x5c) character, the code incorrectly iterates forward a character without properly checking the bounds of the string resulting in a 1 byte read beyond the allocated heap buffer.

Snippet of the vulnerable code:

cups/cups/raster-interpret.c

1039 static _cups_ps_obj_t   *               /* O  - New object or NULL on EOF */
1040 scan_ps(_cups_ps_stack_t *st,           /* I  - Stack */
1041         char             **ptr)         /* IO - String pointer */
1042 {
...
1085   switch (*cur)
1086   {
1087     case '(' :                          /* (string) */
1088         obj.type = CUPS_PS_STRING;
1089         start    = cur;
1090
1091         for (cur ++, parens = 1, valptr = obj.value.string,
1092                  valend = obj.value.string + sizeof(obj.value.string) - 1;
1093              *cur;
1094              cur ++)
1095         {
1096           if (*cur == ')' && parens == 1)
1097             break;
1098
1099           if (*cur == '(')
1100             parens ++;
1101           else if (*cur == ')')
1102             parens --;
1103
1104           if (valptr >= valend)
1105           {
1106             *ptr = start;
1107
1108             return (NULL);
1109           }
1110
1111           if (*cur == '\\')
1112           {
1113            /*
1114             * Decode escaped character...
1115             */
1116
1117             cur ++;
1118
1119             if (*cur == 'b')
1120               *valptr++ = '\b';
1121             else if (*cur == 'f')
1122               *valptr++ = '\f';
1123             else if (*cur == 'n')
1124               *valptr++ = '\n';
1125             else if (*cur == 'r')
1126               *valptr++ = '\r';
1127             else if (*cur == 't')
1128               *valptr++ = '\t';
1129             else if (*cur >= '0' && *cur <= '7')
1130             {
1131               int ch = *cur - '0';
1132
1133               if (cur[1] >= '0' && cur[1] <= '7')
1134               {
1135                 cur ++;
1136                 ch = (ch << 3) + *cur - '0';
1137               }
1138
1139               if (cur[1] >= '0' && cur[1] <= '7')
1140               {
1141                 cur ++;
1142                 ch = (ch << 3) + *cur - '0';
1143               }
1144
1145               *valptr++ = (char)ch;
1146             }
1147             else if (*cur == '\r')
1148             {
1149               if (cur[1] == '\n')
1150                 cur ++;
1151             }
1152             else if (*cur != '\n')
1153               *valptr++ = *cur;
1154           }
1155           else
1156             *valptr++ = *cur;
1157         }

Line 1085 contains the case statement which provides the logic used to iterate through the given string.

On line 1091, the for loop within the case statement is used to iterate through each character after encountering an open paranthesis character (0x28), storing the pointer to the current character in cur.

On line 1111, the code checks if the current character is a backslash and finally, in line 1117, the character index is incremented without checking the length, now pointing to the null byte terminating the string.

Upon the next iteration of the loop, on line 1094, the loop now begins iterating through unallocated memory resulting in undefined behaviour.

A Base64 encoded blob of an example PostScript document that can trigger the issue is below.

L///KFwAY3V1ZQ==

Attacker Value

By providing this malformed PostScript document, an attacker could compromise the machine running the affected software, either CUPS or another application that uses the libppd library. Once compromised, this can provide an attacker a unique, privileged position in the targeted network.

Credit

This issue is being disclosed through the AHA! CNA and is credited to: zenofex and WanderingGlitch

Timeline

2023-07-27 (Thu): Initial findings presented at AHA! Meeting 0x00c9.
2023-08-21 (Mon): PoC validated and this disclosure drafted.
2023-08-23 (Wed): Disclosed to the project per [their GitHub repo’s instructions](https://github.com/OpenPrinting/cups/security).
2023-08-24 (Thu): Project acknowledged the vulnerability.
2023-09-20 (Wed): Project released fixed versions of CUPS [v2.4.7](https://github.com/OpenPrinting/cups/releases/tag/v2.4.7) and libppd [commit d09348b](https://github.com/OpenPrinting/libppd/commit/d09348b81150df69420848f7b978951e15a6a77b).
2023-09-21 (Thu): Public disclosure of [CVE-2023-4504](https://takeonme.org/cves/CVE-2023-4504.html).
@todb
Copy link

todb commented Sep 21, 2023

Severity

Low

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N

CVE ID

CVE-2023-4504

Weaknesses

Credits