PHP 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    }

漏洞源于从 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 设计