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.
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.
(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
i've added 2.2.0 to the tree now. give it a few days and then stabilize.
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.
(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.
(In reply to Aaron Bauman from comment #5) considering 2.2.2 fixes more security issues, prob want to straight to that
@arches, please stabilize the following: =media-libs/gd-2.2.2
Stable on alpha.
amd64 stable
x86 stable
Stable for HPPA PPC64.
arm stable
ppc stable
sparc stable
ia64 stable
Removing unstable arches from CC. @maintainer(s), please cleanup the vulnerable versions.
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).
@maintainer(s), please cleanup the vulnerable versions.
The vulnerable versions of media-libs/gd have been removed, so this can proceed.
tree is clean