Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!

Bug 581942 (CVE-2016-3074)

Summary: <media-libs/gd-2.2.2: signedness vulnerability (CVE-2016-3074)
Product: Gentoo Security Reporter: Agostino Sarubbo <ago>
Component: VulnerabilitiesAssignee: Gentoo Security <security>
Status: RESOLVED FIXED    
Severity: normal CC: graphics+disabled, php-bugs, vapier
Priority: Normal    
Version: unspecified   
Hardware: All   
OS: Linux   
URL: http://www.openwall.com/lists/oss-security/2016/04/21/6
See Also: https://bugs.gentoo.org/show_bug.cgi?id=581834
Whiteboard: B2 [glsa cve]
Package list:
Runtime testing required: ---

Description Agostino Sarubbo gentoo-dev 2016-05-03 09:01:26 UTC
From ${URL} :

Overview
========

libgd [1] is an open-source image library.  It is perhaps primarily used
by the PHP project.  It has been bundled with the default installation
of PHP since version 4.3 [2].

A signedness vulnerability (CVE-2016-3074) exist in libgd 2.1.1 which
may result in a heap overflow when processing compressed gd2 data.


Details
=======

4 bytes representing the chunk index size is stored in a signed integer,
chunkIdx[i].size, by `gdGetInt()' during the parsing of GD2 headers:

libgd-2.1.1/src/gd_gd2.c:
,----
|  53 typedef struct {
|  54     int offset;
|  55     int size;
|  56 }
|  57 t_chunk_info;
`----

libgd-2.1.1/src/gd_gd2.c:
,----
|  65 static int
|  66 _gd2GetHeader (gdIOCtxPtr in, int *sx, int *sy,
|  67                int *cs, int *vers, int *fmt, int *ncx, int *ncy,
|  68                t_chunk_info ** chunkIdx)
|  69 {
| ...
|  73     t_chunk_info *cidx;
| ...
| 155     if (gd2_compressed (*fmt)) {
| ...
| 163         for (i = 0; i < nc; i++) {
| ...
| 167             if (gdGetInt (&cidx[i].size, in) != 1) {
| 168                 goto fail2;
| 169             };
| 170         };
| 171         *chunkIdx = cidx;
| 172     };
| ...
| 181 }
`----

`gdImageCreateFromGd2Ctx()' and `gdImageCreateFromGd2PartCtx()' then
allocates memory for the compressed data based on the value of the
largest chunk size:

libgd-2.1.1/src/gd_gd2.c:
,----
| 371|637     if (gd2_compressed (fmt)) {
| 372|638         /* Find the maximum compressed chunk size. */
| 373|639         compMax = 0;
| 374|640         for (i = 0; (i < nc); i++) {
| 375|641             if (chunkIdx[i].size > compMax) {
| 376|642                 compMax = chunkIdx[i].size;
| 377|643             };
| 378|644         };
| 379|645         compMax++;
| ...|...
| 387|656         compBuf = gdCalloc (compMax, 1);
| ...|...
| 393|661     };
`----

A size of <= 0 results in `compMax' retaining its initial value during
the loop, followed by it being incremented to 1.  Since `compMax' is
used as the nmemb for `gdCalloc()', this leads to a 1*1 byte allocation
for `compBuf'.

This is followed by compressed data being read to `compBuf' based on the
current (potentially negative) chunk size:

libgd-2.1.1/src/gd_gd2.c:
,----
| 339 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
| 340 {
| ...
| 413         if (gd2_compressed (fmt)) {
| 414
| 415             chunkLen = chunkMax;
| 416
| 417             if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
| 418                                 compBuf,
| 419                                 chunkIdx[chunkNum].size,
| 420                                 (char *) chunkBuf, &chunkLen, in)) {
| 421                 GD2_DBG (printf ("Error reading comproessed chunk\n"));
| 422                 goto fail;
| 423             };
| 424
| 425             chunkPos = 0;
| 426         };
| ...
| 501 }
`----


libgd-2.1.1/src/gd_gd2.c:
,----
| 585 BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, int h)
| 586 {
| ...
| 713         if (!gd2_compressed (fmt)) {
| ...
| 731         } else {
| 732             chunkNum = cx + cy * ncx;
| 733
| 734             chunkLen = chunkMax;
| 735             if (!_gd2ReadChunk (chunkIdx[chunkNum].offset,
| 736                                 compBuf,
| 737                                 chunkIdx[chunkNum].size,
| 738                                 (char *) chunkBuf, &chunkLen, in)) {
| 739                 printf ("Error reading comproessed chunk\n");
| 740                 goto fail2;
| 741             };
| ...
| 746         };
| ...
| 815 }
`----

The size is subsequently interpreted as a size_t by `fread()' or
`memcpy()', depending on how the image is read:

libgd-2.1.1/src/gd_gd2.c:
,----
| 221 static int
| 222 _gd2ReadChunk (int offset, char *compBuf, int compSize, char *chunkBuf,
| 223            uLongf * chunkLen, gdIOCtx * in)
| 224 {
| ...
| 236     if (gdGetBuf (compBuf, compSize, in) != compSize) {
| 237         return FALSE;
| 238     };
| ...
| 251 }
`----

libgd-2.1.1/src/gd_io.c:
,----
| 211 int gdGetBuf(void *buf, int size, gdIOCtx *ctx)
| 212 {
| 213     return (ctx->getBuf)(ctx, buf, size);
| 214 }
`----


For file contexts:

libgd-2.1.1/src/gd_io_file.c:
,----
|  52 BGD_DECLARE(gdIOCtx *) gdNewFileCtx(FILE *f)
|  53 {
| ...
|  67     ctx->ctx.getBuf = fileGetbuf;
| ...
|  76 }
| ...
|  92 static int fileGetbuf(gdIOCtx *ctx, void *buf, int size)
|  93 {
|  94     fileIOCtx *fctx;
|  95     fctx = (fileIOCtx *)ctx;
|  96
|  97     return (fread(buf, 1, size, fctx->f));
|  98 }
`----


And for dynamic contexts:

libgd-2.1.1/src/gd_io_dp.c:
,----
|  74 BGD_DECLARE(gdIOCtx *) gdNewDynamicCtxEx(int initialSize, void *data, int freeOKFlag)
|  75 {
| ...
|  95     ctx->ctx.getBuf = dynamicGetbuf;
| ...
| 104 }
| ...
| 256 static int dynamicGetbuf(gdIOCtxPtr ctx, void *buf, int len)
| 257 {
| ...
| 280     memcpy(buf, (void *) ((char *)dp->data + dp->pos), rlen);
| ...
| 284 }
`----


PoC
===

Against Ubuntu 15.10 amd64 running nginx with php5-fpm and php5-gd [3]:

,----
| $ python exploit.py --bind-port 5555 http://1.2.3.4/upload.php
| [*] this may take a while
| [*] offset 912 of 10000...
| [+] connected to 1.2.3.4:5555
| id
| uid=33(www-data) gid=33(www-data) groups=33(www-data)
| 
| uname -a
| Linux wily64 4.2.0-35-generic #40-Ubuntu SMP Tue Mar 15 22:15:45 UTC
| 2016 x86_64 x86_64 x86_64 GNU/Linux
| 
| dpkg -l|grep -E "php5-(fpm|gd)"
| ii  php5-fpm       5.6.11+dfsg-1ubuntu3.1 ...
| ii  php5-gd        5.6.11+dfsg-1ubuntu3.1 ...
| 
| cat upload.php
| <?php
|     imagecreatefromgd2($_FILES["file"]["tmp_name"]);
| ?>
`----


Solution
========

This bug has been fixed in git HEAD [4].



Footnotes
_________

[1] [http://libgd.org/]

[2] [https://en.wikipedia.org/wiki/Libgd]

[3] [https://github.com/dyntopia/exploits/tree/master/CVE-2016-3074]

[4] [https://github.com/libgd/libgd/commit/2bb97f407c1145c850416a3bfbcc8cf124e68a19]


@maintainer(s): after the bump, in case we need to stabilize the package, please let us know if it is ready for the stabilization or not.
Comment 1 SpanKY gentoo-dev 2016-05-03 19:58:22 UTC
i need to roll a new release upstream.  there was concern about making sure php also had the fix, but i haven't looked there ... i'm not that familiar with their vcs and where their copy of gd is kept.
Comment 2 Brian Evans (RETIRED) gentoo-dev 2016-05-03 20:00:22 UTC
(In reply to SpanKY from comment #1)
> i need to roll a new release upstream.  there was concern about making sure
> php also had the fix, but i haven't looked there ... i'm not that familiar
> with their vcs and where their copy of gd is kept.

PHP has this CVE covered in the latest releases.  See bug 581834
Comment 3 SpanKY gentoo-dev 2016-05-20 02:58:40 UTC
i've added 2.2.0 to the tree now.  give it a few days and then stabilize.
Comment 4 GLSAMaker/CVETool Bot gentoo-dev 2016-06-28 03:02:45 UTC
CVE-2016-3074 (http://nvd.nist.gov/nvd.cfm?cvename=CVE-2016-3074):
  Integer signedness error in GD Graphics Library 2.1.1 (aka libgd or libgd2)
  allows remote attackers to cause a denial of service (crash) or potentially
  execute arbitrary code via crafted compressed gd2 data, which triggers a
  heap-based buffer overflow.
Comment 5 Aaron Bauman (RETIRED) gentoo-dev 2016-06-28 03:09:18 UTC
(In reply to SpanKY from comment #3)
> i've added 2.2.0 to the tree now.  give it a few days and then stabilize.

2.2.0 is no longer in the tree.  2.2.1 has been there for over 30 days.

Would you like to target 2.2.1, the more recent 2.2.2, or hold off on stabilization?  2.2.1 would solve the security issue at this point.  Thanks.

Added to existing GLSA request.
Comment 6 SpanKY gentoo-dev 2016-06-28 03:49:29 UTC
(In reply to Aaron Bauman from comment #5)

considering 2.2.2 fixes more security issues, prob want to straight to that
Comment 7 Aaron Bauman (RETIRED) gentoo-dev 2016-06-28 04:00:18 UTC
@arches, please stabilize the following:

=media-libs/gd-2.2.2
Comment 8 Tobias Klausmann (RETIRED) gentoo-dev 2016-06-30 08:04:58 UTC
Stable on alpha.
Comment 9 Agostino Sarubbo gentoo-dev 2016-07-01 08:30:05 UTC
amd64 stable
Comment 10 Agostino Sarubbo gentoo-dev 2016-07-01 08:31:43 UTC
x86 stable
Comment 11 Jeroen Roovers (RETIRED) gentoo-dev 2016-07-02 09:05:06 UTC
Stable for HPPA PPC64.
Comment 12 Markus Meier gentoo-dev 2016-07-05 20:46:30 UTC
arm stable
Comment 13 Agostino Sarubbo gentoo-dev 2016-07-08 07:57:19 UTC
ppc stable
Comment 14 Agostino Sarubbo gentoo-dev 2016-07-08 10:05:51 UTC
sparc stable
Comment 15 Agostino Sarubbo gentoo-dev 2016-07-08 12:05:13 UTC
ia64 stable
Comment 16 Aaron Bauman (RETIRED) gentoo-dev 2016-07-09 03:34:47 UTC
Removing unstable arches from CC.

@maintainer(s), please cleanup the vulnerable versions.
Comment 17 GLSAMaker/CVETool Bot gentoo-dev 2016-07-16 13:11:51 UTC
This issue was resolved and addressed in
 GLSA 201607-04 at https://security.gentoo.org/glsa/201607-04
by GLSA coordinator Aaron Bauman (b-man).
Comment 18 Aaron Bauman (RETIRED) gentoo-dev 2016-07-16 13:13:09 UTC
@maintainer(s), please cleanup the vulnerable versions.
Comment 19 Michael Orlitzky gentoo-dev 2016-09-29 18:58:12 UTC
The vulnerable versions of media-libs/gd have been removed, so this can proceed.
Comment 20 Aaron Bauman (RETIRED) gentoo-dev 2016-10-10 11:33:40 UTC
tree is clean