Skip to main content
Vulnerability Database/CVE-2026-63647

CVE-2026-63647: CordysCRM Authentication Bypass Vulnerability

CVE-2026-63647 is an authentication bypass flaw in CordysCRM allowing unauthenticated access to SSE endpoints. Attackers can read user events, inject messages, or terminate channels. This article covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-63647 Overview

CordysCRM is an open-source, AI-powered customer relationship management (CRM) system that supports private deployment. Versions prior to 1.7.2 expose three anonymous Server-Sent Events (SSE) endpoints through SseController: /sse/subscribe, /sse/broadcast, and /sse/close. The ShiroFilter.addPublicPathFilters configuration permits these SSE paths without authentication, and the endpoints trust a caller-controlled userId parameter instead of deriving identity from an authenticated principal. An unauthenticated network attacker can read another user's private event stream, inject forged system messages, or terminate arbitrary user channels. The maintainers fixed the flaw in release 1.7.2.

Critical Impact

Remote, unauthenticated attackers can hijack SSE channels to read workflow events, approval requests, mentions, and alerts belonging to any user, or disrupt notifications by closing their sessions.

Affected Products

  • CordysCRM versions prior to 1.7.2
  • CordysCRM SseController component (backend/crm/src/main/java/cn/cordys/crm/system/notice/sse/SseController.java)
  • CordysCRM ShiroFilter public path configuration

Discovery Timeline

  • 2026-09-18 - CVE CVE-2026-63647 published to NVD
  • 2026-09-23 - Last updated in NVD database

Technical Details for CVE-2026-63647

Vulnerability Analysis

The vulnerability is a Missing Authentication for Critical Function issue tracked as [CWE-306]. SseController in CordysCRM exposes three endpoints used for real-time notification delivery over SSE. Because ShiroFilter.addPublicPathFilters allow-lists the /sse/* paths, the Shiro security filter chain never enforces authentication on these routes. Each endpoint accepts a userId query parameter and uses it directly as the channel identifier, so any caller can act on behalf of any user by supplying that user's identifier.

Three distinct abuses follow from the design. Calling /sse/subscribe with a target userId opens a live stream carrying that user's workflow events, approval requests, mentions, and alerts. Calling /sse/broadcast allows an attacker to push crafted SYSTEM_HEARTBEAT messages into another user's stream, enabling notification spoofing or social-engineering payloads. Calling /sse/close terminates another user's active SSE channel, causing a denial of service against real-time notifications.

Root Cause

The root cause is twofold: an insecure filter configuration that anonymously exposes sensitive endpoints, and a trust boundary violation where server-side logic derives identity from a client-supplied userId string rather than the authenticated Shiro subject. Together, these make the endpoints impersonation-friendly by design.

Attack Vector

Exploitation requires only network reachability to the CordysCRM HTTP interface. No credentials, user interaction, or privileged position are required. An attacker enumerates or guesses valid userId values (for example, admin) and issues HTTP requests directly.

java
     }
 
 
-    /**
-     * 模拟向所有客户端广播事件-(测试使用)
-     */
-    @GetMapping("/broadcast")
-    @Operation(summary = "模拟向所有客户端广播事件-(测试使用)")
-    public String broadcast(@RequestParam String userId, @RequestParam String clientId, @RequestParam String message) {
-        sseService.sendToClient(userId, clientId, "SYSTEM_HEARTBEAT: " + message);
-        return "Broadcast: " + message;
-    }
-
     /**
      * 主动断开客户端连接
      */
// Source: https://github.com/1Panel-dev/CordysCRM/commit/6cb81deb53434ae7792673c50312ff91685d7f9d

The patch removes the /broadcast endpoint entirely and hardens client registration in SseService, refusing to attach clients when userId is blank or equals the default admin account.

Detection Methods for CVE-2026-63647

Indicators of Compromise

  • HTTP GET requests to /sse/subscribe, /sse/broadcast, or /sse/close with a userId query parameter that does not match the session's authenticated principal.
  • Long-lived text/event-stream responses served to clients that never authenticated to CordysCRM.
  • Unexpected SYSTEM_HEARTBEAT: messages appearing in user notification streams from unknown source addresses.
  • Abrupt SSE channel terminations correlated with /sse/close requests from external IPs.

Detection Strategies

  • Inspect reverse-proxy or application access logs for requests to /sse/* paths that carry no session cookie or bearer token.
  • Correlate userId values in SSE request logs against the authenticated user for the same source session and alert on mismatches.
  • Flag requests to /sse/broadcast from any source in production; the endpoint is labeled a test utility and should not receive traffic.

Monitoring Recommendations

  • Enable verbose logging on SseController and forward events to a centralized log platform for retention and correlation.
  • Baseline normal SSE subscription volume per user and alert on spikes or subscriptions to high-value accounts such as administrators.
  • Monitor for scans and enumeration probes targeting /sse/subscribe?userId= patterns.

How to Mitigate CVE-2026-63647

Immediate Actions Required

  • Upgrade CordysCRM to version 1.7.2 or later, which removes the /sse/broadcast endpoint and filters the default admin user in client registration.
  • Restrict network access to the CordysCRM management interface to trusted networks until the upgrade is completed.
  • Review notification and approval activity for the exposure window to identify any impersonated SYSTEM_HEARTBEAT messages or leaked approval requests.

Patch Information

The fix is delivered in CordysCRM 1.7.2. See the GitHub Security Advisory GHSA-9qg8-cm35-xqp4, the remediation commit 6cb81de, the pull request discussion, and the v1.7.2 release notes.

java
     public Flux<String> addClient(String userId, String clientId) {
         log.info("当前在线用户数: {} ", userClients.size());
 
-        if (StringUtils.isAnyBlank(userId, clientId)) {
+        // 过滤掉默认 admin 用户
+        if (StringUtils.isAnyBlank(userId, clientId) || "admin".equals(userId)) {
             log.info("User ID or Client ID is blank, cannot add client.");
             return null;
         }
// Source: https://github.com/1Panel-dev/CordysCRM/commit/6cb81deb53434ae7792673c50312ff91685d7f9d

Workarounds

  • Remove /sse/subscribe, /sse/broadcast, and /sse/close from ShiroFilter.addPublicPathFilters so that Shiro enforces authentication on the routes.
  • Block the /sse/broadcast path at a reverse proxy or web application firewall (WAF), since it is documented as a test-only endpoint.
  • Rewrite server-side handlers to derive userId from the authenticated Shiro subject rather than the request parameter, and reject requests where the two differ.
bash
# Example nginx block: deny anonymous access to CordysCRM SSE endpoints
location ~ ^/sse/(subscribe|broadcast|close) {
    # Block the test-only broadcast endpoint outright
    if ($uri ~* "^/sse/broadcast") { return 403; }

    # Require a session cookie for the remaining SSE endpoints
    if ($http_cookie !~* "JSESSIONID=") { return 401; }

    proxy_pass http://cordyscrm_backend;
    proxy_buffering off;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.