Python PIL 远程命令执行漏洞(GhostButt / CVE-2017-8291)

Python PIL(Pillow)是一个流行的 Python 图像处理库,支持多种图像格式并提供强大的图像处理功能。

Python 中处理图片的模块 PIL(Pillow),因为其内部调用了 GhostScript 而受到 GhostButt 漏洞(CVE-2017-8291)的影响,造成远程命令执行漏洞。

PIL 内部根据图片头(Magic Bytes)判断图片类型,如果发现是一个 EPS 文件(头为 %!PS),则分发给 PIL/EpsImagePlugin.py 处理。

在这个模块中,PIL 调用了系统的 gs 命令,也就是 GhostScript 来处理图片文件:

command = ["gs",
            "-q",                         # quiet mode
            "-g%dx%d" % size,             # set output geometry (pixels)
            "-r%fx%f" % res,              # set input DPI (dots per inch)
            "-dBATCH",                    # exit after processing
            "-dNOPAUSE",                  # don't pause between pages,
            "-dSAFER",                    # safe mode
            "-sDEVICE=ppmraw",            # ppm driver
            "-sOutputFile=%s" % outfile,  # output file
            "-c", "%d %d translate" % (-bbox[0], -bbox[1]),
                                            # adjust for image origin
            "-f", infile,                 # input file
            ]

# 省略判断是否安装 GhostScript 的代码
try:
    with open(os.devnull, 'w+b') as devnull:
        subprocess.check_call(command, stdin=devnull, stdout=devnull)
    im = Image.open(outfile)

虽然设置了 -dSAFER,也就是安全模式,但因为 GhostScript 的一个沙盒绕过漏洞(GhostButt CVE-2017-8291),导致这个安全模式被绕过,可以执行任意命令。

另外,截至目前,GhostScript 官方最新版 9.21 仍然受到这个漏洞影响,所以可以说:只要操作系统上安装了 GhostScript,PIL 就存在命令执行漏洞。

参考链接:

环境搭建

执行如下命令启动一个存在漏洞的Web应用,其中使用了PIL处理用户上传的文件:

docker compose up -d

环境启动后,访问 http://your-ip:8000/ 即可看到一个上传页面。

漏洞复现

该应用的正常功能是允许用户上传一个 PNG 文件,后端调用 PIL 加载图片,输出图片的长宽。但我们可以将可执行命令的 EPS 文件后缀改成 PNG 进行上传,因为后端是根据文件头来判断图片类型,所以能够绕过后缀检查。

例如,我们可以上传 poc.png,该文件会在服务器上执行 touch /tmp/aaaaa 命令。通过将 POC 中的命令修改为反弹 shell 命令,我们可以获得服务器的 shell 访问权限:

漏洞利用演示