Threat intelligence, Threat Research

kotaemon check_connection Unauthenticated Deserialization RCE

by Security News

kotaemon check_connection
Unauthenticated RCE (CVE-2026-69098)

OVERVIEW

SonicWall Capture Labs threat research team became aware of the threat CVE-2026-69098, assessed its impact, and developed mitigation measures. The flaw, also known as the kotaemon check_connection Unauthenticated Deserialization Remote Code Execution, is a critical vulnerability affecting kotaemon (Cinnamon/kotaemon), an open-source Gradio-based retrieval-augmented generation chat application, in all versions up to and including 0.12.0. It lets an unauthenticated attacker run arbitrary code on the server with a single HTTP POST to the model test-connection endpoint, because a client-supplied model specification is merged over the stored one and handed to a deserializer with its class allowlist disabled, instantiating any Python object the request names. Classified under CWE-502 (Deserialization of Untrusted Data) and rated CVSS 9.8 (Critical) under v3.1, with the CNA assigning v4.0 a score of 9.3, it was reported by HK4zCzi and assigned by VulnCheck, published on August 4, 2026. Its EPSS score is 0.91% (59.5th percentile). The severity reaches 9.8 because the optional login screen only hides interface tabs: the Gradio event API is served with no authentication, so any client that can reach the port can call the endpoint. No patched release is available, so administrators should restrict network access to the service until a fix ships.

TECHNICAL OVERVIEW

kotaemon is an open-source retrieval-augmented generation application that lets users chat with their documents through pluggable language-model, embedding, and reranking providers. It is built on the Gradio web framework, is commonly self-hosted, and exposes its interface and HTTP API on TCP 7860. Its model-management screen offers a Test connection action that validates a provider configuration before it is saved.

figure1.png
Figure 1: kotaemon check_connection unauthenticated deserialization flow from HTTP POST to code execution

The root cause is in the test-connection handler. In libs/ktem/ktem/llms/ui.py, check_connection parses the client-supplied specification as YAML, merges it over the stored model specification with a dictionary update, and passes the result to the theflow library's deserialize with safe=False. The YAML parsing itself is safe and builds only plain data; the flaw is that the merge lets a client key replace the stored __type__, and the deserializer is then invoked with its class allowlist turned off.

figure2.png
Figure 2: check_connection merges the client spec and calls deserialize with safe set to false

The deserializer treats any dictionary that carries a __type__ key as an object to construct. It imports the dotted path named in __type__, recursively deserializes the remaining keys into constructor arguments, and calls the class with them. Because the argument values are built before the outer object, a nested __type__ is instantiated first, which means a command runs during deserialization rather than after it.

figure3.png
Figure 3: deserialize imports the attacker __type__ and instantiates it with attacker-supplied arguments

The import is performed by import_dotted_string. The library ships an allowlist mode: when safe is true, it resolves names only against an explicit allowed_modules mapping and rejects anything else. kotaemon calls it with safe=False, which takes the unrestricted branch and imports any module and attribute the string names. The defense the library provides is present but not used on this path.

figure4.png
Figure 4: import_dotted_string skips its allowlist when safe is false and imports any module

No upstream fix has been released, but the correct pattern already exists in the same file. The sibling create_llm and save_llm handlers, which persist a specification instead of instantiating it immediately, overwrite __type__ with a server-chosen value after parsing the client YAML, so a client-supplied type is ignored. check_connection, the one handler that constructs the object at once, omits that step, and the same omission is repeated in the embedding and reranking test-connection handlers. Pinning __type__ server-side as the sibling handlers do, together with re-enabling the deserializer allowlist, is the remediation.

figure5.png
Figure 5: sibling create_llm pins __type__ server-side, the step check_connection omits

TRIGGERING THE VULNERABILITY

The following conditions must be met for successful exploitation of CVE-2026-69098:

  • Network-Reachable Deployment: The target must run kotaemon as a self-hosted web application so that the HTTP API is reachable. The default Gradio listener is TCP 7860, bound to all interfaces in the shipped launch configuration.
  • Vulnerable Version: The kotaemon build must be 0.12.0 or earlier. No patched release exists, so current deployments remain affected until one ships.
  • No API Authentication: The instance must expose the Gradio event API, which is served without an authentication provider. The optional user-management login, enabled by default with the credentials admin and admin, only toggles the visibility of interface tabs and does not gate the API, so no credentials are required to call the endpoint.
  • Registered Model Name: The request must name a model already present in the registry. A default install pre-registers names such as claude, openai, google, groq, cohere, and mistral, so no preparatory step is needed.
  • Client-Supplied Type Field: The YAML specification must carry a __type__ key that names an importable callable. A legitimate client never sends this key, because the interface strips it before display and the save and create handlers pin it server-side.

EXPLOITATION

Exploiting CVE-2026-69098 needs no credentials and no special tooling. The attacker sends a single HTTP POST to the test-connection endpoint with a JSON body carrying a registered model name and a specification whose __type__ field the attacker controls. The core primitive is direct: setting __type__ to a callable such as subprocess.check_output, with an argument list, runs an operating-system command while the deserializer builds the object, before the handler does anything else. The command executes with the privileges of the kotaemon process. Because the deserializer recurses into every field, the attacker's callable can also be nested inside another field's value, so the command still runs when the outer object is a stored model class that would otherwise reject extra arguments.

The publicly demonstrated proof of concept adds one optional step to make the result visible. It sets the outer __type__ to unittest.mock.Mock, a standard-library testing class repurposed as a gadget because it accepts any arguments and is callable, and nests subprocess.check_output inside the mock's return_value. The handler's connection test then calls the deserialized object and echoes what it returns, so the command's standard output comes back in the response and a blind execution becomes one whose result is read straight from the reply. The wrapper also absorbs the stored specification's leftover fields, such as the model name and API key. It is a convenience for observing output, not a requirement for code execution.

figure6.png
Figure 6: single unauthenticated POST carrying the __type__ deserialization payload

With that payload, the reply carries the text Connection success. Got response: followed by the command output, for example the result of id, so a successful attack is visible in the response itself. A payload that omits the mock still runs the command; the endpoint simply reports a failed connection and returns nothing. The same deserializer backs all three test-connection handlers, for language models, embeddings, and rerankings, so each is independently exploitable.

Video Demonstration

PAYLOAD KEY COMPONENTS
ComponentValuePurpose
Target EndpointPOST /call/check_connectionGradio event handler that instantiates the submitted model specification at once
Transportplaintext HTTP on TCP 7860Default Gradio listener, with no TLS unless explicitly configured
AuthenticationnoneThe event API is served without an authentication provider
Model Namea registered name such as claudeRequired to reach the deserializer; pre-registered on a default install
Injection Parameter__type__ in the model specificationClient key that overwrites the stored class after the merge
Execution Primitivesubprocess.check_output with argumentsInstantiated and called during deserialization; runs the command (the core RCE)
Reflection Wrapper (optional)unittest.mock.Mock in return_valuePublic PoC technique to return the command output; not required for execution
Success Oracleevent-stream Connection success. Got response:Command output echoed back in the response

SONICWALL PROTECTIONS

To ensure SonicWall customers are prepared for any exploitation that may occur due to this vulnerability, the following signature has been released:

Signature IDSignature Name
IPS: 22563kotaemon check_connection Insecure Deserialization

REMEDIATION RECOMMENDATIONS

The risks posed by CVE-2026-69098 can be mitigated or eliminated with the following measures:

  • Restrict Network Exposure: Because no fixed release exists, the primary control is reachability. Firewall TCP 7860, or the configured port, so the service is reachable only from trusted administrative hosts, and place it behind an authenticating reverse proxy with TLS rather than exposing it directly.
  • Do Not Rely on the Login Screen: The user-management login only hides interface tabs; it does not protect the HTTP API. Treat any reachable instance as unauthenticated and enforce access control in front of it.
  • Apply a Server-Side Type Pin: Until an upstream release ships, operators who run from source should mirror the create_llm and save_llm handlers inside check_connection and its embedding and reranking counterparts, overwriting __type__ with the stored value and re-enabling the deserializer allowlist by calling it with safe=True and an explicit allowed-modules mapping.
  • Rotate Exposed Provider Credentials: kotaemon stores the API keys configured for its language-model, embedding, and reranking providers. Treat the keys on any exposed instance as compromised and rotate them.
  • Monitor for Exploitation Artifacts: Inspect requests to the Gradio event endpoints for a client-supplied __type__ key inside the data payload, which a legitimate client never sends, and alert on unexpected child processes or outbound connections from the kotaemon host shortly after such a request.
  • Utilizing IPS Signatures: Apply updated signature coverage at the perimeter and on any segment that can reach the kotaemon listener on TCP 7860, and keep signature sets current.
  • Network Segmentation: Isolate application servers from sensitive internal resources and implement egress filtering to detect unauthorized outbound connections.

RELEVANT LINKS

ATTRIBUTION

The vulnerability was reported by HK4zCzi through the kotaemon project issue tracker and was assigned CVE-2026-69098 by VulnCheck as the CNA, published on August 4, 2026. No fixed release has been issued as of this writing.

Third-party vulnerability database mirrors:

Share This Article

An Article By

Security News

The SonicWall Capture Labs Threat Research Team gathers, analyzes and vets cross-vector threat information from the SonicWall Capture Threat network, consisting of global devices and resources, including more than 1 million security sensors in nearly 200 countries and territories. The research team identifies, analyzes, and mitigates critical vulnerabilities and malware daily through in-depth research, which drives protection for all SonicWall customers. In addition to safeguarding networks globally, the research team supports the larger threat intelligence community by releasing weekly deep technical analyses of the most critical threats to small businesses, providing critical knowledge that defenders need to protect their networks.

Related Articles

  • Adobe Commerce and Magento StyleSmuggler Unauthenticated RCE
    Read More
  • PaperCut NG/MF Unauthenticated Remote Code Execution
    Read More