Every SolarWinds ARM Install Shipped the Same Key — and the Port Is Open by Instruction

CVE-2026-28326 is an unauthenticated remote code execution flaw in SolarWinds Access Rights Manager, disclosed by the vendor on 17 September 2026 and fixed in ARM 2026.2.1. SolarWinds' own advisory describes it in one sentence — “the issue stems from a hardcoded static key” — and scores it 8.8 High on CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, with credit to Kai Huang of Armadin. NVD lists the weakness as CWE-321, use of a hard-coded cryptographic key, and marks 2026.2 and all prior versions affected.

That is the whole vendor record. On 26 September, Bishop Fox published a build-diff analysis that turns those two sentences into something defenders can act on — including a working exploit that returned command execution as NT AUTHORITY\SYSTEM, and a detection method that reads patch state from an unpatched server without touching the vulnerability.

Why an identity product is the wrong place for this bug

Access Rights Manager is SolarWinds' identity governance product. It scans and manages permissions across Active Directory, file servers, Exchange, and SharePoint, and it runs as LocalSystem by default. Code execution inside ARM therefore starts at the top of its host and then reaches outward through every delegated right the product was deliberately granted. This is the same structural problem that makes CI/CD credential exposure so costly: the compromise is not the box, it is the box's standing authority over everything else.

Four decisions that compose into a bypass

The vulnerable surface is a gRPC listener on TCP 55555, used between ARM's own components. Unpatched builds authenticate callers with a client certificate or, as a fallback, an HMAC token keyed by a secret the product reads from its own configuration. Bishop Fox's analysis identifies four independently avoidable design choices that stack:

  • The secret ships in the installer. It is identical on every deployment of a given build and recoverable by anyone who can download the product. Nothing regenerates it at install time.
  • The token authenticates only attacker-controlled values. The HMAC covers a string built from two request fields, with no nonce, challenge, or binding to the channel or request body — so a valid token can be minted offline.
  • Nothing requires the caller to be local. The fallback existed for short-lived local child processes that have no certificate, but no locality check was applied, on a listener bound to every interface including IPv6.
  • Success grants a borrowed identity. Rather than minting a narrow “local task” principal, the path loads a registered client certificate from the server's own store and adopts its thumbprint. On a default install with no client enrolled, a matching certificate is already present — the attacker arranges nothing in advance.

Past the gate sits a duplex stream of .NET Remoting messages deserialized by BinaryFormatter, behind a filter of 27 regular expressions matching known gadget types. That is a denylist over an unbounded type space guarding a sink that executes what it is handed. Microsoft obsoleted BinaryFormatter in .NET 5 and removed it in .NET 9, and documents that untrusted input cannot be made safe in front of it. ARM targets .NET Framework 4.8, where it cannot be switched off.

The mutual-TLS detail worth carrying elsewhere

The listener runs TLS and asks for a client certificate — but requests one rather than requiring one. A caller presenting none still completes the handshake; the connection is simply marked unauthenticated, and the application-layer interceptor becomes the only control left standing. That interceptor is exactly what the shipped key defeats. The difference between the two behaviours is one enum value in a configuration call, which makes this a generalizable audit question: for any service you believe is protected by mutual TLS, confirm which of the two options the code actually passes.

The score assumes a firewall rule nobody verified

The AV:A in the 8.8 vector assumes adjacent-network reach. Nothing in the software enforces that. The Windows Firewall rule ARM creates for 55555 starts disabled, set to block, scoped to localhost — and becomes an allow rule only when an administrator configures one through the Configuration Wizard. But SolarWinds' documented prerequisites instruct administrators to open 55555 inbound, because ARM cannot reach remote collectors otherwise. So on any working multi-host deployment the port is open; the only variable is how widely. On a flat network, or where the rule admits any internal host, the same bug scores 9.8 on AV:N. Bishop Fox's point is that CVSS provides Modified Attack Vector for precisely this, and teams should apply it deliberately rather than inheriting a base score built on an unexamined rule.

One nuance deserves emphasis: the allowlisted zone is not only servers. The wizard registers collector hosts alongside machines running the ARM client console — ordinary administrator workstations. A phished console operator is inside the allowlist without any server being touched.

Detection, and why timing matters

The patch changed a gRPC status code, which incidentally makes safe remote detection possible. Before the fix, every rejection in the auth gate returned gRPC 14 UNAVAILABLE; the rewritten gate returns 16 UNAUTHENTICATED with the message “Client certificate not found. Please register your application first.” Two anonymous, zero-length calls separate the builds — no forged token, no serialized object, no contact with the hardcoded key. Bishop Fox released the checker at github.com/BishopFox/CVE-2026-28326-check. A collector answers both probes identically on both builds, so it is reported unknown rather than patched — treat inconclusive as unresolved, not clean.

The forensic advice is the part teams will get wrong: look for prior exploitation before you patch, not after. The service restart that applies the update wipes the evidence. Where GrantMA is deployed, an ARM administrator can read GET /api/armconfig/network/Connections and look for a record with IsAuthenticated: false beside a populated ClientCertThumbPrint — a pairing the vulnerable path produces and a normal client login does not. That list is memory-only and ages out after three days, so a clean result proves very little. Log-based hunting is worse: a successful attack is quieter than the scan that finds the hole, because the gate returns early on accepting the forged token and the remote-connection warning is suppressed on the server role.

The fix, and its one caveat

SolarWinds deleted the fallback branch rather than hardening it. A caller without a client certificate is now accepted only when the peer is localhost and it presents a token matching one the server generated — a fresh GUID created at every server start, never written to disk, passed on the command line to child processes the server launches itself. Either condition would close the path alone. The deserialization filter was also upgraded from denylist-only to an allowlist checked before a widened denylist, though the allowlist still admits everything under System., where most public gadgets live; for that prefix, the denylist remains the control doing the work.

Removing BinaryFormatter is not available on .NET Framework, and replacing .NET Remoting means rebuilding the transport every ARM component depends on. Faced with a sink they could not remove, SolarWinds shut the door in front of it. That is the right call under the constraint — and a reminder of what legacy serialization costs a product for years after the decision that introduced it.

What to do

  • Patch to 2026.2.1 or later. Bishop Fox's lab work identifies the fixed build as 2026.2.1.7 and affected builds as 2026.2.0.42 and earlier; the vendor advisory states 2026.2 and all previous versions are affected.
  • Hunt before you restart. Read the ARM connection list for IsAuthenticated: false with a populated certificate thumbprint first — patching destroys it, and it only retains three days.
  • Read the actual firewall rule for 55555. “Internal” and “reachable only by ARM's registered components” are different statements, and only the second justifies an 8.8. Re-score with Modified Attack Vector where it does not hold.
  • Audit mutual TLS for request-vs-require across your estate. This failure mode is invisible from outside and not specific to SolarWinds.
  • Treat identity-governance servers as tier-zero. A product running as LocalSystem with delegated rights over AD, file servers, Exchange, and SharePoint deserves the isolation you give domain controllers.

Sources: