
SolarWinds ARM CVE-2026-28326 के लिए गैर-विनाशकारी पैच-स्थिति जाँचकर्ता जो TCP 55555 पर gRPC लिसनर की जाँच करके VULNERABLE, PATCHED, या INCONCLUSIVE रिपोर्ट करता है।
CVE-2026-28326 के लिए एक अनधिकृत पैच-स्थिति जाँच, जो SolarWinds Access Rights Manager (ARM) में अनधिकृत रिमोट कोड
निष्पादन भेद्यता है, जिसे 2026-09-17 को
ARM 2026.2.1
में ठीक किया गया। 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)। SolarWinds को Armadin के Kai Huang द्वारा रिपोर्ट किया गया।
उस वेक्टर में AV:A यह मानता है कि TCP 55555 केवल एक निकटवर्ती नेटवर्क से पहुँच योग्य है, जो वह
स्थिति है जिसके लिए ARM का अपना फ़ायरवॉल टूलिंग बनाया गया है; जहाँ पोर्ट को अधिक व्यापक रूप से खोला गया है वहाँ
स्कोर उस वातावरण के लिए जोखिम को कम आँकता है।
TCP 55555 पर ARM का gRPC रिमोटिंग लिसनर पीयर को क्लाइंट प्रमाणपत्र से प्रमाणित करता है। अनपैच्ड
बिल्ड एक फ़ॉलबैक क्रेडेंशियल भी स्वीकार करते हैं जो एक हार्डकोडेड कुंजी से व्युत्पन्न होता है जो हर
इंस्टॉलेशन पर समान होती है, जो उस प्रमाणीकरण को बायपास करती है और एक असुरक्षित डीसेरिएलाइज़ेशन पथ को उजागर करती है। परिणाम
ARM सेवा खाते के रूप में कोड निष्पादन है, जो डिफ़ॉल्ट रूप से LocalSystem के रूप में चलता है।
स्क्रिप्ट रिपोर्ट करती है कि प्रत्येक लक्ष्य पर फ़िक्स मौजूद है या नहीं। यह प्रमाणीकरण इंटरसेप्टर द्वारा लौटाए गए
स्टेटस कोड पढ़ती है और कुंजी का उपयोग नहीं करती, प्रमाणित नहीं करती, या डीसेरिएलाइज़ेशन पथ पर डेटा नहीं भेजती। VULNERABLE के अलावा कोई परिणाम
अपने आप में यह नहीं दर्शाता कि कोई लक्ष्य पैच किया गया है; देखें गैर-भेद्य परिणामों की व्याख्या।
# 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+, केवल मानक लाइब्रेरी — कोई तृतीय-पक्ष पैकेज नहीं।
टूल को कलेक्टर के बजाय ARM सर्वर के विरुद्ध चलाएँ। दोनों भूमिकाएँ 55555 पर सुनती हैं, लेकिन एक
कलेक्टर फ़िक्स से पहले और बाद में बाहरी अनुरोधों को उसी तरह अस्वीकार करता है, इसलिए टूल इसे
INCONCLUSIVE के रूप में रिपोर्ट करता है।
भेद्य सर्वर (डिफ़ॉल्ट आउटपुट; [!] मार्कर और VULNERABLE एक TTY पर लाल रंग में दिखाए जाते हैं):
$ ./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)
पैच किया गया सर्वर। फ़िक्स किया गया बिल्ड एक grpc-message लौटाता है, जिसे टूल निर्णय के साथ
प्रिंट करता है:
$ ./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.
वह लिसनर जिसे वर्गीकृत नहीं किया जा सकता। एक कलेक्टर, या एक सर्वर जो बाहरी अनुरोधों को अस्वीकार करता है, दोनों बिल्ड पर दोनों प्रोब का उत्तर एक ही तरह देता है:
$ ./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
--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 के साथ JSON आउटपुट। दोनों प्रोब परिणाम शामिल हैं ताकि निर्णय को कच्ची प्रतिक्रियाओं के विरुद्ध
जाँचा जा सके:
$ ./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": ""
}
]
}
]
यह जाँच उत्पादन प्रणालियों के विरुद्ध उपयोग के लिए है:
Remoting/Send संदेश बॉडी में एक सीरिएलाइज़्ड
ऑब्जेक्ट द्वारा ट्रिगर होती है। दोनों प्रोब शून्य-लंबाई बॉडी वाला एक gRPC संदेश भेजते हैं,
और इंटरसेप्टर किसी भी संदेश बॉडी को डिस्पैच किए जाने से पहले कॉल को अस्वीकार कर देता है।GetCertificate प्रोब
एक खाली नामांकन कोड भेजता है, जिसे सर्वर किसी भी लंबित कोड को खोजने या हटाने से पहले अस्वीकार कर देता है, इसलिए प्रोब
उस क्लाइंट के लिए नामांकन कोड का उपभोग नहीं करता जो पंजीकरण के बीच में है।| # | Call | Purpose |
|---|---|---|
| 1 | ConnectionAuthorization/GetCertificate | Confirms the endpoint is ARM and is in the server role |
| 2 | Remoting/Send | The patch-state probe |
एक अनपैच्ड सर्वर पर एक अस्वीकृत अनाम अनुरोध डिबग स्तर पर लॉग होता है
(Unable to validate remote request token) और पीयर का नाम बताने वाली एक सूचनात्मक पंक्ति। पैच किए गए
बिल्ड पीयर का नाम बताने वाली एक अस्वीकृति और ऊपर grpc-message में उद्धृत पंजीकरण संकेत लॉग करते हैं।
कोई भी बिल्ड सत्र रिकॉर्ड नहीं करता।
जोखिम के बजाय शोषण की तलाश करने के लिए, ARM के NetworkConnectionEntry रिकॉर्ड की समीक्षा करें। IsAuthenticated=false और
एक भरे हुए ClientCertThumbPrint वाली प्रविष्टि प्रमाणीकरण बायपास के अनुरूप है और सामान्य ट्रैफ़िक में
नहीं होती।
फ़िक्स ने बदल दिया कि प्रमाणीकरण इंटरसेप्टर उस पीयर को कौन-सा स्टेटस लौटाता है जो कोई क्लाइंट
प्रमाणपत्र प्रस्तुत नहीं करता। पैच से पहले, क्लाइंट-प्रमाणपत्र जाँच में प्रत्येक अस्वीकृति पथ Unavailable सेट करता था।
पैच के बाद, localhost-plus-token जाँच एक विशिष्ट संदेश के साथ Unauthenticated सेट करती है, और
उस पथ पर और कुछ भी 16 नहीं लौटाता। दो अनाम कॉल बिल्ड को अलग करती हैं:
निर्णय Remoting/Send से पढ़ा जाता है क्योंकि केवल डुप्लेक्स-स्ट्रीमिंग और यूनरी हैंडलर
इंटरसेप्टर का स्टेटस प्रसारित करते हैं; सर्वर- और क्लाइंट-स्ट्रीमिंग हैंडलर दोनों बिल्ड पर इसे अधिलेखित कर देते हैं।
जाँच पैच किए गए कोड पथ के व्यवहार का निरीक्षण करती है और किसी संस्करण स्ट्रिंग पर निर्भर नहीं करती। यह रिपोर्ट नहीं करती कि पैच किया गया सर्वर कौन-सा बिल्ड चला रहा है।
ARM लिसनर उन TLS कनेक्शनों को रीसेट कर देता है जो ALPN h2 पर बातचीत नहीं करते, इसलिए टूल इसे
स्पष्ट रूप से सेट करता है। एक स्कैनर जो ALPN के बिना TLS पर कनेक्ट होता है, उसे पोर्ट से कोई डेटा नहीं मिलता।
VULNERABLE और PATCHED प्रत्येक एक देखी गई सर्वर प्रतिक्रिया पर आधारित हैं। INCONCLUSIVE और
ERROR का अर्थ है कि प्रोब ने लक्ष्य को वर्गीकृत नहीं किया, इसलिए इसकी पैच स्थिति अज्ञात है। इस कारण
INCONCLUSIVE को PATCHED से अलग रिपोर्ट किया जाता है।
प्रत्येक निर्णय में एक संक्षिप्त reason टैग होता है। --brief इसे तीसरे कॉलम के रूप में प्रिंट करता है और --json
इसे reason के रूप में ले जाता है।
तीनों INCONCLUSIVE कारणों के लिए पैच स्थिति अज्ञात है। कारण टैग इंगित करता है कि जाँच को फिर से चलाने से पहले
क्या संबोधित करना है।
| Code | Meaning |
|---|
एग्ज़िट कोड 0 में PATCHED, UNAFFECTED, और तीनों INCONCLUSIVE कारण शामिल हैं। एक पैच किए गए लक्ष्य को
उस लक्ष्य से अलग करने के लिए जिसे वर्गीकृत नहीं किया जा सका, एग्ज़िट कोड के बजाय निर्णय पढ़ें
(--brief का पहला कॉलम या --json का verdict फ़ील्ड)।
INCONCLUSIVE के रूप में रिपोर्ट किया जाता है। इस मामले का परीक्षण एक मॉक के विरुद्ध किया गया था जो प्रतिक्रिया को पुनरुत्पादित करता है, न कि
तैनात किए गए कलेक्टर के विरुद्ध।grpc-status को एक शाब्दिक HPACK फ़ील्ड के रूप में स्कैन करता है और Huffman या
डायनामिक-टेबल डिकोडिंग को लागू नहीं करता। gRPC का C कोर, जिसका ARM उपयोग करता है, इन ट्रेलरों को
असंपीड़ित भेजता है। यदि कोई भविष्य का बिल्ड इन्हें Huffman-कोड करता है, तो टूल INCONCLUSIVE के साथ
partial-response रिपोर्ट करता है।PATCHED केवल इस CVE पर लागू होता है और अन्य ARM
भेद्यताओं की स्थिति नहीं दर्शाता।VULNERABLE इंगित करता है कि जिस पथ तक प्रोब पहुँचा उस पर फ़िक्स मौजूद नहीं है। टूल
लक्ष्य पर कोड निष्पादित नहीं करता और पूर्व समझौते का पता नहीं लगाता;
ऊपर NetworkConnectionEntry संकेतक देखें।ARM 2026.2.1 रिलीज़ नोट्स के अनुसार ARM 2026.2.1.7 या बाद के संस्करण में अपग्रेड करें। बिल्ड 2026.2.0.42 और उससे पहले के प्रभावित हैं।
अतिरिक्त नोट्स:
यह कोड एक MIT लाइसेंस के अंतर्गत वितरित किया गया है।
पूर्व पारस्परिक सहमति के बिना लक्ष्यों पर हमला करने के लिए इस टूल का उपयोग अवैध है। सभी लागू स्थानीय, राज्य, और संघीय कानूनों का पालन करना अंतिम उपयोगकर्ता की ज़िम्मेदारी है। डेवलपर्स कोई दायित्व नहीं मानते हैं और इस प्रोग्राम के कारण होने वाले किसी भी दुरुपयोग या क्षति के लिए ज़िम्मेदार नहीं हैं।
| Flag | Description |
|---|
TARGET | One or more HOST[:PORT] targets; the port defaults to 55555 |
-f, --targets-file FILE | Read targets from a file (one per line; # comments) |
-p, --port PORT | Default port when a target omits one (default: 55555) |
--timeout SECS | Per-probe timeout (default: 12) |
--workers N | Concurrent targets (default: 16); output stays in input order |
-b, --brief | Single aligned line per target, for scanning many hosts |
--json | Emit structured JSON, including both probes per target |
--no-color | Disable coloured output (also honours NO_COLOR and non-TTY) |
Remoting/Send | GetCertificate | Build | Verdict |
|---|
14 UNAVAILABLE | 0 OK | <= 2026.2.0.42 | VULNERABLE |
16 UNAUTHENTICATED | any | >= 2026.2.1.7 | PATCHED |
14 UNAVAILABLE | 14 UNAVAILABLE | either | INCONCLUSIVE |
| Verdict | Reason tag | Meaning |
|---|
VULNERABLE | unavailable-on-server-role | Remoting/Send returned 14 on a listener answering GetCertificate as the server role. The fix is not present. |
PATCHED | unauthenticated-gate | Remoting/Send returned 16, the status introduced by the fix. Applies to this CVE only. |
INCONCLUSIVE | external-requests-refused | Both probes returned 14. A collector role and a restricted server behave identically here on both builds. Scan the ARM server itself. |
INCONCLUSIVE | partial-response | Only one probe returned a status, so the pair cannot be compared. Retry. |
INCONCLUSIVE | unrecognized-status | The endpoint answered with a status combination outside the recognized set. |
UNAFFECTED | not-arm | No ARM gRPC services answered here. |
ERROR | unreachable | The TCP connection, TLS handshake, or HTTP/2 exchange failed. |
0 | No target was VULNERABLE |
1 | At least one target is VULNERABLE |
2 | Usage error (bad arguments, invalid target, or unreadable targets file) |
130 | Interrupted (Ctrl-C) |