{"id":201,"date":"2004-08-06T21:46:48","date_gmt":"2004-08-06T14:46:48","guid":{"rendered":""},"modified":"2004-08-06T21:46:48","modified_gmt":"2004-08-06T14:46:48","slug":"bad-png-crash-or-0wn3d","status":"publish","type":"post","link":"https:\/\/deepquest.code511.com\/blog\/2004\/08\/bad-png-crash-or-0wn3d\/","title":{"rendered":"bad PNG= crash or 0wn3d?"},"content":{"rendered":"<p>A malicious website serving a malicious PNG file could:<br \/>\n                   &#8211; Compromise the browsers of visitors.<br \/>\n                   &#8211; A malicious PNG could be sent via e-mail and compromise<br \/>\n                     the e-mail viewer of the recipient.<br \/>\n                   &#8211; For systems with user-providable images for &#8220;face<br \/>\n                     browsers&#8221;, a local system compromise could be possible via<br \/>\n                     a malicious PNG.<\/p>\n<p>Want more?<!--more--><\/p>\n<p>[url=http:\/\/deepquest.code511.com\/pngvery_bad.png]Proof of concept[\/url]<br \/>\nwill only crash you browser.<\/p>\n<p>This advisory lists code flaws discovered by inspection of the libpng-1.2.5<br \/>\ncode. Only the first one has been examined in practice to confirm<br \/>\nexploitability. The other flaws certainly warrant fixing.<\/p>\n<p>A patch which should plug all these issues is appended beneath the advisory.<br \/>\nNOTE! This patch serves as demo purposes for the flaws only. An official<br \/>\nv1.2.6 libpng with an official, slightly different fix will be released by<br \/>\nthe libpng team in parallel with this advisory.<\/p>\n<p>1) Remotely exploitable stack-based buffer overrun in png_handle_tRNS<br \/>\n(pngrutil.c)<\/p>\n<p>If a PNG file is of the correct format, a length check on PNG data is missed<br \/>\nprior to filling a buffer on the stack from the PNG data. The exact flaw would<br \/>\nseem to be a logic error; failure to bail out of a function after a warning<br \/>\ncondition is hit, here:<\/p>\n<p>      if (!(png_ptr->mode &#038; PNG_HAVE_PLTE))<br \/>\n      {<br \/>\n         \/* Should be an error, but we can cope with it *\/<br \/>\n         png_warning(png_ptr, &#8220;Missing PLTE before tRNS&#8221;);<br \/>\n      }<br \/>\n      else if (length > (png_uint_32)png_ptr->num_palette)<br \/>\n      {<br \/>\n         png_warning(png_ptr, &#8220;Incorrect tRNS chunk length&#8221;);<br \/>\n         png_crc_finish(png_ptr, length);<br \/>\n         return;<br \/>\n      }<\/p>\n<p>We can see, if the first warning condition is hit, the length check is missed<br \/>\ndue to the use of an &#8220;else if&#8221;.<\/p>\n<p>A scarier possibility is targetted exploitation by e-mailing a nasty PNG to<br \/>\nsomeone who uses a graphical e-mail client to decode PNGs with a vulnerable<br \/>\nlibpng.<\/p>\n<p>2) Dangerous code in png_handle_sBIT (pngrutil.c) (Similar code in<br \/>\npng_handle_hIST).<\/p>\n<p>Although seemingly not exploitable, there is dangerous code in this function.<br \/>\nIt relies on checks scattered elsewhere in the code in order to not overflow<br \/>\na 4-byte stack buffer. This line here should upper-bound the read onto the<br \/>\nstack to 4 bytes:<\/p>\n<p>   png_crc_read(png_ptr, buf, truelen);<\/p>\n<p>3) Possible NULL-pointer crash in png_handle_iCCP (pngrutil.c) (this flaw is<br \/>\nduplicated in multiple other locations).<\/p>\n<p>There are lots of lines such as these in the code:<\/p>\n<p>   chunkdata = (png_charp)png_malloc(png_ptr, length + 1);<\/p>\n<p>Where &#8220;length&#8221; comes from the PNG. If length is set to UINT_MAX then length + 1 will equate to<br \/>\nzero, leading to the PNG malloc routines to return NULL and<br \/>\nsubsequent access to crash. These lengths are sometimes checked to ensure<br \/>\nthey are smaller that INT_MAX, but it is not clear that all code paths perform<br \/>\nthis check, i.e. png_push_read_chunk in pngpread.c does not do this check<br \/>\n(this is progressive reading mode as used by browsers).<\/p>\n<p>4) Theoretical integer overflow in allocation in png_handle_sPLT (pngrutil.c)<\/p>\n<p>This isn&#8217;t likely to cause problems in practice, but there&#8217;s the possibility<br \/>\nof an integer overflow during this allocation:<\/p>\n<p>   new_palette.entries = (png_sPLT_entryp)png_malloc(<br \/>\n       png_ptr, new_palette.nentries * sizeof(png_sPLT_entry));<\/p>\n<p>5) Integer overflow in png_read_png (pngread.c)<\/p>\n<p>A PNG with excessive height may cause an integer overflow on a memory<br \/>\nallocation and subsequent crash allocating row pointers. This line is possibly<br \/>\nfaulty; I can&#8217;t see anywhere that enforces a maximum PNG height:<\/p>\n<p>      info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,<br \/>\n         info_ptr->height * sizeof(png_bytep));<\/p>\n<p>6) Integer overflows during progressive reading.<\/p>\n<p>There are many lines like the following, which are prone to integer overflow:<\/p>\n<p>      if (png_ptr->push_length + 4 > png_ptr->buffer_size)<\/p>\n<p>It is not clear how dangerous this is.<\/p>\n<p>7) Other flaws.<\/p>\n<p>There is broad potential for other integer overflows which I have not spotted &#8211;<br \/>\nthe amount of integer arithmetic surrounding buffer handling is large,<br \/>\nunfortunately.<\/p>\n<p>CESA-2004-001 &#8211; rev 3<br \/>\nChris Evans<br \/>\n[email]chris@scary.beasts.org[\/email]<\/p>\n<p>[Advertisement: I am interested in moving into a security related field<br \/>\n full-time. E-mail me to discuss.]<\/p>\n<p>diff -ru libpng-1.2.5\/png.h libpng-1.2.5.fix\/png.h<br \/>\n&#8212; libpng-1.2.5\/png.h  2002-10-03 12:32:26.000000000 +0100<br \/>\n+++ libpng-1.2.5.fix\/png.h      2004-07-13 23:18:10.000000000 +0100<br \/>\n@@ -835,6 +835,9 @@<br \/>\n \/* Maximum positive integer used in PNG is (2^31)-1 *\/<br \/>\n #define PNG_MAX_UINT ((png_uint_32)0x7fffffffL)<\/p>\n<p>+\/* Constraints on width, height, (2 ^ 24) &#8211; 1*\/<br \/>\n+#define PNG_MAX_DIMENSION 16777215<br \/>\n+<br \/>\n \/* These describe the color_type field in png_info. *\/<br \/>\n \/* color type masks *\/<br \/>\n #define PNG_COLOR_MASK_PALETTE    1<br \/>\ndiff -ru libpng-1.2.5\/pngpread.c libpng-1.2.5.fix\/pngpread.c<br \/>\n&#8212; libpng-1.2.5\/pngpread.c     2002-10-03 12:32:28.000000000 +0100<br \/>\n+++ libpng-1.2.5.fix\/pngpread.c 2004-07-13 23:03:58.000000000 +0100<br \/>\n@@ -209,6 +209,8 @@<\/p>\n<p>       png_push_fill_buffer(png_ptr, chunk_length, 4);<br \/>\n       png_ptr->push_length = png_get_uint_32(chunk_length);<br \/>\n+      if (png_ptr->push_length > PNG_MAX_UINT)<br \/>\n+         png_error(png_ptr, &#8220;Invalid chunk length.&#8221;);<br \/>\n       png_reset_crc(png_ptr);<br \/>\n       png_crc_read(png_ptr, png_ptr->chunk_name, 4);<br \/>\n       png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;<br \/>\n@@ -638,6 +640,8 @@<\/p>\n<p>       png_push_fill_buffer(png_ptr, chunk_length, 4);<br \/>\n       png_ptr->push_length = png_get_uint_32(chunk_length);<br \/>\n+      if (png_ptr->push_length > PNG_MAX_UINT)<br \/>\n+         png_error(png_ptr, &#8220;Invalid chunk length.&#8221;);<\/p>\n<p>       png_reset_crc(png_ptr);<br \/>\n       png_crc_read(png_ptr, png_ptr->chunk_name, 4);<br \/>\ndiff -ru libpng-1.2.5\/pngrutil.c libpng-1.2.5.fix\/pngrutil.c<br \/>\n&#8212; libpng-1.2.5\/pngrutil.c     2004-07-13 13:36:37.000000000 +0100<br \/>\n+++ libpng-1.2.5.fix\/pngrutil.c 2004-07-13 23:43:02.000000000 +0100<br \/>\n@@ -350,7 +350,11 @@<br \/>\n    png_crc_finish(png_ptr, 0);<\/p>\n<p>    width = png_get_uint_32(buf);<br \/>\n+   if (width > PNG_MAX_DIMENSION)<br \/>\n+      png_error(png_ptr, &#8220;Width is too large&#8221;);<br \/>\n    height = png_get_uint_32(buf + 4);<br \/>\n+   if (height > PNG_MAX_DIMENSION)<br \/>\n+      png_error(png_ptr, &#8220;Height is too large&#8221;);<br \/>\n    bit_depth = buf[8];<br \/>\n    color_type = buf[9];<br \/>\n    compression_type = buf[10];<br \/>\n@@ -675,7 +679,7 @@<br \/>\n    else<br \/>\n       truelen = (png_size_t)png_ptr->channels;<\/p>\n<p>&#8211;   if (length != truelen)<br \/>\n+   if (length != truelen || length > 4)<br \/>\n    {<br \/>\n       png_warning(png_ptr, &#8220;Incorrect sBIT chunk length&#8221;);<br \/>\n       png_crc_finish(png_ptr, length);<br \/>\n@@ -1244,7 +1248,8 @@<br \/>\n          \/* Should be an error, but we can cope with it *\/<br \/>\n          png_warning(png_ptr, &#8220;Missing PLTE before tRNS&#8221;);<br \/>\n       }<br \/>\n&#8211;      else if (length > (png_uint_32)png_ptr->num_palette)<br \/>\n+      if (length > (png_uint_32)png_ptr->num_palette ||<br \/>\n+          length > PNG_MAX_PALETTE_LENGTH)<br \/>\n       {<br \/>\n          png_warning(png_ptr, &#8220;Incorrect tRNS chunk length&#8221;);<br \/>\n          png_crc_finish(png_ptr, length);<br \/>\n@@ -1400,7 +1405,7 @@<br \/>\n void \/* PRIVATE *\/<br \/>\n png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)<br \/>\n {<br \/>\n&#8211;   int num, i;<br \/>\n+   unsigned int num, i;<br \/>\n    png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];<\/p>\n<p>    png_debug(1, &#8220;in png_handle_hIST\\n&#8221;);<br \/>\n@@ -1426,8 +1431,8 @@<br \/>\n       return;<br \/>\n    }<\/p>\n<p>&#8211;   num = (int)length \/ 2 ;<br \/>\n&#8211;   if (num != png_ptr->num_palette)<br \/>\n+   num = length \/ 2 ;<br \/>\n+   if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH)<br \/>\n    {<br \/>\n       png_warning(png_ptr, &#8220;Incorrect hIST chunk length&#8221;);<br \/>\n       png_crc_finish(png_ptr, length);<br \/>\n@@ -2868,6 +2873,9 @@<br \/>\n                png_read_data(png_ptr, chunk_length, 4);<br \/>\n                png_ptr->idat_size = png_get_uint_32(chunk_length);<\/p>\n<p>+               if (png_ptr->idat_size > PNG_MAX_UINT)<br \/>\n+                  png_error(png_ptr, &#8220;Invalid chunk length.&#8221;);<br \/>\n+<br \/>\n                png_reset_crc(png_ptr);<br \/>\n                png_crc_read(png_ptr, png_ptr->chunk_name, 4);<br \/>\n                if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))<\/p>\n<p>discovered by Chris Evans<\/p>\n","protected":false},"excerpt":{"rendered":"<p>libPNG 1.2.5 stack-based buffer overflow and other code concerns<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-201","post","type-post","status-publish","format-standard","hentry","category-security"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p4bBYZ-3f","_links":{"self":[{"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/posts\/201","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/comments?post=201"}],"version-history":[{"count":0,"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/posts\/201\/revisions"}],"wp:attachment":[{"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/media?parent=201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/categories?post=201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/deepquest.code511.com\/blog\/wp-json\/wp\/v2\/tags?post=201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}