core-jmp core-jmpdeath of core jump

CVE-2026-59309 & CVE-2026-59310: Patch-diffing VMware vCenter to Auth Bypass and RCE

Mobeta patch-diff of VMSA-2026-0006: vCenter 8.0.3 syslog dynafile traversal (CVE-2026-59310) and SASL SRP A=N auth bypass (CVE-2026-59309), both pre-auth 9.8. Templates, Ghidra, Sigma, YARA. RCE hop unpublished.

oxfemale October 2, 2026 36 min read 81 reads
Export PDF
CVE-2026-59309 & CVE-2026-59310: Patch-diffing VMware vCenter to Auth Bypass and RCE
Original text: "CVE-2026-59309 & CVE-2026-59310: Patch-diffing VMware vCenter to auth bypass and RCE" — Raphaël Dray, Lead Pentester, Mobeta (22 September 2026). Advisory: VMSA-2026-0006. Figures, rsyslog templates, the vendor diff, Sigma, and YARA follow the source. The authors confirmed RCE via the write primitive and did not publish that last hop; this draft does not add it. Lab VMs only.
CVE-2026-59309 checker: SRP bind as administrator@vsphere.local with no password
Original OG/featured (1878×700 PNG): SRP bind as administrator@vsphere.local, then Administrators group members. Source: original article.
Two attack paths into one appliance: syslog and LDAP SRP
Same appliance, two unlocked doors, one build number.
Comparing 63 rebuilt packages instead of opening boxes
A full product build bumps every VMware RPM. Hash the files, not the filenames.

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
Kitchen table: The building has a mail slot (syslog) and a vault keypad (LDAP/SRP). The mail slot files whatever name you write on the envelope, including names with ../ that walk out of the mailroom into the public lobby (VAMI). The keypad was supposed to reject a cheat combination the math textbook forbids (A multiple of N). It only rejected zero. Send the modulus itself, the shared secret becomes an empty box, and SHA-1 of empty is a public number. Two doors, one renovation (8.0.3.01000). Patch the renovation. Do not wait for a public last hop.
For operators: ATT&CK: T1190 both; T1505.003 if the write lands in a web root; T1078.001 for SRP-as-admin. Listen 514/6514 and 389/636/2020 only from management/syslog peers. Deploy the published Sigma/YARA. Do not treat “rsyslog RPM unchanged” as “syslog is fine.” The bug is the VMware template.

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

FieldValue
AdvisoryVMSA-2026-0006
CVE-2026-59309VMware Directory Service, authentication bypass · CVSS 9.8
CVE-2026-59310vCenter Syslog receiver, directory traversal → arbitrary code execution · CVSS 9.8
AffectedvCSA 8.0.3.00900 (build 15564605) and earlier 8.0.3.x
Fixed8.0.3.01000 (build 15566761)
From the original. Two 9.8s, one of them already in the wild, no public write-up at the time they started.

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.

sha256sum of rsyslog RPM identical in both ISOs
SHA-256 0510041c1c5d88243e440ebe663724130aa2d3b89805d879eaeb6fd194883e6c on both 00900 and 01000. Source: original article.

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.

A guestbook name with dots and slashes filed in the wrong drawer
HOSTNAME is the name on the envelope. The mailroom used it as a folder path.

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

CVE-2026-59310 checker sending traversal syslog and fetching the planted file
Checker against 192.168.3.150: send traversal syslog, wait for flush, fetch marker. Source: original article.
Firefox showing planted MOBETA-syslog.log on VAMI HTTPS
Browser read-back of the planted file on :5480. Source: original article.

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

  1. Reachability to UDP/514, TCP/514 (imptcp), or TLS syslog (imtcp); none require authentication.
  2. No app-name spoof on esxLoc; only HOSTNAME ≠ vCenter hostname.
  3. rsyslog user can write; configurev2/ exists, shipped by VAMI.

Detection and mitigation (59310)

  1. Patch to 8.0.3.01000+ (secpath-replace on all four templates).
  2. Restrict inbound 514/6514 to trusted forwarders.
  3. 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 )
}
For operators: A HOSTNAME with ../ is not a noisy heuristic; it is RFC-invalid. The file rule (rsyslogd writing htdocs) is post-compromise and high confidence. YARA wants RFC 5424 prefix AND traversal AND a vCenter path fragment together.
Kitchen table: If a postcard’s return address contains “go up two floors then into the lobby,” it is not a real return address. That is the network Sigma. If the mail clerk is seen putting a letter in the public display case, that is the file Sigma.

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.

ArtifactHash vs 01000
vmdird (usr/lib/vmware-vmdir/sbin/vmdird)byte-identical
libvmdirauth.sobyte-identical
libvmdirclient.sobyte-identical
libsaslvmdirdb.sobyte-identical
liblsass_auth_provider_vmdir.soSHA-256 differs
From the original sweep. The daemon never moved. The Likewise plugin did — for the wrong reason.

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.

Import table diff adding BN_div only in 8.0.3.01000
10a11, one added line: BN_div. Source: original article.

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.

nm -S showing srp_server_mech_step2 size 0x71c vs 0x7e9
One size mismatch in the whole file. Source: original article.

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.

A vault opened with a cheat combination of zeros
A = N is the cheat combination the RFC said to reject.

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.

Ghidra side-by-side: patched build inserts A mod N == 0 check with BN_div
Green block: BN_new, BN_CTX_new, BN_div, BN_is_zero, bail with Illegal value for A (A mod N == 0). Source: original article.

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.

rabin2 -z: patched build gains Illegal value for A (A mod N == 0)
Fourth Illegal value string is new in the patch. Source: original article.

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.

CVE-2026-59309 checker: SRP bind as administrator@vsphere.local, Administrators has 9 members
3-step SRP as administrator@vsphere.local, A=N, forged M1, AES-OFB session, cn=Administrators query. Source: original article.

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)

  1. Patch 8.0.3.01000+ (A mod N == 0 rejection; client-side symmetry belongs on custom SRP clients too).
  2. vmdird LDAP 389/636/2020 only from management networks. The bug needs TCP and a known identity, no prior credential.
  3. Alert on SRP binds where A is 0 or a multiple of advertised N — not a natural client value.
Kitchen table: The vault asked “is the combination zero?” and forgot “is it a multiple of the public modulus?” Sending the modulus itself is like saying the word “open” in the language the lock already uses for math. After the door opens, the inner radio (AES-OFB) is keyed from SHA-1 of nothing, which everyone can compute. Then you can read the staff directory.
For operators: This is not “LDAP anonymous bind is on.” SRP completes as a real identity. Session keys are deterministic once A=N. YARA matches BindRequest+SRP and the first 32 bytes of RFC 5054 1024- or 2048-bit N at the head of A. We are not shipping a bind client. The math and the checker screenshot are the public record.

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

TermKitchenOperator
Full product buildEvery box on the pallet gets a new sticker.63/538 RPMs bump together; not a hotfix.
rsyslog byte-identicalThe 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 ruleAnything not addressed to this building goes to the ESX drawer.HOSTNAME != $$myhostname → esxLoc.
secpath-replaceStrip ../ from the envelope before filing.rsyslog property replacer in 01000.
Used-twice ../ padWalk upstairs until you hit the roof, extras do nothing.../ at / is identity; 16× covers both depths.
DWARF-only hash missThe shipping label changed, the machine inside did not.bora- path in debug info.
A = NThe 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 layerAfter the door, a coded walkie-talkie.AES-128-OFB + HMAC-SHA1 EtM.
Kitchen column is ours; operator column follows Dray.

ATT&CK, CWE, and what not to file

BugMapDo not file
CVE-2026-59310 AFW + HTTPS read-backCWE-22; T1190; T1505.003 if web rootA bug in upstream rsyslog 8.2306
CVE-2026-59310 → RCET1190; advisory 9.8A public CGI/cron recipe — they withheld it
CVE-2026-59309 SRP A=NCWE-347 / missing RFC check; T1078.001; T1087.002“Anonymous LDAP is enabled”
Likewise plugin hash changeRebuild noiseThe 59309 fix
Two CVEs, one VMSA, two protocols. Patch both.

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)

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

  1. Confirm vCSA build ≥ 15566761 (8.0.3.01000). If not, this is an emergency change, not a quarterly.
  2. 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.
  3. Load the six published rules. Tune the LDAP CIDR filter before it pages on vSphere itself.
  4. Spot-check :5480/configurev2/ for files that are not VAMI’s. Spot-check rsyslog user-owned files under htdocs.
  5. Turn vmdird bind/search logging up long enough to see SRP success followed by Administrators searches.
  6. 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

  1. Upgrade vCSA to 8.0.3.01000 or later. Confirm the template contains secpath-replace and libsrp links BN_div.
  2. ACL UDP/TCP 514 and 6514 to syslog peers only. ACL 389/636/2020 to management only. Treat 5480 as sensitive.
  3. Deploy the three 59310 and three 59309 rules from the original (Sigma + YARA) on NTA, Linux file events, and vmdird verbose logs.
  4. Hunt existing htdocs files owned by the rsyslog user; hunt SRP binds followed by Administrators searches.
  5. Do not close 59310 because “we don’t use syslog from ESXi” — esxLoc is the default last rule for any foreign HOSTNAME.
  6. Do not close 59309 because “LDAP is internal” without measuring who can actually reach vmdird.
  7. Lab the public AFW marker on an isolated appliance if you must; do not aim it at production htdocs.
  8. 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.

oxfemale Vulnerability research, reverse engineering, and exploit development.
// Discussion