Detect senaite.core Eval Injection and Missing Authorization Exploitation (CVE-2026-54569) in Splunk
Detects exploitation of CVE-2026-54569, a critical (CVSS 9.8) eval injection (CWE-95) and missing authorization (CWE-862) vulnerability in senaite.core versions >= 2.0.0 through <= 2.6.0. senaite.core is a Python/Plone-based Laboratory Information Management System (LIMS). The flaw allows unauthenticated or under-privileged attackers to reach endpoints that pass attacker-controlled input into Python eval()/expression evaluation, enabling remote code execution in the context of the Zope/Plone application server. Detection focuses on suspicious HTTP requests to senaite endpoints containing Python expression payloads (e.g. __import__, os.system, subprocess) and on anomalous child processes spawned by the Zope/senaite Python process, which is the primary post-exploitation signal.
MITRE ATT&CK
- Tactic
- Initial Access Execution
SPL Detection Query
index=web (sourcetype="ms:iis:auto" OR sourcetype="access_combined" OR sourcetype="nginx:plus:access")
| eval decoded_uri=urldecode(uri_query)
| where (like(uri_path,"%/senaite%") OR like(uri_path,"%/@@%") OR like(uri_path,"%/portal_setup%") OR like(uri_path,"%/bika_setup%"))
| where match(decoded_uri,"(?i)(__import__|os\.system|subprocess|eval\(|exec\(|compile\(|popen|getattr\(|marshal\.loads|base64\.b64decode)") OR match(uri_path,"(?i)(__import__|os\.system|subprocess|eval\()")
| stats count min(_time) as first_seen max(_time) as last_seen values(uri_path) as paths values(status) as statuses by src_ip http_user_agent host
| sort - count Splunk search for senaite.core endpoints receiving requests containing Python expression injection markers.
Data Sources
Required Sourcetypes
False Positives & Tuning
- Legitimate senaite administrators editing TAL/Python expressions via the Plone TTW interface
- Authorized vulnerability scanners and penetration testing traffic
- Content migration or API automation legitimately referencing Python module names
Other platforms for CVE-2026-54569
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Simulated eval-injection probe against senaite endpoint
Expected signal: Web server access log entry to a /senaite endpoint whose query contains __import__ and os.system.
- Test 2Suspicious child process from Zope/python server
Expected signal: ProcessRollup2/process-start event with ParentBaseFileName python3 spawning /bin/sh.
- Test 3Missing-authorization access to protected senaite endpoint
Expected signal: Web access log entry to a privileged senaite management path from an unauthenticated source.
References (6)
- https://github.com/senaite/senaite.core/security/advisories/GHSA-jrw6-7x4q-w25j
- https://github.com/senaite/senaite.core/pull/2903
- https://github.com/senaite/senaite.core/pull/2919
- https://github.com/senaite/senaite.core/commit/a24d65e99a17ac43c5374ed9f0a60d0fe60d2f74
- https://github.com/senaite/senaite.core/commit/ef4b6d73575b0fbc0edc6114e5e025089aaf9eb7
- https://github.com/advisories/GHSA-jrw6-7x4q-w25j
Response Playbook
Triage
- Confirm the target host runs senaite.core and identify its version; versions >= 2.0.0 through <= 2.6.0 are vulnerable to CVE-2026-54569.
- Extract the full decoded URL/query and request body from the flagged web request and confirm whether it contains a valid Python expression payload (e.g. __import__, os.system, subprocess) rather than a benign string.
- Correlate the source IP against threat intelligence and check whether the same IP made prior reconnaissance requests or hit missing-authorization endpoints without credentials.
- Review the HTTP response status code — a 200 with an unusual body size may indicate successful code execution versus a 4xx/5xx indicating a blocked attempt.
Containment
- Block the offending source IP(s) at the WAF/perimeter and rate-limit or restrict access to the senaite management and expression-evaluation endpoints.
- If active exploitation is confirmed, isolate the senaite/Zope application server from the network to prevent lateral movement and data exfiltration of lab records.
- Apply the vendor fix by upgrading senaite.core beyond 2.6.0 (per PRs #2903 and #2919) or apply the patch commits, then restart the Zope instance.
Evidence Collection
- Preserve web server access logs (IIS/nginx/Apache) and Zope event/access logs covering the request window, including full URLs and request bodies.
- Capture the process tree and command lines of any child processes spawned by the python/zope process, along with EDR process telemetry and memory if RCE is suspected.
- Snapshot the senaite/Plone datastore (Data.fs) and any files written to disk in the app directory or /tmp during the incident window.
Escalation Criteria
- !Escalate to incident response immediately if a suspicious child process (shell, curl, wget, nc, python) was spawned by the Zope/python server following the injection request.
- !Escalate if the flagged request returned HTTP 200 and was followed by outbound connections to unknown external hosts, indicating successful RCE and possible C2.
- !Escalate if lab result/patient data access or exfiltration is observed, given senaite is a LIMS handling sensitive laboratory records.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Web server access logs showing requests to senaite endpoints with Python expression payloads. - >
Zope event.log / access.log entries and any Python tracebacks around the request time. - >
Child process command lines and files created by the python/zope process (e.g. dropped scripts in /tmp or the instance directory).
Tuning Guidance
Baseline legitimate administrator use of the Plone through-the-web (TTW) TAL/Python expression editor, which can legitimately contain expression-like syntax, and exclude known admin source IPs and authenticated admin sessions. Restrict the query to internet-facing senaite hosts, and where possible require the missing-authorization signal (no valid session/credential) to fire alongside the payload marker to reduce false positives. Add authorized scanner IP ranges to an allowlist.
Hunting Queries
Hunt for repeated eval-injection probing against senaite endpoints across the environment.
W3CIISLog | extend d = url_decode(csUriQuery) | where d has_any("__import__","os.system","subprocess","eval(") | summarize count() by cIP, csUriStem, bin(TimeGenerated, 1h) index=web (senaite OR bika_setup) | eval d=urldecode(uri_query) | where match(d,"(?i)(__import__|os\.system|subprocess|eval\()") | stats count by src_ip, uri_path Atomic Red Team Tests
Sends an HTTP request to a lab senaite instance with a Python expression payload in the query string to validate web-log detection.
Command
curl -s -o /dev/null -w '%{http_code}' "http://senaite-lab.local:8080/senaite/@@evaluate?expr=__import__('os').system('id')" Cleanup
rm -f /tmp/senaite_probe.log Expected Telemetry
Web server access log entry to a /senaite endpoint whose query contains __import__ and os.system.
Expected Detection
The kql/spl web-log queries flag the request as an eval-injection probe.
Simulates post-exploitation by having a python parent spawn a shell command, mimicking RCE via the eval injection.
Command
python3 -c "import subprocess; subprocess.call(['/bin/sh','-c','id; hostname'])" Cleanup
true Expected Telemetry
ProcessRollup2/process-start event with ParentBaseFileName python3 spawning /bin/sh.
Expected Detection
The crowdstrike_cql and elastic_eql process/sequence queries detect the anomalous child process.
Sends an unauthenticated request to a senaite endpoint that should require authorization, validating detection of the CWE-862 component.
Command
curl -s -o /dev/null -w '%{http_code}' "http://senaite-lab.local:8080/senaite/bika_setup/manage_main" Cleanup
true Expected Telemetry
Web access log entry to a privileged senaite management path from an unauthenticated source.
Expected Detection
Web-log queries surface unauthenticated access to protected senaite management endpoints.