CVE-2025-27462, -27463 and -27464 (XSA-468) are in the XenServer VM Tools
for Windows, but the record names only "Xen / Windows PV drivers / all"
and NVD left them Deferred with no configuration, so nothing bounded the
tools a Windows inventory lists ("XenServer VM Tools" / XenServer / 9.6.0).
Citrix's bulletin CTX692748 does: "All versions BEFORE 9.4.1", Linux guests
not affected.
The tools' "What's new" on docs.xenserver.com links each security release to
its bulletin (9.4.1 -> CTX692748), so it is the list, read the way XenCenter's
notes are; the fetch loop is now shared. A bulletin counts only if its VM
Tools bound is the release the notes link it from and its "Affected Versions"
names no other product; every CVE it lists is then the tools'. Severity comes
from the bulletin page's JSON, where Citrix now renders it.
Matched are "XenServer VM Tools" and "Citrix VM Tools" (before the 9.3.2
rebrand), whose version is the installer's, on Windows hosts only. The
Management Agent entries are not read: 9.4.2 ships agent 9.3.2.95. Findings
close when the tools update; a feed outage keeps the last parse. Known limit:
drivers updated through Windows Update alone leave the tools' entry old.
257 lines
12 KiB
Python
257 lines
12 KiB
Python
"""XenServer VM Tools for Windows CVEs from the tools' release notes and Citrix bulletins — run: python -m pytest tests/test_vm_tools_bulletins.py
|
|
|
|
Field report (24.10.2026): CVE-2025-27462, -27463 and -27464 (XSA-468) are in
|
|
the XenServer VM Tools for Windows. The record and NVD name only
|
|
"Xen / Windows PV drivers / all" (NVD: Deferred, no configuration), which bounds
|
|
nothing. Citrix's bulletin CTX692748 does:
|
|
|
|
The following supported versions of XenServer VM Tools for Windows are
|
|
affected: All versions BEFORE 9.4.1
|
|
|
|
The tools' "What's new" on docs.xenserver.com links the release to it:
|
|
|
|
9.4.1 — All customers who are affected by the issues described in
|
|
CTX692748 - XenServer and Citrix Hypervisor Security Bulletin should update
|
|
to this version of the tools or later.
|
|
|
|
The Windows inventory reads "XenServer VM Tools" / XenServer / 9.6.0.
|
|
|
|
Markup below is verbatim from docs.xenserver.com and support.citrix.com
|
|
(fetched 24.10.2026), trimmed to the parts the parser reads.
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from app.services import app_cve_scanner_service as cpe
|
|
from app.services import cvelistv5_scan_service as c5
|
|
from app.services import vm_tools_bulletin_service as vtb
|
|
|
|
|
|
def _release(version, body, h="h4"):
|
|
"""The newest release is an <h3> "XenServer VM Tools for Windows 9.6.1",
|
|
every older one an <h4> "9.4.1"."""
|
|
title = f"XenServer VM Tools for Windows {version}" if h == "h3" else version
|
|
return (f'<div id="{version.replace(".", "")}" class="topic">\n'
|
|
f'<{h} class="title">{title}</{h}>\n<div class="body">\n'
|
|
f'<div class="p">\nThis set of tools contains the following component versions:\n'
|
|
f'</div>\n{body}</div>\n</div>\n')
|
|
|
|
|
|
def _components(installer, agent):
|
|
return ('<ul class="ul">\n'
|
|
f'<li class="li">\n<div class="p">\nInstaller: {installer}\n</div></li>\n'
|
|
f'<li class="li">\n<div class="p">\nManagement Agent: {agent}\n</div></li>\n'
|
|
'</ul>\n')
|
|
|
|
|
|
NOTES = (
|
|
_release("9.6.1", _components("9.6.1.12", "9.6.1.16"), h="h3")
|
|
+ '<h3 class="title" id="earlier-releases">Earlier releases</h3>\n'
|
|
+ _release("9.4.2", _components("9.4.2", "9.3.2.95"))
|
|
+ _release("9.4.1", _components("9.4.1", "9.3.1.80")
|
|
+ '<div class="p">\nAll customers who are affected by the issues described in '
|
|
'<a class="xref" data-has-usertext="true" href="https://support.citrix.com/article/'
|
|
'CTX692748" data-attr-href="https://support.citrix.com/article/CTX692748" '
|
|
'data-attr-format="html" data-attr-scope="external" target="_blank" '
|
|
'rel="noopener noreferrer">CTX692748 - XenServer and Citrix Hypervisor Security '
|
|
'Bulletin</a> should update to this version of the tools or later.\n</div>\n')
|
|
+ '<div id="fixed-issues-in-941" class="topic">\n<h4 class="title">Fixed issues in 9.4.1'
|
|
'</h4>\n<div class="body">\n<div class="p">\nSome Windows VMs can experience unexpected '
|
|
'hard reboots.\n</div></div></div>\n'
|
|
+ _release("9.4.0", _components("9.4.0", "9.3.0.65")
|
|
+ '<div class="p">For more information, see the support article <a class="xref" '
|
|
'href="https://support.citrix.com/article/CTX678047">Static IP loss when updating '
|
|
'to VM Tools for Windows 9.3.3 or earlier</a>.</div>\n'))
|
|
|
|
|
|
def _s(text):
|
|
return (f'<p><span style="font-size: 11pt;font-family: Calibri, sans-serif;color: #000000;">'
|
|
f'{text}</span></p>\n')
|
|
|
|
|
|
def _bulletin(cves, affected, sev="High"):
|
|
lis = "".join(f'<li style="font-size: 11pt;font-family: Calibri, sans-serif;color: #000000;">'
|
|
f'\n<p><span style="font-size: 11pt;">{c}</span></p>\n</li>' for c in cves)
|
|
return (
|
|
'<div class="article-detail-card-content wolken-h5">\n'
|
|
'<p><h3><span style="font-size: 11pt;font-family: Calibri, sans-serif;color: #000000;">'
|
|
'Description of Problem</span></h3>\n'
|
|
+ _s("Issues have been identified in the XenServer VM Tools for Windows that allow an "
|
|
"attacker who has the ability to execute arbitrary unprivileged code within a guest "
|
|
"Windows VM to compromise that same VM. This issue affects Windows VMs running on "
|
|
"either XenServer 8.4 and Citrix Hypervisor 8.2 CU1 LTSR.")
|
|
+ _s("These issues have the following identifiers:")
|
|
+ f'<ul style="margin-top: 0px;margin-bottom: 0px;padding-inline-start: 48px;">{lis}</ul>\n'
|
|
'<h3 style="margin-top: 0pt;margin-bottom: 10pt;"><span style="font-size: 11pt;'
|
|
'font-family: Calibri, sans-serif;color: #000000;">Affected Versions</span></h3>\n'
|
|
+ affected
|
|
+ _s("Linux guest VMs are not affected by this issue.")
|
|
+ '<h3><span style="font-size: 11pt;font-family: Calibri, sans-serif;color: #000000;">'
|
|
'What Customers Should Do</span></h3>\n'
|
|
+ _s("We have released updated versions of the guest tools installer at https://support."
|
|
"citrix.com/s/article/CTX235403-updates-to-xenserver-vm-tools-for-windows-for-"
|
|
"xenserver-and-citrix-hypervisor and we are also making them available through "
|
|
"Windows Update.")
|
|
+ '</div>\n'
|
|
# The severity is not in the article markup; the page renders it from
|
|
# this JSON (trimmed), after the list of every possible value.
|
|
'<script type="text/javascript">var articleDetails = JSON.parse(\'{"kbFlexMapValueList":'
|
|
'[{"articleId":692748,"attributeId":12,"attributeName":"Severity","attributeTypeId":2,'
|
|
'"attributeTypeName":"LOV","flexAttributeLOVList":[{"lovId":6688,"lovName":"Critical"},'
|
|
'{"lovId":6690,"lovName":"High"},{"lovId":6691,"lovName":"Medium"}],"kbFlexMapId":6,'
|
|
f'"kbFlexMapValId":20988,"lovId":6690,"lovName":"{sev}","mandatory":false}},'
|
|
'{"attributeName":"Language","kbFlexMapValId":61150,"lovId":6693,'
|
|
'"lovName":"English (en_US)"}]}\');</script>\n')
|
|
|
|
|
|
AFFECTED = (_s("The following supported versions of XenServer VM Tools for Windows are affected:")
|
|
+ '<ul style="margin-top: 0px;margin-bottom: 0px;padding-inline-start: 48px;"><li '
|
|
'style="font-size: 11pt;font-family: Calibri, sans-serif;color: #000000;"><span '
|
|
'style="font-size: 11pt;">All versions BEFORE 9.4.1</span></li></ul>\n<p> </p>\n')
|
|
CTX692748 = _bulletin(["CVE-2025-27462", "CVE-2025-27463", "CVE-2025-27464"], AFFECTED)
|
|
|
|
|
|
# ---- 1. parse -----------------------------------------------------------------
|
|
def test_release_notes_name_the_security_releases():
|
|
"""9.4.0 links a support article, not a bulletin."""
|
|
assert vtb.parse_release_notes(NOTES) == [("9.4.1", "CTX692748")]
|
|
|
|
|
|
def test_newest_release_is_read_too():
|
|
top = _release("9.6.2", _components("9.6.2", "9.6.2.3") + '<div class="p">This release '
|
|
'fixes the issues described in <a href="https://support.citrix.com/article/'
|
|
'CTX699999">CTX699999 - XenServer Security Bulletin</a>.</div>', h="h3")
|
|
assert vtb.parse_release_notes(top + NOTES) == [("9.6.2", "CTX699999"),
|
|
("9.4.1", "CTX692748")]
|
|
|
|
|
|
def test_bulletin_page():
|
|
es = vtb.parse_bulletin("CTX692748", CTX692748, "9.4.1")
|
|
assert [e["cve"] for e in es] == ["CVE-2025-27462", "CVE-2025-27463", "CVE-2025-27464"]
|
|
for e in es:
|
|
assert (e["start"], e["lt"], e["lte"], e["sev"], e["ctx"]) == \
|
|
(None, "9.4.1", None, "high", "CTX692748")
|
|
assert e["desc"].startswith("Issues have been identified in the XenServer VM Tools for "
|
|
"Windows")
|
|
assert e["desc"].endswith("Source: Citrix Security Bulletin CTX692748.")
|
|
|
|
|
|
def test_bulletin_that_disagrees_with_the_release_notes_decides_nothing():
|
|
assert vtb.parse_bulletin("CTX692748", CTX692748, "9.4.2") == []
|
|
|
|
|
|
def test_bulletin_that_also_bounds_another_product_decides_nothing():
|
|
"""Which of its CVEs are the tools' and which the host's is prose."""
|
|
page = _bulletin(["CVE-2099-0001", "CVE-2099-0002"], AFFECTED + _s(
|
|
"The following supported versions of XenServer are affected: XenServer 8.4 and "
|
|
"Citrix Hypervisor 8.2 CU1 LTSR without the hotfixes listed below."))
|
|
assert vtb.parse_bulletin("CTX699999", page, "9.4.1") == []
|
|
|
|
|
|
def test_severity_from_markup_too():
|
|
"""The XenCenter bulletins still write "Severity: Medium" in the article."""
|
|
page = '<p><strong>Severity:</strong> Medium</p>' + CTX692748.split("<script")[0]
|
|
assert {e["sev"] for e in vtb.parse_bulletin("CTX692748", page, "9.4.1")} == {"medium"}
|
|
|
|
|
|
# ---- 2. scan ------------------------------------------------------------------
|
|
class _Asset:
|
|
id = 1
|
|
hostname = "vm-test"
|
|
operating_system = "Microsoft Windows Server 2022 Standard"
|
|
|
|
|
|
def _scan(monkeypatch, packages, os_name="Microsoft Windows Server 2022 Standard"):
|
|
monkeypatch.setattr(vtb, "load", lambda db: {
|
|
"CTX692748": vtb.parse_bulletin("CTX692748", CTX692748, "9.4.1")})
|
|
index = {}
|
|
c5._merge_vm_tools_bulletins(None, index)
|
|
got = []
|
|
monkeypatch.setattr(cpe, "_upsert", lambda db, asset, n, v, c, new_ids, **kw:
|
|
got.append((n, v, c["cve"], c["fixed"], c["severity"], c.get("refs"))))
|
|
monkeypatch.setattr(_Asset, "operating_system", os_name)
|
|
c5.scan_asset(None, _Asset(), packages, index, touched=set())
|
|
return got
|
|
|
|
|
|
def test_field_report(monkeypatch):
|
|
got = _scan(monkeypatch, [{"name": "XenServer VM Tools", "version": "9.4.0",
|
|
"vendor": "XenServer"}])
|
|
ref = '["https://support.citrix.com/external/article/CTX692748"]'
|
|
assert got == [("XenServer VM Tools", "9.4.0", cve, "9.4.1", "high", ref)
|
|
for cve in ("CVE-2025-27462", "CVE-2025-27463", "CVE-2025-27464")], got
|
|
# the screenshot's install, the fix itself, and the installer's four-part build
|
|
for ver in ("9.6.0", "9.4.1", "9.6.1.12"):
|
|
assert _scan(monkeypatch, [{"name": "XenServer VM Tools", "version": ver}]) == [], ver
|
|
|
|
|
|
def test_name_before_the_rebrand(monkeypatch):
|
|
"""9.3.2 renamed "Citrix VM Tools" to "XenServer VM Tools"."""
|
|
got = _scan(monkeypatch, [{"name": "Citrix VM Tools", "version": "9.3.1"}])
|
|
assert {g[2] for g in got} == {"CVE-2025-27462", "CVE-2025-27463", "CVE-2025-27464"}
|
|
|
|
|
|
def test_management_agent_is_not_the_tools_release(monkeypatch):
|
|
"""9.4.2 ships Management Agent 9.3.2.95: its version is not the tools'."""
|
|
for name in ("XenServer Windows Management Agent", "Citrix XenServer Windows Management Agent",
|
|
"Citrix Hypervisor PV Tools"):
|
|
assert _scan(monkeypatch, [{"name": name, "version": "9.3.2.95"}]) == [], name
|
|
|
|
|
|
def test_windows_only(monkeypatch):
|
|
assert _scan(monkeypatch, [{"name": "XenServer VM Tools", "version": "9.4.0"}],
|
|
os_name="Ubuntu 24.04") == []
|
|
|
|
|
|
# ---- 3. fetch -----------------------------------------------------------------
|
|
class _Resp:
|
|
def __init__(self, text, status=200):
|
|
self.text, self.status_code = text, status
|
|
|
|
def raise_for_status(self):
|
|
if self.status_code != 200:
|
|
raise OSError(self.status_code)
|
|
|
|
|
|
def _client(pages):
|
|
class Client:
|
|
def __init__(self, **kw):
|
|
pass
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *a):
|
|
return False
|
|
|
|
def get(self, url):
|
|
if url == vtb.RELEASE_NOTES:
|
|
return pages["notes"]
|
|
return pages.get(url.rsplit("/", 1)[1]) or _Resp("", 403)
|
|
return Client
|
|
|
|
|
|
def test_build_index(monkeypatch):
|
|
import httpx
|
|
stored = {}
|
|
monkeypatch.setattr(vtb, "_store", lambda db, idx: stored.update(idx))
|
|
monkeypatch.setattr(vtb, "load", lambda db: {"CTX600000": ["kept"]})
|
|
monkeypatch.setattr(httpx, "Client", _client(
|
|
{"notes": _Resp(NOTES), "CTX692748": _Resp(CTX692748)}))
|
|
idx = vtb.build_index(None)
|
|
assert sorted(idx) == ["CTX600000", "CTX692748"], idx
|
|
assert len(idx["CTX692748"]) == 3
|
|
assert stored == idx
|
|
|
|
|
|
def test_unreachable_serves_last_parse(monkeypatch):
|
|
"""A bulletin that drops out would close its findings as "patched"."""
|
|
import httpx
|
|
monkeypatch.setattr(vtb, "load", lambda db: {"CTX692748": ["last"]})
|
|
monkeypatch.setattr(httpx, "Client", _client({"notes": _Resp("", 503)}))
|
|
assert vtb.build_index(None) == {"CTX692748": ["last"]}
|
|
monkeypatch.setattr(httpx, "Client", _client({"notes": _Resp(NOTES)})) # bulletin 403
|
|
assert vtb.build_index(None) == {"CTX692748": ["last"]}
|