Opens in a new tab
The Attack Path Tour
Horizon3 website preview featuring NodeZero autonomous security validation

Zammad CVE-2026-102489: Session Leak to RCE

Zach Hanley
October 7, 2026

Introduction

On 24 September 2026, the Dutch Institute for Vulnerability Disclosure (DIVD) announced publicly that they had suffered a security incident and had attributed it to an “agentic AI powered attack” given the forensic artifacts they discovered during the incident response. They attributed the source of the initial breach to Zammad, a helpdesk and ticketing application.

During their investigation, they determined that two distinct 0-day vulnerabilities were abused:

  1. CVE-2026-102489 – A session hijack that ultimately allows executing code as the zammad user
  2. CVE-2026-102490 – A privilege escalation that allows escalating to root

Soon after, DIVD released further details of the Zammad vulnerabilities and released a Indicators of Compromise script which looked for leaked session cookies in error logs.

CVE-2026-102489: Zammad Session Leak

Given the above details, it was classified as a session hijack and that session cookies would appear in the logs, so we dug in.

We prompted a vulnerability research harness, with Anthropic’s Opus 4.8 as the model, to attempt to identify and reproduce the issues given:

  1. The DIVD CSIRT cases
  2. The DIVD IOC script
  3. The Zammad open-source GitHub repository

After a couple hours, the results were back and the harness did confirm that if there was a way to trigger an error it would leak the session cookie strings, but it was unable to find an unauthenticated trigger request.

Fuzzing The Code

Re-prompting the agent, we advised it to build fuzzing tooling to target the areas of code it suspected most likely to be associated with the vulnerable area of code. Several minutes later into a fuzzing campaign against the websocket code, it triggered an error that was reflected back to the requestor which contained the session cookies we were looking for.

The vulnerability trigger is a simple single request to the WebSocket endpoint, /ws, with a payload of {“event”: “base”}.

Code sending a base event to the Zammad WebSocket endpoint to trigger CVE-2026-102489
Figure 1. Triggering the leak

The server then reflects this error back to the client, which leaks the cookies for all active users.

Proof-of-concept exploit showing Zammad session cookies leaked through the WebSocket vulnerability
Figure 2. Example exploit request

Understanding the Source

Starting from the architectural perspective, Zammad is a Ruby application that utilizes websockets. It stores all global connection states in the @clients class-level instance variable. The headers variable notably includes the session Cookie header.

Zammad websocket_server.rb code storing client headers in the global clients variable
Figure 3. websocket_server.rb storing headers

Every event that occurs in the framework, the @clients variable is passed with it. There is no event type or client based filtering.

Zammad websocket_server.rb code passing client data and headers into the session event handler
Figure 4. websocket_server.rb event dispatching

The base event handler for the session object takes every parameter passed to it and unpacks each key-value pair stored in it. Which means the sensitive cookies are stored inside the headers key of the clients variable.

Zammad sessions event base handler assigning supplied parameters as instance variables
Figure 5. sessions/event/base.rb handler

Lastly, in sessions/event.rb, the run() method returns the entire event’s instance variables in a response to the client when an error occurs. 

Zammad sessions event code returning an error message containing event instance variables
Figure 6. Returns event variables in response to client

When a client sends {“event”:”base”}, the dispatcher successfully resolves Sessions::Event::Base as a real Ruby constant, instantiates it with the full @clients registry mass-assigned onto the object, then fails when .run is called – as Base has no implementation. Ruby constructs the NoMethodError message to include the receiver’s full object representation, which contains every instance variable including @clients, and sessions/event.rb returns that message string directly to the caller.

Authenticated Session to Code Execution

Once an attacker has a valid admin session cookie from the leak, they can write arbitrary files into the Zammad application directory via the package installation endpoint. By writing a malicious ERB template that overrides the built-in password reset email view, they can trigger its execution by initiating a password reset for any user – causing Zammad’s mailer to render the attacker-controlled template with full Ruby execution privileges, achieving remote code execution as the zammad OS user.

Proof-of-concept exploit confirming remote code execution as the Zammad system user
Figure 7. Exploit in action

Our proof of concept exploit can be found here: https://github.com/horizon3ai/CVE-2026-102489

Given that the privilege escalation vector we believe to be CVE-2026-102490 remains unpatched, we will be withholding those details.

References

How can NodeZero help you?
Let our experts walk you through a demonstration of NodeZero®, so you can see how to put it to work for your organization.
Get a Demo
Share: