



Executive Summary
On 22 September 2026 Mobeta’s Raphaël Dray published a 31-minute patch-diff of VMSA-2026-0006: two pre-auth CVSS 9.8 bugs in vCenter Server Appliance, one already exploited in the wild when they started, neither with a public technical write-up. Vulnerable: vCSA 8.0.3.00900 (build 15564605) and earlier 8.0.3.x. Fixed: 8.0.3.01000 (build 15566761). CVE-2026-59310 is CWE-22 in VMware’s rsyslog templates: RFC 5424 HOSTNAME is interpolated into a dynafile path, directories are created, and the message body is written. They planted a file under VAMI’s static root and read it back on :5480. They achieved RCE from that write and left the last hop unpublished. CVE-2026-59309 is a missing RFC 5054 check in Cyrus SASL’s libsrp.so: send A = N on an SRP bind, the shared secret collapses to 0, K = SHA-1(empty) = da39a3ee5e6b4b0d3255bfef95601890afd80709, forge M1, ride the AES-OFB security layer, LDAP as administrator@vsphere.local with no password.
The pipeline is the other story: 63 of 538 RPMs change in a full build; rsyslog’s RPM is byte-identical (0510041c1c5d88243e440ebe663724130aa2d3b89805d879eaeb6fd194883e6c); 1,061 files differ; path/type/magnitude triage; whole-file hashes lie (Likewise plugin was DWARF-only); .text diffs found srp_server_mech_step2 and a new BN_div import. This page keeps every original screenshot, template, vendor diff, Sigma rule, and YARA rule, then adds kitchen-table translations. It does not add the unpublished RCE hop.
Two independent, unauthenticated paths into the same appliance: the syslog receiver on UDP/TCP 514, and the Directory Service’s vmdir/STS RPC surface.
Raphaël Dray, Mobeta, 22 September 2026
How to read this page
- If you run vCenter: patch 8.0.3.01000+, restrict 514/6514 and 389/636/2020, then the detection rules at the end of each CVE section.
- If you patch-diff for a living: Part 1 is the method. Whole-file SHA-256 of Likewise was a false positive; section hashes were not.
- If crypto is not your morning: skip the SRP formulas; the kitchen vault box is the same bug.
- If you write detections: the Sigma and YARA blocks are the author’s, verbatim.
The advisory tells you what, not where
| Field | Value |
|---|---|
| Advisory | VMSA-2026-0006 |
| CVE-2026-59309 | VMware Directory Service, authentication bypass · CVSS 9.8 |
| CVE-2026-59310 | vCenter Syslog receiver, directory traversal → arbitrary code execution · CVSS 9.8 |
| Affected | vCSA 8.0.3.00900 (build 15564605) and earlier 8.0.3.x |
| Fixed | 8.0.3.01000 (build 15566761) |
Why RPM-diffing alone gets you nowhere
A full product build, not a hotfix: 63 of 538 RPMs change filename together. Every VMware-* package rebuilds whether it contains a fix or not. Two unrelated patches share one version. The package list is a suspect list, not a location.
Free gift: rsyslog-8.2306.0-3.ph4.x86_64.rpm is byte-identical across both ISOs. CVE-2026-59310 is not upstream rsyslog. It is VMware configuration or wrapping code.

So they extracted every RPM on both sides into filesystem trees (RPM → cpio.gz → cpio → files, 538 packages × 2), SHA-256’d every file, and diffed hash listings. 1,061 files differ in content. Triage, in order: (1) paths under syslog/vmdir/lwraft/afd or matching *sts*; (2) binaries/JVM over config, without ignoring config — one fix lived there; (3) small diffs over whole-file recompiles. That produced a short list in the two families the advisory already named.
Two threads, two write-ups
Identity/directory packages vs VAMI syslog templates. Different protocols, listeners, footholds; same version number. Independent unauthenticated paths: syslog UDP/TCP 514 (and TLS), and vmdir/STS RPC. They took syslog first: smaller blast radius (a config template), and the in-the-wild one.
CVE-2026-59310: syslog path traversal
CWE-22 → pre-auth arbitrary file write, advisory language “arbitrary code execution,” CVSS 9.8. vCenter’s built-in rsyslog (UDP/514, plaintext TCP/514 imptcp, TLS imtcp) is configured from one file: /usr/lib/vmware-visl-integration/config/vmware-syslog.conf.template. In the vulnerable build, four dynafile templates interpolate %app-name% and %hostname% with no sanitization:
$template defaultLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
$template vpxdLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
$template rsyslogadminLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
$template esxLoc,"/var/log/vmware/esx/%hostname%/%hostname%-syslog.log"
APP-NAME and HOSTNAME are attacker-controlled RFC 5424 header tokens. Nothing authenticates them before omfile’s dynafile writer auto-creates parent directories ($CreateDirs defaults on) and appends. An unauthenticated message on any of three listeners can fall through to the last unconditional ruleset rule, hit esxLoc, and write the attacker’s body to a chosen path. If that path is under VAMI’s static root, HTTPS read-back needs no extra step.

No spoofed app-name required
esxLoc is the easy one: last unconditional rule, bound to every listener:
# VC syslog server log collection
if ($hostname != $$myhostname) then ?esxLoc;esxFmt
$InputPTCPServerBindRuleset all and $InputUDPServerBindRuleset all bind UDP, plaintext TCP, and TLS to the same ruleset. Any message whose HOSTNAME ≠ the appliance hostname (anything from outside) falls through to esxLoc. Content is esxFmt:
$template esxFmt,"%timestamp:::date-rfc3339% %syslogseverity-text% %hostname% %app-name% %msg%\n"
MSG is written verbatim — arbitrary bytes if the attacker wants.
The vendor patch is the data-flow confession
8.0.3.01000 wraps every substitution with rsyslog’s secpath-replace property replacer (strips/neutralizes .. and separators). Exhibit E from the original:
-$template defaultLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
+$template defaultLoc,"/var/log/vmware/%app-name:::secpath-replace%/%app-name:::secpath-replace%-syslog.log"
-$template vpxdLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
+$template vpxdLoc,"/var/log/vmware/%app-name:::secpath-replace%/%app-name:::secpath-replace%-syslog.log"
-$template rsyslogadminLoc,"/var/log/vmware/%app-name%/%app-name%-syslog.log"
+$template rsyslogadminLoc,"/var/log/vmware/%app-name:::secpath-replace%/%app-name:::secpath-replace%-syslog.log"
-$template esxLoc,"/var/log/vmware/esx/%hostname%/%hostname%-syslog.log"
+$template esxLoc,"/var/log/vmware/esx/%hostname:::secpath-replace%/%hostname:::secpath-replace%-syslog.log"
Nothing else about the ruleset or writer changed. Content-hash diff of the ISO trees is how they isolated it; rsyslog itself was identical, so they stayed in VMware’s template.
The “used twice” trick
esxLoc uses %hostname% twice: /var/log/vmware/esx/<H>/<H>-syslog.log — directory and filename prefix, same literal, different depths. A payload that cancels one depth will not cancel the other — unless you overshoot to /. .. at / is a no-op. Pad with more ../ than either expansion needs; both walk to root; extras absorb; both push the same forward path; only the last component differs (second gets -syslog.log).
Concrete, as published: H = "../"*16 + "opt/vmware/share/htdocs/configurev2/MOBETA". Directory start depth 4: 4 of 16 ../ reach /, 12 no-ops, then the forward path → /opt/vmware/share/htdocs/configurev2/MOBETA as a directory (mkdir-parents). File start depth 6 inside that directory: 6 of 16 to root, same forward path, final component MOBETA-syslog.log → /opt/vmware/share/htdocs/configurev2/MOBETA-syslog.log.
Impact
/opt/vmware/share/htdocs/ is VAMI’s document root on :5480; configurev2/ is reachable without further HTTP traversal. Planted file:
https://<vcenter>:5480/configurev2/MOBETA-syslog.log


Confirmed: unauthenticated arbitrary file write with attacker-controlled content, readable over HTTPS. Escalation to RCE (advisory 9.8 / “arbitrary code execution”) means aiming the same primitive at an executable sink — CGI directory or cron drop-in are the obvious candidates. They achieved RCE. They do not publish that chain. Neither do we.
Preconditions
- Reachability to UDP/514, TCP/514 (imptcp), or TLS syslog (imtcp); none require authentication.
- No app-name spoof on esxLoc; only HOSTNAME ≠ vCenter hostname.
- rsyslog user can write; configurev2/ exists, shipped by VAMI.
Detection and mitigation (59310)
- Patch to 8.0.3.01000+ (
secpath-replaceon all four templates). - Restrict inbound 514/6514 to trusted forwarders.
- Alert on files under
/var/log/vmware/**or/opt/vmware/share/htdocs/**with..-derived components, or HOSTNAME/APP-NAME containing/or..(invalid per RFC 5424 §6.2.4).
Author’s Sigma (network), Sigma (file), and YARA, verbatim:
title: CVE-2026-59310: vCenter Syslog HOSTNAME Path Traversal Attempt
id: 7f4e2a1c-83b0-4d9e-a5c7-f612e8903421
status: experimental
description: |
Detects syslog messages sent to vCenter's rsyslog receiver (TCP/UDP 514,
TLS 6514) whose HOSTNAME header field contains path-traversal sequences.
In vCenter 8.0.3.00900 and earlier the HOSTNAME field is interpolated
unsanitized into the esxLoc dynafile path template, enabling unauthenticated
arbitrary file write (CVE-2026-59310, CVSS 9.8).
A HOSTNAME value containing ../ is invalid per RFC 5424 s6.2.4 and is not
produced by any well-behaved syslog sender.
references:
- https://www.vmware.com/security/advisories/VMSA-2026-0006.html
- https://www.rfc-editor.org/rfc/rfc5424
author: Raphael Dray, Mobeta
date: 2026/08/21
tags:
- attack.initial_access
- attack.t1190
- attack.persistence
- attack.t1505.003
logsource:
category: network_traffic
product: syslog
detection:
selection_port:
dst_port:
- 514
- 6514
selection_payload:
payload|contains:
- '../'
- '%2e%2e%2f'
- '%2e%2e/'
condition: selection_port and selection_payload
falsepositives:
- None; a HOSTNAME containing ../ is RFC-invalid and has no legitimate origin
from a correctly implemented syslog sender
level: high
title: CVE-2026-59310: rsyslog File Creation in vCenter VAMI Web Root
id: 3c8a5f2e-94d1-4b7f-b3e6-a017c5284b63
status: experimental
description: |
Detects file-creation events produced by the rsyslogd process inside
/opt/vmware/share/htdocs/, the document root served by vCenter's VAMI
management interface on TCP 5480. rsyslogd has no legitimate reason to
write to this directory; any file it creates there is a high-confidence
indicator of successful CVE-2026-59310 exploitation.
references:
- https://www.vmware.com/security/advisories/VMSA-2026-0006.html
author: Raphael Dray, Mobeta
date: 2026/08/21
tags:
- attack.initial_access
- attack.t1190
- attack.persistence
- attack.t1505.003
logsource:
category: file_event
product: linux
detection:
selection:
Image|endswith: '/rsyslogd'
TargetFilename|startswith:
- '/opt/vmware/share/htdocs/'
condition: selection
falsepositives:
- None; rsyslogd has no legitimate write access to the VAMI web root
level: critical
rule CVE_2026_59310_vCenter_Syslog_PathTraversal
{
meta:
description = "Detects RFC 5424 syslog messages with path-traversal sequences"
" in the HOSTNAME field targeting vCenter's rsyslog dynafile"
" template (CVE-2026-59310, CVSS 9.8)"
author = "Raphael Dray, Mobeta"
date = "2026-08-21"
reference = "https://www.vmware.com/security/advisories/VMSA-2026-0006.html"
cve = "CVE-2026-59310"
strings:
// RFC 5424 PRI + VERSION 1 prefix
$rfc5424_hdr = /^<[0-9]{1,3}>1 /
// Traversal sequences in the HOSTNAME field (bare and percent-encoded)
$trav_bare = "../../" ascii
$trav_pct_lo = "%2e%2e%2f" ascii
$trav_pct_up = "%2E%2E%2F" ascii
// Target path components for the vCenter VAMI htdocs write primitive
$path_htdocs = "opt/vmware/share/htdocs" ascii
$path_conf2 = "configurev2" ascii
condition:
$rfc5424_hdr and
( $trav_bare or $trav_pct_lo or $trav_pct_up ) and
( $path_htdocs or $path_conf2 )
}
CVE-2026-59309: SRP authentication bypass
VMware Directory Service, CVSS 9.8. Same 1,061-file set; syslog was four template lines. Identity looked like one binary of five — until section diffs cleared it.
| Artifact | Hash vs 01000 |
|---|---|
vmdird (usr/lib/vmware-vmdir/sbin/vmdird) | byte-identical |
| libvmdirauth.so | byte-identical |
| libvmdirclient.so | byte-identical |
| libsaslvmdirdb.so | byte-identical |
| liblsass_auth_provider_vmdir.so | SHA-256 differs |
The flag that wasn’t a fix
The Likewise plugin is unstripped: same three exports (VmDirCheckUserInList, VmDirAuthenticateUserPam, VmDirAuthenticateUserEx), same globals, same symbol count. Section-level: .text, .rodata, .data, .symtab, .dynsym identical. Only DWARF differs — embedded build path bora-25333653 → bora-25599006. 143 functions at ~100% similarity. 15,479 bytes of debug info. False positive. Widen the net: every ELF in the 1,061, hash .text only.
Widening the search
28 files have a real .text difference; most are relocation noise (vpxd, vSAN, Envoy). One sits in identity: opt/likewise/lib64/sasl2/libsrp.so.3.0.0. .text grows 208 bytes, one function resizes, new PLT import BN_div — OpenSSL big-number division, never linked in 00900.

srp_server_mech_step2.isra.15 is the only symbol whose size changed: 0x71c → 0x7e9. Similarity: every function ~100% except this one at 0.75.

libsrp.so.3.0.0 is Cyrus SASL SRP, the LDAP bind mechanism vmdird uses. cmusaslsecretSRP is still in .rodata — live, not dead vendor code. libsaslvmdirdb.so (auxprop / verifiers) is identical; the bug is protocol arithmetic, not secret storage.

The missing check
Decompile both srp_server_mech_step2 builds: one inserted block after an existing check that both keep. Both reject A ≤ 0. Only 01000 computes A mod N via BN_div and rejects remainder 0.

That error string does not exist in 00900’s string table. Vulnerable build has short “Illegal value for ‘A'”, “Illegal value for ‘B'”, “SRP: Illegal value for u” — never the A mod N variant, never BN_div.

RFC 5054 §3.1: the host MUST abort if A % N is zero. Without it, anyone who knows a valid identity (e.g. administrator@vsphere.local) opens an SRP SASL bind and sends A = N. Plain A = 0 is already rejected; nonzero multiples of N are not.
The forced collapse of the shared secret
Server shared secret: S = (A · v^u)^b mod N, with u = H(A ∥ B). If A ≡ 0 (mod N), A · v^u is 0 before exponentiation, so S = 0 regardless of the stored verifier v. Session key K is SHA-1 of the big-endian encoding of S. BN_bn2bin(0) is an empty byte string, so K = SHA-1(ε) = da39a3ee5e6b4b0d3255bfef95601890afd80709 — a public constant. No password, no verifier, no factorization of N.
Forging client evidence
Cyrus SASL SRP: M1 = H((H(N) ⊕ H(g)) ∥ H(I) ∥ s ∥ A ∥ B ∥ K ∥ H(L)). Every input is a constant, attacker-chosen A, or server-sent (N, g, s, B). With K known, M1 is exact.
Three-step bind and the security layer
- Step 1: client identity; server returns N, g, s, B.
- Step 2: client sends A and M1.
- Step 3: server M2; bind completes.
vmdird then installs a mandatory security layer: AES-OFB + HMAC-SHA1 over the LDAP socket; plaintext PDUs after that are rejected. Full exploitation means operating that layer under the forged K. Ghidra: cipher aes-128-ofb (cipher_options entry 3 at 0x0030c3c0), Encrypt-then-MAC (srp_encode 0x00103810), _plug_decode 0x00108280 reads 4-byte BE inner length. AES-128 key = K[:16]; HMAC-SHA1 key = K (20 bytes); enc_iv = server sIV from step-2 response; dec_iv = client cIV (they used 16 zero bytes in step 2).
End-to-end: subtree search under dc=vsphere,dc=local returns directory entries including cn=Administrators membership — Administrator, domain-joined machine accounts, every vCenter solution-user principal. Unauthenticated read of the vSphere SSO directory, plus whatever LDAP writes that identity has, no password.

Detection (59309)
Three phases: connection attempt, post-bind enumeration, packet signature of degenerate A. Author’s rules, verbatim:
title: CVE-2026-59309: LDAP Connection to vCenter Directory Service from Unexpected Source
id: 9e1b3d7a-c452-4f8e-8b2d-70e3a914c582
status: experimental
description: |
Detects TCP connections to vCenter Directory Service (vmdird) LDAP ports
(389, 636, 2020) from hosts outside the designated management network.
CVE-2026-59309 requires only a TCP connection and a known account identity;
no credential is needed. A bind attempt from a non-management source is
a prerequisite for exploitation and warrants investigation.
Tune filter_mgmt_cidr to match your management-plane CIDR.
references:
- https://www.vmware.com/security/advisories/VMSA-2026-0006.html
- https://www.rfc-editor.org/rfc/rfc5054
author: Raphael Dray, Mobeta
date: 2026/08/21
tags:
- attack.initial_access
- attack.t1190
- attack.credential_access
- attack.t1078.001
logsource:
category: network_connection
product: linux
detection:
selection:
Initiated: 'true'
DestinationPort:
- 389
- 636
- 2020
filter_mgmt_cidr:
SourceIp|cidr:
- '10.0.0.0/8' # adjust to your management-plane CIDR
- '172.16.0.0/12'
- '192.168.0.0/16'
condition: selection and not filter_mgmt_cidr
falsepositives:
- Legitimate management tooling reaching vmdird from expected subnets;
tune filter_mgmt_cidr before deployment
level: medium
title: CVE-2026-59309: Successful SASL SRP Bind Followed by Sensitive LDAP Search
id: 2f7c4a9e-d360-4b1c-a8f5-c93b1e075d4a
status: experimental
description: |
Detects a behavioral pattern consistent with CVE-2026-59309 exploitation:
a successful SASL SRP bind to vmdird immediately followed by LDAP search
operations targeting sensitive Directory Information Tree subtrees.
An attacker exploiting this bug authenticates without knowing the account
password; the first observable post-bind action is typically enumeration
of the vSphere SSO directory (cn=Users, cn=Administrators,
cn=ServicePrincipals). Correlate events within a 5-second window per
source IP. Requires vmdird access logging at DEBUG or VERBOSE level.
references:
- https://www.vmware.com/security/advisories/VMSA-2026-0006.html
author: Raphael Dray, Mobeta
date: 2026/08/21
tags:
- attack.initial_access
- attack.t1190
- attack.discovery
- attack.t1087.002
logsource:
product: vmware
service: vmdir
detection:
bind_success:
EventType: 'BIND'
AuthMechanism: 'SASL/SRP'
Result: 'success'
sensitive_search:
EventType: 'SEARCH'
BaseDN|contains:
- 'dc=vsphere,dc=local'
- 'cn=Users'
- 'cn=Administrators'
- 'cn=ServicePrincipals'
condition: bind_success and sensitive_search
falsepositives:
- Legitimate administrative tooling performing SASL SRP binds followed by
directory lookups; validate against known management hosts and service accounts
level: high
rule CVE_2026_59309_vCenter_SRP_DegeneratePublicValue
{
meta:
description = "Detects LDAP packets containing a Cyrus SASL SRP step-2 bind"
" where the client public value A equals the RFC 5054 group"
" modulus N, the degenerate value that collapses the shared"
" secret to zero (CVE-2026-59309, CVSS 9.8)"
author = "Raphael Dray, Mobeta"
date = "2026-08-21"
reference = "https://www.vmware.com/security/advisories/VMSA-2026-0006.html"
cve = "CVE-2026-59309"
strings:
// LDAP BindRequest (tag 0x60) with SASL credentials for mechanism "SRP"
// 60=BindRequest 02 01 03=version 3 04 00=empty DN
// a3=SaslCredentials 04 03 53 52 50="SRP"
$ldap_srp_bind = { 60 ?? 02 01 03 04 00 a3 ?? 04 03 53 52 50 }
// First 32 bytes of RFC 5054 1024-bit group prime N
// Sending A = N means A starts with exactly these bytes
$N_1024 = {
EE AF 0A B9 AD B3 8D D6 9C 33 F8 0A FA 8F C5 E8
60 72 61 87 75 FF 3C 0B 9E A2 31 4C 9C 25 65 76
}
// First 32 bytes of RFC 5054 2048-bit group prime N
$N_2048 = {
AC 6B DB 41 32 4A 9A 9B F6 06 E8 C3 97 3B E7 36
29 72 02 24 8B 74 7D 8A 82 35 EF B6 17 F9 C0 AE
}
condition:
$ldap_srp_bind and ( $N_1024 or $N_2048 )
}
Mitigation (59309)
- Patch 8.0.3.01000+ (A mod N == 0 rejection; client-side symmetry belongs on custom SRP clients too).
- vmdird LDAP 389/636/2020 only from management networks. The bug needs TCP and a known identity, no prior credential.
- Alert on SRP binds where A is 0 or a multiple of advertised N — not a natural client value.
Closing the series
Three products of one build number: an advisory that said two critical bugs existed; a diffing pipeline that said where (one config template; one function in one SASL plugin); and a walkable data flow for both. Syslog was an unsanitized field into a path. Auth bypass was not trusting the first hash hit, killing it at section level, and widening until the real .text change showed up where a package-name grep would never look.
A glossary for both sides of the table
| Term | Kitchen | Operator |
|---|---|---|
| Full product build | Every box on the pallet gets a new sticker. | 63/538 RPMs bump together; not a hotfix. |
| rsyslog byte-identical | The mail clerk is the same person. | Bug is VMware template, not upstream daemon. |
| dynafile %hostname% | The envelope name becomes a folder path. | omfile + $CreateDirs; RFC 5424 HOSTNAME. |
| esxLoc last rule | Anything not addressed to this building goes to the ESX drawer. | HOSTNAME != $$myhostname → esxLoc. |
| secpath-replace | Strip ../ from the envelope before filing. | rsyslog property replacer in 01000. |
| Used-twice ../ pad | Walk upstairs until you hit the roof, extras do nothing. | ../ at / is identity; 16× covers both depths. |
| DWARF-only hash miss | The shipping label changed, the machine inside did not. | bora- path in debug info. |
| A = N | The cheat combination the textbook forbade. | RFC 5054 §3.1 missing check. |
| K = SHA-1(empty) | The inner radio code everyone already knows. | da39a3ee5e6b4b0d3255bfef95601890afd80709 |
| Mandatory security layer | After the door, a coded walkie-talkie. | AES-128-OFB + HMAC-SHA1 EtM. |
ATT&CK, CWE, and what not to file
| Bug | Map | Do not file |
|---|---|---|
| CVE-2026-59310 AFW + HTTPS read-back | CWE-22; T1190; T1505.003 if web root | A bug in upstream rsyslog 8.2306 |
| CVE-2026-59310 → RCE | T1190; advisory 9.8 | A public CGI/cron recipe — they withheld it |
| CVE-2026-59309 SRP A=N | CWE-347 / missing RFC check; T1078.001; T1087.002 | “Anonymous LDAP is enabled” |
| Likewise plugin hash change | Rebuild noise | The 59309 fix |
What this is not
- Not the unpublished RCE hop from the syslog write. AFW to htdocs is documented; CGI/cron is named as obvious and not spelled out.
- Not a drop-in SRP bind exploit. Math, Ghidra offsets, and a checker screenshot are what they published.
- Not “every vCenter on the internet.” 514 and 389 must be reachable. Many are.
- Not a reason to ignore 59309 because you firewalled syslog, or vice versa.
Resources (as in the original)
- VMSA-2026-0006
- RFC 5424 §6.2.4 HOSTNAME/APP-NAME
- RFC 5054 §3.1 A % N == 0
- CWE-22
- rsyslog property replacer
- Cyrus SASL
- Sigma · YARA
- Original Mobeta post
Why the pipeline is the paper
A quarterly-style vCenter train rebuilds everything. Filename diffs are noise. Content hashes of unpacked trees are the first real set (1,061). Path heuristics get you to syslog templates in an afternoon. The same heuristics plus whole-file hashes almost sent 59309 into Likewise DWARF. The method that actually found SRP: hash .text, look for new imports (BN_div), then nm -S for the one grown function, then strings for a message that did not exist yesterday. That sequence is reusable on the next VMSA that ships two 9.8s and a build number.
Config vs binary: they ranked compiled code first and still found 59310 in a template because they did not drop config. The instinct to ignore “just a conf file” is how you miss dynafile interpolation. The instinct to trust “the .so hash changed” is how you miss a bora- path.
In-the-wild on 59310 is why syslog went first. A write to VAMI htdocs is a pager-worthy primitive even before RCE. 59309 is quieter on the wire if you do not parse SRP: it looks like a successful admin bind. That is why the degenerate-A YARA exists.
Network exposure, said plainly
vCenter appliances still show up with 514 and 389 on the same management VLAN as everything else, or worse, on a DMZ because “the UI is 443.” VAMI 5480 is often reachable from the same place you can hit syslog. The AFW read-back demo used that coincidence. If 514 is internet-facing, you are in the in-the-wild set. If only 389 is, you still have a passwordless SSO directory. Segment both.
TLS syslog (6514) is not a fix: the HOSTNAME field is still inside the decrypted message. imtcp vs imptcp vs UDP changes encryption in flight, not interpolation after parse. SRP over LDAPS (636) is not a fix: TLS wraps the same broken arithmetic.
A kitchen recap of both factories
Factory one: a mail slot. You write a fake hotel name with sixteen flights of stairs. The clerk creates folders all the way to the public lobby display and files your letter. Someone in the lobby reads it through the glass (HTTPS :5480). The renovation adds a stamp that blacks out the stairs on the envelope.
Factory two: a vault. You say you are the administrator. The lock asks for a math handshake. You send a public number the RFC said never to accept. The shared secret becomes nothing. The hash of nothing is a famous constant. You prove you know the password by hashing things everyone already saw plus that constant. The inner radio uses the same constant as a key. You ask who is in Administrators. Nine names come back. The renovation adds “if A mod N is 0, hang up.”
One sticker on the pallet (8.0.3.01000) closes both. Until then, two pagers.
RFC 5424, said without the RFC
A syslog message is not a free-form line. RFC 5424 puts PRI, version, timestamp, HOSTNAME, APP-NAME, PROCID, MSGID, then structured data, then MSG. HOSTNAME and APP-NAME are specified as tokens, not paths. Slash and dot-dot are not legal there. That is why Mobeta’s network Sigma can claim near-zero false positives: a well-behaved sender never emits them. vCenter’s template treated those tokens as path segments anyway. The protocol said “name.” The disk said “folder.”
esxFmt then concatenates timestamp, severity, hostname, app-name, and the raw MSG plus a newline. The file you fetch from VAMI is not an empty marker unless you send an empty MSG. The checker used a marker string; an operator aiming at a config file would send that file’s bytes in MSG. That is the AFW primitive. It is also why executable sinks are “obvious” and why they stopped the public write-up there.
UDP 514, TCP 514, TLS 6514 are three ways to deliver the same header. Encryption in flight does not sanitize HOSTNAME after decrypt. Binding all three to one ruleset with $Input*BindRuleset all is why “we only exposed TLS syslog” is not a mitigation. The last rule still fires.
Why sixteen ../ and not four
People copy four ../ from a blog and fail because esxLoc uses the same string at two depths. The directory expansion starts shallower than the file expansion. If you cancel only the directory, the file expansion still has leftover segments and lands in the wrong place (or fails). If you cancel only the file, the directory is wrong. Overshooting to / is the one payload that is correct for both because extra .. at root are no-ops. Sixteen is surplus on purpose, not magic. After both expansions sit at root, the forward path opt/vmware/share/htdocs/configurev2/MOBETA is identical; only the suffix -syslog.log distinguishes the file.
rsyslog’s mkdir-parents is load-bearing. The directory half of the template must succeed so the file half has a container — except after the overshoot both halves recreate the same tree from /. VAMI already shipped configurev2/, so even without mkdir the last components might exist; mkdir makes the MOBETA directory itself. The public demo needed that.
SRP for people who do not want the formulas
SRP is supposed to prove you know a password without sending it. The server holds a verifier v derived from the password. The client sends a public ephemeral A; the server sends B. They combine A, B, and v into a shared secret S that should be impossible to compute if you do not know the password. Then they hash S into a session key K and prove they both have K by exchanging M1/M2.
RFC 5054 says: if the client’s A is a multiple of the public modulus N, S becomes 0 no matter what v is. So the server must abort. vmdird’s plugin aborted only for A ≤ 0. A = N is positive and a multiple of N. S = 0. K = SHA-1 of an empty encoding of zero. That SHA-1 is in every rainbow table on earth because it is the hash of nothing. M1 is then a hash of public constants plus that K. The server checks M1, it matches, the bind is “successful” as whatever identity you claimed in step 1.
LDAP after that is not plaintext. Cyrus SRP turns on a security layer. If you cannot speak AES-OFB with HMAC-SHA1 using keys cut from K, the bind was theater. Because K is public, the layer is public too. IVs come from the handshake (server sIV, client cIV). The original used zero cIV. Then a search of cn=Administrators is just LDAP.
libsaslvmdirdb.so still fetches real verifiers from vmdir. The verifier is unused in the collapsed math. Changing how secrets are stored would not have fixed this. Checking A mod N does.
Patch-diffing as a weekly habit
VMware advisories often look like this: product, version, CVSS, “this component.” The function name is not in the PDF. If you own a lab ISO pair, the Mobeta pipeline is the rest of the job: unpack both, hash files, triage by path and by .text, distrust DWARF, look for new imports and new strings. New error strings are gifts. “Illegal value for ‘A’ (A mod N == 0)” is a commit message the compiler left in .rodata.
63 rebuilt RPMs is normal for a train. It is also how two 9.8s hide next to vSAN log writers that changed by relocation noise. If you only open the packages whose names match the advisory (“vmdir”, “syslog”), you still need the Likewise sasl2 directory, which no package-name grep for vmdird would have ranked first. That is the paragraph to steal for your own VMSA work.
Config files deserve the same hash pass as ELF. Four template lines were the entire 59310 fix. Ranking “binaries first” is a prior, not a filter. They kept config and it paid.
What a SOC should do this week
- Confirm vCSA build ≥ 15566761 (8.0.3.01000). If not, this is an emergency change, not a quarterly.
- If you cannot patch today: firewall 514/6514 and 389/636/2020 to known peers. That is not equivalent to the patch (local attackers, mis-peered syslog) but it cuts the internet-facing set.
- Load the six published rules. Tune the LDAP CIDR filter before it pages on vSphere itself.
- Spot-check :5480/configurev2/ for files that are not VAMI’s. Spot-check rsyslog user-owned files under htdocs.
- Turn vmdird bind/search logging up long enough to see SRP success followed by Administrators searches.
- Tell IR that “no new RPM for rsyslog” is not evidence 59310 is absent, and “vmdird hash unchanged” is not evidence 59309 is absent.
If you are a purple team: reproduce the AFW marker on an isolated 8.0.3.00900, then patch and confirm secpath-replace blocks it. Do not drop a webshell on a production VAMI to “prove 9.8.” The advisory and the HTTPS read-back are enough to get the change board to move.
The in-the-wild sentence
Mobeta stopped on syslog first because it was already being used. A pre-auth write to a management UI’s document root is a commodity primitive: implant, redirect, steal cookies from VAMI, stage the next tool. They confirmed RCE and still withheld the hop. That is a disclosure choice, not a claim that RCE is theoretical. Defenders should assume the people already exploiting 59310 did not withhold anything.
59309 may be quieter in the wild: it looks like an admin logged in. Watch for SRP from addresses that never bind, and for Administrators enumeration immediately after. Solution-user principals in that group listing are a reminder that “read the directory” is already a vSphere identity-plane compromise even before you reset a password.
Listeners, ports, and what “pre-auth” means here
Pre-auth on 59310 means: no syslog credential, no ESXi enrollment, no VAMI login. You send a datagram or a TCP line. The appliance files it. Pre-auth on 59309 means: no password for the identity you claim. You still need a valid identity string. administrator@vsphere.local is the default everyone knows. SSO might use a different domain suffix in some farms; the attack does not care as long as the account exists and SRP is the bind mechanism. It is not anonymous LDAP in the RFC 4513 sense. It is “I am Administrator” accepted without the password.
Port 2020 appears in the original alongside 389 and 636. vmdird has historically exposed extra LDAP-ish listeners for replica/HA paths. If your firewall story is “we closed 389,” check 636 and 2020. If your story is “LDAP is only on the management NIC,” measure it; that is the pentest Mobeta links at the end.
VAMI :5480 is not the syslog bug, it is the read-back. Closing 5480 from untrusted networks does not stop the write; it stops the convenient HTTPS confirm. The file still lands. Cron and CGI do not need 5480. That is another reason the unpublished hop can ignore VAMI entirely.
Photon OS, Likewise, Cyrus SASL, rsyslog: vCenter is a product assembled from those. Bugs live in the glue (templates, plugins) as often as in vpxd. A scanner that only fingerprints vpxd version will still want the appliance build number 15564605 vs 15566761.
Build 15564605 is 8.0.3.00900. Build 15566761 is 8.0.3.01000. If your CMDB stores marketing versions (“8.0 U3”) you will not know which side of the advisory you are on. Collect vpxd -v / appliance build, not only the UI string.
The SHA-256 they printed for rsyslog is a one-line close-out for “did we miss an upstream CVE.” If your copy of that RPM hashes the same on both ISOs, you have reproduced their first negative result. Then open vmware-syslog.conf.template and grep secpath-replace. Presence is 01000-or-backported. Absence is 59310 still live, even if someone rebuilt rsyslog for unrelated reasons.
For 59309, grep the strings of libsrp.so for A mod N == 0 or check whether BN_div is in the import table. Those two artifacts are the patch. vmdird’s own hash is expected to match across the bump; do not use it as a clean bill of health.
None of this requires running their checker against production. The files and strings are on the appliance you already own. The checker screenshots in the original are proof the data flow works, not a tool you must fire at the SSO domain to believe VMware.
Thirty-one minutes of Mobeta is a lot of Ghidra for two missing if-statements. That is the genre: the advisory is a table, the fix is four template lines and one BN_div, and the write-up exists so you do not have to rediscover which of 1,061 files mattered while 59310 is already in the wild. Steal the pipeline. Install the build. Load the rules. Leave the last RCE hop in the unpublished column where they put it.
What “active exploitation” changes about 59310
A 9.8 in an advisory is a calendar item. A 9.8 with in-the-wild in the same week is an incident-response item. Mobeta ordered the paper that way: syslog first, identity second. If you only have time for one change window, 59310 is the one already being used as a write primitive against a management plane. 59309 is not milder — passwordless SSO is a full identity compromise — but it may not be the packet you are catching tonight unless you parse SRP.
Assume planted files exist if 514 was reachable from untrusted networks at any point after 8.0.3.00900 (and earlier 8.0.3.x) shipped and before you patched. List htdocs, list cron, list CGI, list sudoers.d, list authorized_keys for the rsyslog user if that user can write them. The public demo used configurev2; the unpublished hop used “an executable sink.” Hunt both classes.
After patch, secpath-replace should turn ../ into a safe token so the same datagram creates a boring file under /var/log/vmware/esx/ instead of walking to /. Confirm with a lab send, not production. If the lab still writes htdocs, you did not land 01000’s template.
For 59309 after patch, a bind with A=N should fail with the new string. If it still succeeds, you are not running the new libsrp even if the appliance UI says 8.0 U3i or similar. String-scan the on-disk plugin. That is faster than trusting a marketing version.
Logging: vmdird at VERBOSE is noisy. Use it as a hunt window, not a forever SIEM source, unless you have capacity. The behavioural Sigma (SRP success then sensitive search) is the high-signal half; the connection Sigma is the early-warning half and needs CIDR tuning so vCenter talking to itself does not page.
YARA on a TAP in front of 514 and 389 is how you catch attempts that never get a successful write or bind — scanners, failed padding, encoded ../. Host file events catch success. You want both. Attempt-only visibility is how you find the people who have not succeeded yet.
Finally: two bugs, one VMSA, does not mean they share a root cause. Do not write a single “vCenter 9.8” detection. Write a syslog detection and an SRP detection. The original’s six rules already did that split. Copy the split even if you rewrite the YAML for your SIEM dialect.
The figures in this draft are the original Mobeta captures: rsyslog hash, 59310 checker and browser read-back, BN_div import, nm size change, Ghidra side-by-side, rabin2 strings, and the 59309 checker that lists Administrators. The OG PNG (1878×700) is the featured image. Related-post thumbnails from the site footer are not included. Sigma and YARA are copied in full, including the UUIDs and dates Raphaël Dray published (2026-08-21). Treat those IDs as the canonical rule names if you import them.
A note on checkers versus exploits
The original shows terminal checkers against 192.168.3.150. Those are lab proofs: a marker in a planted file, a bind as administrator@vsphere.local, a listing of nine Administrators members. They are not a permission slip to run the same against a customer SSO domain “to verify.” Verification on an appliance you own is: strings, hashes, template grep, then a lab ISO. Verification on an appliance you do not own is a crime. This draft is a detection-and-patch article that happens to contain the public math.
The AFW path is fully specified because you cannot hide a dynafile template and a ../ count and still explain the bug. The RCE hop is not, because an executable sink plus a known write is enough of a recipe that they chose not to finish it in public. Respect that line. If you need to test RCE, do it on an isolated 00900 with your own sink, not by pasting a missing section into this page.
SRP is specified down to SHA-1(empty) because that constant is the bug. A working bind client is still software you should not drop on a blog. Packet YARA for A=N is the defender-shaped version of the same fact. Prefer the YARA.
If you remember only the kitchen version: the mail slot files the fake return address, including stairs into the lobby; the vault accepts the public modulus as a combination; one sticker on the pallet (8.0.3.01000) closes both; the mail clerk’s own RPM never changed; the first “changed” library was only a new shipping label. That is the whole 31-minute post, plus the rules, plus the figures, plus the request that you patch before you finish reading. Then restrict who may speak to 514 and 389, and keep the unpublished hop unpublished for good.
Key Takeaways
- VMSA-2026-0006: two pre-auth 9.8s in one vCSA build. 8.0.3.00900 and earlier 8.0.3.x; fix 8.0.3.01000.
- 59310: unsanitized %hostname%/%app-name% in four dynafile templates; esxLoc needs no app-name spoof; AFW + VAMI read-back confirmed; RCE achieved, chain unpublished; patch is secpath-replace.
- 59309: missing A mod N == 0 in libsrp srp_server_mech_step2; A=N → S=0 → K=SHA-1(empty); forge M1; break AES-OFB layer; LDAP as administrator@vsphere.local.
- rsyslog RPM identical; Likewise .so hash was DWARF; real fix is BN_div in libsrp.so.3.0.0.
- Detect: HOSTNAME with ../ on 514/6514; rsyslogd writing htdocs; LDAP from non-mgmt to 389/636/2020; SRP success then cn=Administrators; YARA for A starting with RFC 5054 N.
- Restrict those ports. Patch. Do not wait for a public last hop.
Defensive Recommendations
- Upgrade vCSA to 8.0.3.01000 or later. Confirm the template contains secpath-replace and libsrp links BN_div.
- ACL UDP/TCP 514 and 6514 to syslog peers only. ACL 389/636/2020 to management only. Treat 5480 as sensitive.
- Deploy the three 59310 and three 59309 rules from the original (Sigma + YARA) on NTA, Linux file events, and vmdird verbose logs.
- Hunt existing htdocs files owned by the rsyslog user; hunt SRP binds followed by Administrators searches.
- Do not close 59310 because “we don’t use syslog from ESXi” — esxLoc is the default last rule for any foreign HOSTNAME.
- Do not close 59309 because “LDAP is internal” without measuring who can actually reach vmdird.
- Lab the public AFW marker on an isolated appliance if you must; do not aim it at production htdocs.
- Read RFC 5424 §6.2.4 and RFC 5054 §3.1 once; they are the specs the bugs violate.
Conclusion
A mail slot that files ../ into the lobby, and a vault that accepts A = N, shipped in one vCenter build, one of them already being used. Mobeta recovered both from a noisy 63-RPM bump by hashing trees, not trusting whole-file hashes, and reading the one function that grew. The vendor’s own diffs are the confessions: secpath-replace on four templates, BN_div plus a new error string in SRP. Patch 8.0.3.01000, shrink who can speak 514 and 389, load the published rules, and leave the unpublished RCE hop unpublished.
Original text: "CVE-2026-59309 & CVE-2026-59310: Patch-diffing VMware vCenter to auth bypass and RCE" by Raphaël Dray at Mobeta.


