
Nicht-destruktiver Patch-Status-Prüfer für SolarWinds ARM CVE-2026-28326, der den gRPC-Listener auf TCP 55555 abfragt, um VULNERABLE, PATCHED oder INCONCLUSIVE zu melden.
Eine unauthentifizierte Patch-Status-Prüfung für CVE-2026-28326, die unauthentifizierte Remote-Code-
Execution-Schwachstelle in SolarWinds Access Rights Manager (ARM), behoben in
ARM 2026.2.1
am 2026-09-17. CWE-798, CVSS 8.8 High
(CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H). Gemeldet an SolarWinds von Kai Huang von Armadin.
Das AV:A in diesem Vektor geht davon aus, dass TCP 55555 nur aus einem angrenzenden Netzwerk
erreichbar ist, was der Ausgangslage entspricht, für die ARMs eigene Firewall-Tools konzipiert sind;
wurde der Port breiter geöffnet, unterschätzt der Score die Exposition für diese Umgebung.
ARMs gRPC-Remoting-Listener auf TCP 55555 authentifiziert Peers mit einem Client-Zertifikat.
Ungepatchte Builds akzeptieren zudem eine Fallback-Anmeldeinformation, die von einem fest
einprogrammierten Schlüssel abgeleitet wird, der auf jeder Installation identisch ist, wodurch diese
Authentifizierung umgangen und ein unsicherer Deserialisierungspfad offengelegt wird. Das Ergebnis
ist Codeausführung als ARM-Dienstkonto, das standardmäßig als LocalSystem läuft.
Das Skript meldet, ob der Fix auf jedem Ziel vorhanden ist. Es liest die Statuscodes, die vom
Authentifizierungs-Interceptor zurückgegeben werden, und verwendet den Schlüssel nicht, authentifiziert
sich nicht und sendet keine Daten an den Deserialisierungspfad. Ein anderes Ergebnis als VULNERABLE
bedeutet nicht für sich genommen, dass ein Ziel gepatcht ist; siehe
Interpretation nicht-schwachstellenbehafteter Ergebnisse.
# single target (port defaults to 55555)
./cve_2026_28326_check.py arm.example.com
# explicit port
./cve_2026_28326_check.py arm.example.com:55555
# several targets
./cve_2026_28326_check.py arm-a.example.com arm-b.example.com
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_28326_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_28326_check.py -f targets.txt --json > results.json
Python 3.8+, nur Standardbibliothek — keine Drittanbieter-Pakete.
Führen Sie das Tool gegen den ARM-Server aus, nicht gegen einen Collector. Beide Rollen lauschen auf
55555, aber ein Collector lehnt externe Anfragen vor und nach dem Fix auf dieselbe Weise ab, sodass
das Tool ihn als INCONCLUSIVE meldet.
| Flag | Beschreibung |
|---|---|
TARGET | Ein oder mehrere HOST[:PORT]-Ziele; der Port ist standardmäßig 55555 |
-f, --targets-file FILE | Ziele aus einer Datei lesen (eine pro Zeile; #-Kommentare) |
-p, --port PORT | Standardport, wenn ein Ziel keinen angibt (Standard: 55555) |
--timeout SECS | Timeout pro Probe (Standard: 12) |
--workers N | Gleichzeitige Ziele (Standard: 16); die Ausgabe bleibt in Eingabereihenfolge |
-b, --brief | Eine ausgerichtete Zeile pro Ziel, zum Scannen vieler Hosts |
--json | Strukturiertes JSON ausgeben, einschließlich beider Proben pro Ziel |
--no-color | Farbige Ausgabe deaktivieren (berücksichtigt auch NO_COLOR und Nicht-TTY) |
Schwachstellenbehafteter Server (Standardausgabe; die [!]-Markierung und VULNERABLE werden auf
einem TTY rot angezeigt):
$ ./cve_2026_28326_check.py arm.example.com
[!] arm.example.com:55555: VULNERABLE [unavailable-on-server-role]
Remoting/Send returned 14 UNAVAILABLE on a listener answering as the server role, the pre-patch rejection path (<= 2026.2.0.42)
Gepatchter Server. Der gefixte Build gibt eine grpc-message zurück, die das Tool neben dem
Urteil ausgibt:
$ ./cve_2026_28326_check.py arm-dr.example.com
[+] arm-dr.example.com:55555: PATCHED [unauthenticated-gate]
Remoting/Send returned 16 UNAUTHENTICATED, the localhost and per-process-token gate introduced by the fix (>= 2026.2.1.7)
grpc-message: Client certificate not found. Please register your application first.
Listener, der nicht klassifiziert werden kann. Ein Collector oder ein Server, der externe Anfragen ablehnt, beantwortet beide Proben auf beiden Builds auf dieselbe Weise:
$ ./cve_2026_28326_check.py collector.example.com
[?] collector.example.com:55555: INCONCLUSIVE [external-requests-refused]
the listener refuses external requests, which a collector role and a restricted server both do identically on either build; scan the ARM server itself
Mehrere Ziele mit --brief:
$ ./cve_2026_28326_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE arm.example.com:55555 unavailable-on-server-role
PATCHED arm-dr.example.com:55555 unauthenticated-gate
INCONCLUSIVE collector.example.com:55555 external-requests-refused
UNAFFECTED db.example.com:55555 not-arm
ERROR offline.example.com:55555 unreachable
exit: 1
JSON-Ausgabe mit --json. Beide Probenergebnisse sind enthalten, damit das Urteil gegen die
Rohantworten geprüft werden kann:
$ ./cve_2026_28326_check.py arm.example.com --json
[
{
"target": "arm.example.com:55555",
"verdict": "VULNERABLE",
"reason": "unavailable-on-server-role",
"detail": "Remoting/Send returned 14 UNAVAILABLE on a listener answering as the server role, the pre-patch rejection path (<= 2026.2.0.42)",
"grpc_message": "",
"probes": [
{
"path": "/SolarWinds_ARM_GRPC_ConnectionAuthrization.ConnectionAuthorization/GetCertificate",
"status": 0,
"message": "",
"alpn": "h2",
"note": ""
},
{
"path": "/SolarWinds_ARM_Remoting.Remoting/Send",
"status": 14,
"message": "",
"alpn": "h2",
"note": ""
}
]
}
]
Die Prüfung ist für den Einsatz gegen Produktionssysteme gedacht:
Remoting/Send-Nachrichtenkörper ausgelöst. Beide Proben senden eine gRPC-Nachricht
mit einem Körper der Länge null, und der Interceptor lehnt den Aufruf ab, bevor ein Nachrichtenkörper
verarbeitet wird.GetCertificate-Probe sendet einen leeren Registrierungscode, den
der Server ablehnt, bevor er ausstehende Codes nachschlägt oder entfernt, sodass die Probe keinen
Registrierungscode für einen Client verbraucht, der sich mitten in der Registrierung befindet.| # | Aufruf | Zweck |
|---|---|---|
| 1 | ConnectionAuthorization/GetCertificate | Bestätigt, dass der Endpunkt ARM ist und sich in der Serverrolle befindet |
| 2 | Remoting/Send | Die Patch-Status-Probe |
Auf einem ungepatchten Server protokolliert eine abgelehnte anonyme Anfrage auf Debug-Level
(Unable to validate remote request token) und eine Informationszeile, die den Peer benennt.
Gepatchte Builds protokollieren eine Ablehnung, die den Peer und den oben in der grpc-message
zitierten Registrierungshinweis benennt. Kein Build zeichnet eine Sitzung auf.
Um nach Ausnutzung statt nach Exposition zu suchen, prüfen Sie ARMs NetworkConnectionEntry-Datensätze.
Ein Eintrag mit IsAuthenticated=false und einem befüllten ClientCertThumbPrint ist konsistent mit
der Authentifizierungsumgehung und tritt im normalen Datenverkehr nicht auf.