Xen Orchestra let anyone log in with an empty pair of curly braces, and somebody already did (VSA-2026-062)
the authentication check in Xen Orchestra asked “which token is this?” and, when handed an empty JSON object, replied “sure, here is the first one i have.” that is VSA-2026-062. Vates published it today, November 10, rated it Critical, and says it is actively exploited by a named piece of infrastructure. there is no CVE yet and no CVSS yet. there is, however, a VM called pivot2 that you should go look for.
authenticationToken=j:%7B%7D into an empty object, the token provider looks that up unfiltered, gets the first token in the collection, and authenticates as its owner if that token has not expired. the attacker gets Admin in XO, and Vates says that can lead to code execution on managed pools and root on XCP-ng hosts. fixed packages: xo-server 5.213.1 (latest channel, XO 6.10.0) and xo-server 5.211.2 (stable channel, XO 6.9.1). exploitation is attributed by Vates to 91.211.251.112:8001 and a Tor onion service. patch, pull XO off any network that is not a management network, then hunt.what Xen Orchestra is, for those who outsourced their hypervisor opinions
XCP-ng is the open-source Xen-based hypervisor a lot of shops moved to when the VMware licensing letters started arriving. Xen Orchestra is the web console and API that manages it: pools, hosts, VMs, backups, storage, users, all of it. the Appliance (XOA) is the packaged version Vates ships and supports.
so when we say “Admin in Xen Orchestra,” we mean the account that can create VMs, change their hardware config, attach disks, and run backups to wherever it likes. it is the keys to the server room, minus the server room.
the bug, exactly as Vates describes it
from VSA-2026-062, “Empty-object login token”, published 2026-11-10:
- REST cookie parsing turns
authenticationToken=j:%7B%7Dinto{}. (thej:prefix is the JSON-cookie convention used by Express’s cookie parser, which is why a cookie can become an object at all.) - REST authentication passes that object to the XO token provider “without requiring a string.”
- the token collection treats
{}as an unfiltered lookup and returns its first token. - the provider authenticates as that token’s owner if the token is unexpired.
- the authenticated
/rest/v0/users/me/authentication_tokensroute then returns reusable login token IDs.
read step 5 slowly. the attacker does not just get one session. they get to list real token IDs they can keep reusing. patching closes the hole. it does not un-leak those tokens. that is why Vates tells you to rotate everything if you were hit.
the vulnerable condition, stated plainly: an unpatched xo-server whose REST API the attacker can reach, with at least one unexpired authentication token in its collection. on any XO instance that people actually log into, assume that second condition is true. whose token comes back first depends on the collection, and Vates rates the outcome as Admin, so plan for Admin.
the fix is one helper function, which is the most depressing part
the fix landed in PR #10748, “fix(xo-server): validate authentication token IDs,” merged 2026-11-10 at 08:29 UTC. it adds an isNonEmptyString check, rejects non-string and empty token IDs before any collection lookup, and builds the lookup query “from validated fields only, never forward caller-supplied objects.” the PR says it was introduced by commit 115bc8f and that testing on a disposable XOA running xo-server 5.213.0 showed the unpatched build accepting the bad token and the patched build rejecting it on the REST API, /signin, and JSON-RPC session.signIn.
that last part matters for defenders: the fix covers three entry points, not just the REST cookie. we would not assume the REST cookie is the only path on unpatched builds. the PR also notes “Not run: stable backport validation.” Vates shipped the stable package anyway, and so should you, but keep an eye on the advisory for an update.
“check that the token is a string” is a type check from week one of any web course. one missing typeof turned into root on hypervisors. please clap.
affected and fixed versions
- Affected: Xen Orchestra before 6.9.1 (stable) and 6.10.0 (latest), and the Xen Orchestra Appliance. the advisory’s affected-versions line reads “xo-server before 5.213.1 and 5.213.2,” which does not match its own resolution section. we are going with the resolution section, which names the actual packages.
- Fixed: xo-server 5.213.1 on the
latestchannel (XO 6.10.0) and xo-server 5.211.2 on thestablechannel (XO 6.9.1). - Grey zone: anything between 5.211.2 and 5.213.1 that is not one of those two packages, for example a 5.212.x build, should be treated as vulnerable. the PR’s own test target, 5.213.0, was vulnerable before the patch.
- if you build XO from source, you need a tree that includes PR #10748.
scores, and who assigned them
- Vates severity: Critical, on its own severity scale.
- CVSS 4.0: “Not available yet,” per the advisory.
- CVE: none assigned as of this writing. so your scanner, which keys on CVE IDs, has no idea this exists. you are the scanner today.
- CISA KEV: not listed. KEV needs a CVE ID. vendor-confirmed exploitation without a CVE is exactly the blind spot where “we patch everything on KEV” programs go to die.
exploited, per the vendor
the advisory says the vulnerability “is actively exploited by a threat actor operating from 91.211.251.112:8001 and ustar3rqxgnskzgnwkgpofgtvxevm6cx3lxdbwt4mndtveqyaohid.onion:3333 (via Tor).” Vates does not give a victim count, a start date, or an attribution beyond that infrastructure, so neither do we.
credit for discovery goes to Filip Magic (@mad31k) and Christoffer Olsen from TDC NET Security, per the advisory.
the host-side artifacts Vates lists tell you roughly what the operator does after getting in:
- a VM named
pivot2 - VMs whose platform config uses
hvm_serial: file, which writes a VM’s serial console to a file on the host -serial file:entries in/var/log/xensource.log*- files
/etc/cron.d/zz-ustar2,/etc/cron.d/zz-ustar,/.ustar5,/.ustar6, and a check of/etc/ld.so.preload
we are not going to speculate about how a serial-to-file VM gets turned into host access. the advisory does not spell it out and neither will we. what matters for you: an attacker with XO Admin created hardware configs that touch the host filesystem, then left cron files and preload checks behind. that is persistence on dom0, not a cute console trick.
step 1: version and traffic
first question: what xo-server are you running? in the XO web UI it is under About. on an XOA or a source install, your package manager or the xo-server package.json will tell you. then feed it to the kit.
second question: did anyone send the pattern? Vates is blunt about this: “In the case of a successful exploitation, there is no log to detect unauthenticated access.” XO itself will not tell you. the only places you will see the cookie are a reverse proxy that logs cookies, a WAF, or packet captures. the advisory’s patterns are authenticationToken=j:%7B%7D and authenticationToken=j:{}. the kit also flags any j:-prefixed authenticationToken cookie as REVIEW, because Vates warns that regex matching “can be bypassed using other formats,” and a legitimate XO session token is a plain string.

note that cookie logging is not on by default in most nginx configs. if you want this visibility, add $http_cookie to a dedicated log format on the XO server block, keep that log short-lived and access-controlled, and remember it now contains session tokens. yes, that is a trade-off. so is not knowing.
step 2: hunt the XCP-ng hosts
this is the part people will skip and should not. Vates gives three host-side checks. run them on every XCP-ng host managed by the XO instance:
xe vm-list params=name-label,platform | grep -B1 'hvm_serial: file'zgrep -- '-serial file:' /var/log/xensource.log*ls -la /etc/ld.so.preload /.ustar* /etc/cron.d/zz-ustar*
and look for the pivot2 VM from XO or xe vm-list. the kit’s xcpng mode takes the output of those commands (copied off the host) and does the matching, so you can collect from a lot of hosts and analyze from one Kali box.

the advisory’s own caveat, which we are repeating because it is correct: these IoCs cover the campaign Vates has seen. “The absence of these IoCs alone is not sufficient to conclude that no compromise has occurred.” a clean result means “not this operator, the way they worked last week.” it does not mean clean.
step 3: containment while you patch
Vates’ mitigations, in its order:
- do not expose Xen Orchestra to the internet. put it on a dedicated management network.
- block
91.211.251.112ingress and egress, and block Tor traffic to cut off the onion C2. - optionally block the empty-token pattern at a WAF or reverse proxy, with the bypass warning above.
- if XO was compromised, rotate every password and secret Vates VMS uses: proxies, backup targets, S3 credentials, root accounts, user accounts.
the kit’s mitigation script prints an nftables table that drops the IOC address both ways and restricts the XO UI and API to a management CIDR you supply. dry run by default. it does not touch anything until you pass --apply.

one more uncomfortable point: if your XO instance was reachable from the internet, the attacker could have pulled every token listing, backup credential, and S3 key it knows about. the backups are the scary part. backup targets are where ransomware crews go to make sure you cannot restore. rotate those first.
the industry part, since we have to
every hypervisor migration deck of the last two years had a slide called “management plane.” it usually said “web UI” and a happy arrow. nobody drew the arrow pointing from the internet. here we are.
also: this has no CVE. if your vulnerability management program only acts on CVE IDs, KEV entries, or scanner plugins, it saw nothing today. vendor advisories without CVEs are a real, recurring category. somebody on your team needs to read the Vates, Citrix, Fortinet, and friends advisory pages directly, like it is 2009. we know. we are sorry.
what to do, in order
- update XO to 6.10.0 (latest) or 6.9.1 (stable). confirm xo-server 5.213.1 or 5.211.2.
- take XO off any network that is not a management network, today, patched or not.
- block
91.211.251.112and Tor egress. - run the three Vates host checks on every XCP-ng host, plus look for
pivot2. - if anything hits, or XO was internet-facing: isolate, rebuild, and rotate every XO and Vates VMS secret, starting with backup and S3 credentials. then invalidate all XO authentication tokens.
- subscribe to the Vates advisories page. there will be a CVE eventually. do not wait for it.
download the Kali defensive kit
xo-vsa-2026-062-defensive-kit.zip contains xo-062-audit.py (version, httplog and xcpng modes), xo-062-mitigate.sh (nftables containment, dry run by default), a README, and the synthetic lab files from the screenshots. fixed versions are printed in every output: xo-server 5.213.1 (latest) and 5.211.2 (stable). it makes no network connections and contains no exploit code. read it before you run it.
sources
- Vates, VSA-2026-062: Empty-object login token (published and updated 2026-11-10)
- Vates, security advisories index (checked 2026-11-10)
- Xen Orchestra PR #10748, “fix(xo-server): validate authentication token IDs” (merged 2026-11-10 08:29 UTC)
- BleepingComputer, “Critical Xen Orchestra auth bypass flaw exploited to hijack VM servers” (2026-11-10), for confirmation that no CVE or CVSS had been assigned
While you’re here, check your own front door
Reading about someone else’s bad week is a good time to glance at your own exposure. Dark Edge Consulting runs a Free External Security Snapshot: a passive, no-touch look at your domain, email, and website, graded A to F with the fixes spelled out. No login, nothing scanned on your side, just what attackers already see from the outside.
See what attackers see from the outside →
Rather talk it through with a human? Book a strategy call →
