Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <f09b6919-97dc-a406-e6c4-add189505d48@redhat.com>
Date: Wed, 20 Sep 2023 14:56:51 +0200
From: Zdenek Dohnal <zdohnal@...hat.com>
To: oss-security@...ts.openwall.com
Subject: CVE-2023-4504 cups, libppd: Postscript parsing heap-based buffer
 overflow

Hi all,

there is the same vulnerability in CUPS and libppd projects.

The original CUPS report:


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

AHA! <https://takeonme.org/> has discovered an issue with CUPS from 
OpenPrinting, and is publishing
this disclosure in accordance with AHA!'s standard disclosure policy 
<https://takeonme.org/cve.html> today,
on $DATE. CVE-2023-4504 <https://takeonme.org/cves/CVE-2023-4504.html> 
has been assigned to this issue.

Any questions about this disclosure should be directed to
*cve@...eonme.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 <https://takeonme.org/cves/CVE-2023-4504.html> appears to 
be an instance of CWE-122 
<https://cwe.mitre.org/data/definitions/122.html>, 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 software. 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 <https://mastodon.social/@zenofex> and WanderingGlitch 
<https://infosec.exchange/@WanderingGlitch>

CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:N

========================================================================================

*Notes:*

- I've tried to reproduce the issue with any filter and PPD using the 
code, but I didn't get crash nor valgrind report - maybe ASAN could show 
the vulnerability, but I haven't tried this.

- the vulnerability should show up when you send the postscript string 
above to a printer which requires cups-raster format (the vulnerable 
code is in path when you convert postscript to raster), which is not 
common these days - nor sending postscript or producing cups-raster.

*Updated Notes:*

I found out the vulnerability should happen only if malformed PostScript 
sequence is in PPD file, not if the sequence is sent for printing 
(vulnerable code was in library function used for reading PS sequence 
which are in PPDs).

*Commits fixing the issue:*

cups: https://github.com/OpenPrinting/cups/commit/2431caddb7e6

libppd: https://github.com/OpenPrinting/libppd/commit/262c909ac5


Have a nice day,


Zdenek Dohnal

CUPS 2.4.x release manager


-- 
Zdenek Dohnal
Senior Software Engineer
Red Hat, BRQ-TPBC

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.