# 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 来处理图片文件：

```python
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 就存在命令执行漏洞。

参考链接：

- [Exploiting Python PIL Module Command Execution Vulnerability](http://blog.neargle.com/2017/09/28/Exploiting-Python-PIL-Module-Command-Execution-Vulnerability/)
- [CVE-2017-8291 详情](https://nvd.nist.gov/vuln/detail/CVE-2017-8291)
- [GhostScript 安全公告](https://www.ghostscript.com/security-advisories.html)

## 环境搭建

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

```
docker compose up -d
```

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

## 漏洞复现

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

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

![漏洞利用演示](01.png)
