This is a building directory left unlocked, not the vault door itself
CVE-2026-9152 is an unauthenticated exposure in a legacy Altium 365 SearchService SOAP endpoint. Per Altium's advisory, it affected all Altium 365 cloud workspaces, including commercial and government cloud regions, while on-prem Altium Enterprise Server was not affected. An attacker who knows or can guess a target workspace identifier can query indexed content and can also inject, modify, or delete search index entries without presenting a token, session, or identity.
The raw technical shape screams severe because it is unauthenticated, network-reachable, and cross-tenant. But reality matters: the impact is constrained to the search index layer, not the underlying vault data or platform control plane. That keeps this out of CRITICAL for defenders prioritizing real-world risk, even though the exposed surface is ugly and the confidentiality exposure is still meaningful.
3 steps from start to impact.
Identify a target workspace
- Attacker has internet reachability to Altium 365 cloud endpoints
- Attacker can obtain or infer a target workspace identifier
- Workspace identifiers are not always public
- Enterprises with tightly controlled external sharing reduce passive discovery
Call the legacy SOAP endpoint without a token
curl, Burp Suite, SoapUI, or a custom Python SOAP client, the attacker sends requests directly to the legacy SearchService endpoint. The bug is that the service accepts search index operations without bearer tokens, cookies, or session validation, so the attacker reaches cross-tenant indexed data with no login step.- A reachable vulnerable SearchService endpoint exists
- The service has not yet been remediated at Altium's side
- Undocumented legacy SOAP paths are harder to discover than first-class REST/GraphQL APIs
- Reverse proxies, WAFs, or service retirement can break naive exploit attempts
Read or poison the search index
- The endpoint exposes read and/or write search index operations
- The target workspace has sensitive metadata in the index
- Impact stops at the search layer rather than modifying source-of-truth vault objects
- Some indexed content may be low sensitivity in mature environments
The supporting signals.
| In-the-wild status | No confirmed active exploitation found in public sources reviewed as of 2026-06-06; not listed in CISA KEV. |
|---|---|
| Public PoC availability | No public GitHub, Exploit-DB, or Metasploit PoC was identified in the reviewed sources as of 2026-06-06. |
| EPSS | Prompt-supplied EPSS is 0.00079 (~0.079%), which is extremely low and argues against broad opportunistic exploitation right now. |
| KEV status | Absent from the CISA KEV catalog as checked on 2026-06-06. |
| Assessed CVSS interpretation | My working vector is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L: internet-reachable, no auth, no user action, cross-tenant confidentiality impact, but integrity/availability damage is mostly limited to the search index. |
| Affected scope | Per Altium: all Altium 365 cloud workspaces in commercial and government cloud were affected; Altium Enterprise Server on-prem was not affected. |
| Fixed state | Altium says the issue was remediated at the service level and no customer action is required; there is no customer-installable fixed version. |
| Exposure reality | This was a SaaS-wide internet surface rather than a niche internal admin port. Reachability was broad, but exploitability still depended on finding the legacy SOAP path and a target workspace identifier. |
| Scanning / exposure data | I found no reliable public Shodan/Censys/FOFA fingerprint for this specific SearchService endpoint in the reviewed sources. That means exposure quantification is poor, but the vendor advisory already confirms the affected population was cloud-wide. |
| Disclosure / reporter / baseline discrepancy | Disclosure date is 2026-05-21 and Altium credits Joris Aerts. Note: your prompt says there is no vendor/authority score, but Altium's advisory page currently displays CVSS 10.0; I still mark this as assessed per your instruction because the goal here is a first-principles real-world severity call. |
noisgate verdict.
The decisive factor is that exploitation is unauthenticated and internet-reachable across a multitenant SaaS platform, which makes the entry condition dangerously easy. It lands at HIGH instead of CRITICAL because the blast radius is materially capped: the attacker compromises the search index plane, not the underlying vault data store or platform execution layer.
Why this verdict
- Unauthenticated remote access pushes this up: no credentials, no prior foothold, no user interaction, and network reachability from the internet are strong severity amplifiers.
- Cross-tenant SaaS exposure pushes this up again: the bug crosses workspace boundaries in a shared cloud platform, so the reachable population before remediation was broad rather than niche.
- Search-index-only impact pulls it down: Altium explicitly says the operations affect the search index, not the underlying vault data. That is the biggest downward pressure on severity.
- Workspace-ID knowledge adds friction: an attacker still needs a target workspace identifier, which implies some discovery work and keeps this from being the easiest spray-and-pray internet bug.
- Threat intel is cold: no KEV listing, no public PoC found, and the prompt-supplied EPSS of 0.00079 all argue against widespread weaponization today.
Why not higher?
This is not a platform takeover, auth bypass to the primary data plane, or remote code execution. The attacker can expose sensitive indexed metadata and corrupt search results, but the advisory is clear that the underlying vault objects are not directly changed through this flaw. That limitation is too important to ignore.
Why not lower?
You do not get to call an unauthenticated, internet-facing, cross-tenant SaaS flaw 'medium' just because the write path stops at the search layer. Confidentiality loss still matters here, especially for project names, component data, folder names, and user metadata that can reveal active programs, suppliers, or regulated work.
What to do — in priority order.
- Confirm vendor-side remediation — Treat this as a vendor-risk verification task. Obtain written confirmation from Altium that your commercial or GovCloud workspaces were covered by the service-side fix and retain the evidence within 30 days for a HIGH finding.
- Reduce public workspace identifier leakage — Review externally shared project links, supplier onboarding docs, screenshots, and public knowledge-base material that could expose workspace identifiers. Even after remediation, reducing tenant discovery surface is worthwhile and should be completed within 30 days.
- Watch SaaS logs for anomalous search activity — If you have access to Altium audit telemetry, proxy logs, or CASB/SSE visibility, look for unauthenticated or unusual SOAP-like requests and sudden spikes in search failures or index anomalies. Put the monitoring rule in place within 30 days.
- Escalate regulated-workspace review — If you use Altium 365 GovCloud or store export-controlled design metadata, validate whether any indexed project names or part metadata were sensitive enough to trigger internal incident review. Do that within 30 days because the confidentiality angle is the main business risk.
MFAdoes not help against a path that never asks for authentication in the first place.EDRon engineer laptops does not meaningfully detect or block abuse that stays inside the SaaS provider's service plane.Patch your endpointsis irrelevant here because there is no customer-side software fix for Altium 365 cloud tenants.Perimeter WAFon your corporate edge does not protect a third-party SaaS endpoint you do not front.
Crowdsourced verification payload.
Run this from an auditor workstation with internet access to your Altium 365 workspace, not from the target host—this is a SaaS check. Invoke it as python3 altium365_searchservice_check.py https://yourworkspace.365.altium.com; no admin rights are needed. It is a heuristic validator because Altium does not publicly document the legacy SearchService path in the reviewed sources.
#!/usr/bin/env python3
# altium365_searchservice_check.py
# Heuristic check for CVE-2026-9152-style unauthenticated SearchService exposure
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN
import sys
import urllib.request
import urllib.error
import urllib.parse
import ssl
import re
CANDIDATE_PATHS = [
"/SearchService.svc?wsdl",
"/SearchService.svc?singleWsdl",
"/SearchService.svc",
"/search/SearchService.svc?wsdl",
"/search/SearchService.svc?singleWsdl",
"/search/SearchService.svc",
"/services/SearchService.svc?wsdl",
"/services/SearchService.svc",
]
SOAP_PROBE = b'''<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body />
</soap:Envelope>'''
AUTH_PATTERNS = [
re.compile(r'login', re.I),
re.compile(r'sign\s*in', re.I),
re.compile(r'authorization', re.I),
re.compile(r'bearer', re.I),
]
SERVICE_PATTERNS = [
re.compile(r'<wsdl:definitions', re.I),
re.compile(r'<definitions', re.I),
re.compile(r'SearchService', re.I),
re.compile(r'soap:Envelope', re.I),
re.compile(r'tempuri\.org', re.I),
re.compile(r'http://schemas\.xmlsoap\.org/wsdl/', re.I),
]
def normalize_base(url: str) -> str:
if not url.startswith("http://") and not url.startswith("https://"):
url = "https://" + url
parsed = urllib.parse.urlparse(url)
if not parsed.scheme or not parsed.netloc:
raise ValueError("Invalid workspace URL")
return f"{parsed.scheme}://{parsed.netloc}"
def fetch(url: str, method: str = "GET", data: bytes = None, headers: dict = None, timeout: int = 10):
req = urllib.request.Request(url, data=data, method=method)
for k, v in (headers or {}).items():
req.add_header(k, v)
ctx = ssl.create_default_context()
try:
with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp:
body = resp.read(4096).decode("utf-8", errors="replace")
return resp.getcode(), dict(resp.headers), body, None
except urllib.error.HTTPError as e:
try:
body = e.read(4096).decode("utf-8", errors="replace")
except Exception:
body = ""
return e.code, dict(e.headers), body, None
except Exception as e:
return None, {}, "", str(e)
def looks_like_auth_gate(code: int, headers: dict, body: str) -> bool:
if code in (401, 403):
return True
loc = headers.get("Location", "")
if code in (301, 302, 303, 307, 308) and any(p.search(loc) for p in AUTH_PATTERNS):
return True
if any(p.search(body) for p in AUTH_PATTERNS):
return True
return False
def looks_like_exposed_service(code: int, body: str) -> bool:
if code != 200:
return False
return any(p.search(body) for p in SERVICE_PATTERNS)
def main():
if len(sys.argv) != 2:
print("UNKNOWN - usage: python3 altium365_searchservice_check.py https://yourworkspace.365.altium.com")
sys.exit(2)
try:
base = normalize_base(sys.argv[1])
except Exception as e:
print(f"UNKNOWN - invalid input: {e}")
sys.exit(2)
saw_network_error = False
saw_auth_or_notfound = False
findings = []
for path in CANDIDATE_PATHS:
url = base + path
code, headers, body, err = fetch(url, method="GET", headers={"User-Agent": "noisgate-check/1.0"})
if err:
saw_network_error = True
findings.append(f"GET {url} -> network error: {err}")
continue
findings.append(f"GET {url} -> HTTP {code}")
if looks_like_exposed_service(code, body) and not looks_like_auth_gate(code, headers, body):
print("VULNERABLE - unauthenticated service metadata exposed")
for f in findings:
print(f)
sys.exit(1)
if code in (401, 403, 404) or looks_like_auth_gate(code, headers, body):
saw_auth_or_notfound = True
continue
if code == 200:
# Try a blank SOAP POST; vulnerable services often respond with SOAP faults without auth
pcode, pheaders, pbody, perr = fetch(
url.split("?")[0],
method="POST",
data=SOAP_PROBE,
headers={
"User-Agent": "noisgate-check/1.0",
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": '""'
}
)
if perr:
saw_network_error = True
findings.append(f"POST {url.split('?')[0]} -> network error: {perr}")
continue
findings.append(f"POST {url.split('?')[0]} -> HTTP {pcode}")
if looks_like_exposed_service(pcode, pbody) and not looks_like_auth_gate(pcode, pheaders, pbody):
print("VULNERABLE - unauthenticated SOAP interaction appears possible")
for f in findings:
print(f)
sys.exit(1)
if pcode in (401, 403, 404) or looks_like_auth_gate(pcode, pheaders, pbody):
saw_auth_or_notfound = True
if saw_auth_or_notfound and not saw_network_error:
print("PATCHED - tested candidate paths require auth, redirect to login, or are absent")
for f in findings:
print(f)
sys.exit(0)
print("UNKNOWN - no positive exposure found, but endpoint path could not be confidently determined")
for f in findings:
print(f)
sys.exit(2)
if __name__ == "__main__":
main()
If you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.