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

CVE-2026-54569: SENAITE.CORE LIMS RCE Vulnerability

CVE-2026-54569 is a remote code execution vulnerability in SENAITE.CORE laboratory information management system allowing unauthenticated attackers to execute arbitrary Python code. This article covers technical details, affected versions from 2.0.0 to 2.6.0, impact on laboratory data and systems, and mitigation strategies.

Published:

CVE-2026-54569 Overview

CVE-2026-54569 is an unauthenticated remote code execution vulnerability in SENAITE.CORE, the framework behind the SENAITE laboratory information management system (LIMS). Versions 2.0.0 through 2.6.0 expose state-changing JSON API routes that skip authorization checks and pass raw request values to Python's eval(). An anonymous attacker can chain a UUID discovery request with a crafted update call to execute arbitrary Python inside the Zope worker process. Successful exploitation can expose or modify laboratory data, files, and accounts, and disrupt LIMS availability.

Critical Impact

An unauthenticated network attacker can achieve remote code execution on the Zope application server hosting SENAITE, resulting in full compromise of laboratory data confidentiality, integrity, and availability.

Affected Products

  • SENAITE.CORE 2.0.0 through 2.6.0
  • SENAITE JSON API (/@@API/update, /@@API/update_many, /@@API/remove, /@@API/doActionFor, /@@API/doActionFor_many, /@@API/getusers)
  • Deployments embedding vulnerable bika.lims and senaite.core record field parsing code

Discovery Timeline

  • 2026-08-26 - CVE-2026-54569 published to NVD
  • 2026-08-26 - Last updated in NVD database

Technical Details for CVE-2026-54569

Vulnerability Analysis

The vulnerability is a two-stage flaw combining missing authorization with unsafe code evaluation, classified under [CWE-95] Improper Neutralization of Directives in Dynamically Evaluated Code (Eval Injection). State-changing routes defined in src/bika/lims/jsonapi/update.py, namely update, update_many, remove, doActionFor, doActionFor_many, and getusers, resolve attacker-supplied object identifiers before checking the senaite.core: Access JSON API permission. During object mutation, set_fields_from_request in src/bika/lims/jsonapi/__init__.py forwards raw request values for RecordsField and RecordField instances directly to eval(). The same unsafe pattern exists in src/senaite/core/browser/fields/record.py and src/senaite/core/browser/fields/records.py. Python evaluation occurs before write-permission checks fire, so even when a later mutator fails and ZODB rolls back the transaction, the injected code has already executed in the Zope worker.

Root Cause

The root cause is the use of eval() on unvalidated request input for RecordField and RecordsField parsing, combined with missing enforcement of the AccessJSONAPI permission on state-changing routes. The design assumed the JSON API layer would gate access, but the update endpoints never validated the permission on the resolved object.

Attack Vector

An anonymous attacker sends a GET request to @@uuid to enumerate the bika_setup object identifier. The attacker then issues a POST to /@@API/update targeting a RecordsField such as RejectionReasons, embedding a Python expression in the field value. The Zope worker evaluates the expression via eval() before any authorization check, granting arbitrary code execution in the LIMS process context.

python
# Security patch: replace unsafe eval with ast.literal_eval
# Source: https://github.com/senaite/senaite.core/commit/a24d65e99a17ac43c5374ed9f0a60d0fe60d2f74

from plone.app.textfield import RichTextValue
from Products.Archetypes.config import TOOL_NAME
from Products.CMFCore.utils import getToolByName
from senaite.core.browser.fields.parsing import parse_record_literal

from bika.lims import api
from bika.lims.utils import to_utf8
python
# Security patch: enforce AccessJSONAPI permission on state-changing routes
# Source: https://github.com/senaite/senaite.core/commit/ef4b6d73575b0fbc0edc6114e5e025089aaf9eb7

from AccessControl import Unauthorized
from AccessControl import getSecurityManager
from senaite.core.browser.fields.parsing import parse_record_literal
from senaite.core.permissions import AccessJSONAPI


def check_jsonapi_permission(obj):
    ...

Detection Methods for CVE-2026-54569

Indicators of Compromise

  • Anonymous HTTP requests to /@@uuid followed shortly by POSTs to /@@API/update or /@@API/update_many.
  • Request payloads targeting RecordsField names such as RejectionReasons that contain Python syntax, function calls, or dunder attributes like __import__.
  • Unexpected child processes spawned by the Zope worker, or outbound network connections from the LIMS host.
  • Tracebacks in Zope logs originating from bika/lims/jsonapi/__init__.pyset_fields_from_request with eval in the stack.

Detection Strategies

  • Inspect web server and reverse proxy logs for unauthenticated access to /@@API/update, /@@API/update_many, /@@API/remove, /@@API/doActionFor, /@@API/doActionFor_many, and /@@API/getusers.
  • Flag JSON API request bodies containing Python builtins, quotes around callable expressions, or references to modules like os, subprocess, or socket.
  • Correlate @@uuid discovery calls with subsequent state-changing JSON API requests from the same source IP within a short time window.

Monitoring Recommendations

  • Enable verbose logging on the Zope worker and forward bika.lims logger output to a central SIEM for anomaly analysis.
  • Monitor SENAITE host processes for unexpected python, shell, or interpreter child processes derived from the Zope parent.
  • Baseline outbound network flows from LIMS servers and alert on new destinations, especially to internet endpoints.

How to Mitigate CVE-2026-54569

Immediate Actions Required

  • Upgrade SENAITE.CORE to a fixed release that includes pull requests #2903 and #2919.
  • Restrict network access to the SENAITE JSON API so it is not reachable from untrusted networks or the public internet.
  • Rotate credentials, API tokens, and secrets stored on any SENAITE host that was exposed to unauthenticated internet traffic prior to patching.

Patch Information

The SENAITE maintainers addressed the flaw in two commits scheduled for the 2.7.0 release. Commit a24d65e replaces eval() with ast.literal_eval through a new parse_record_literal helper. Commit ef4b6d7 enforces the AccessJSONAPI permission on state-changing JSON API routes. Full details are documented in the Senaite Security Advisory GHSA-jrw6-7x4q-w25j.

Workarounds

  • Block or authenticate access to /@@API/ routes at a reverse proxy (nginx, Apache, HAProxy) until the patch is applied.
  • Deny anonymous access to @@uuid and JSON API traversers by tightening Zope role assignments for the Anonymous role.
  • Deploy a web application firewall rule that inspects JSON API request bodies for Python expression indicators such as __import__, os.system, or backtick evaluation syntax.
bash
# Example nginx configuration to require authentication on SENAITE JSON API
location ~ ^/@@API/ {
    auth_basic "SENAITE JSON API";
    auth_basic_user_file /etc/nginx/senaite.htpasswd;
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://senaite_backend;
}

location = /@@uuid {
    allow 10.0.0.0/8;
    deny all;
    proxy_pass http://senaite_backend;
}

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.