SENAITE.CORE
Nicht authentifizierte RCE über RecordsField-Eval der JSON API
Die update-Route der SENAITE JSON API lässt die AccessJSONAPI-Berechtigungsprüfung aus, die ihre Schwester-Route create durchsetzt, und ist dadurch auf einer Standard-Plone-Site für anonyme Aufrufer erreichbar. Sobald ein Zielobjekt aufgelöst ist, führt set_fields_from_request eval() auf dem rohen Anfragewert jedes RecordsField oder RecordField aus, noch vor der Berechtigungsprüfung des Feld-Mutators. Eine anonyme Zwei-Anfragen-Kette führt beliebigen Python-Code im Zope-Worker-Prozess aus.
Beschreibung
Schwachstelle 1: fehlende Autorisierung auf der `update`-Route. Die @@API-View wird von plone.jsonapi.core mit permission="zope2.View" registriert, was eine Standard-Plone-Site an Anonymous vergibt. Die create-Route sichert sich mit einer expliziten AccessJSONAPI-Berechtigungsprüfung ab; update, update_many, remove, doActionFor, doActionFor_many und getusers tun dies nicht.
bika/lims/jsonapi/create.py:179-182 (die Prüfung, die update auslässt)
if not getSecurityManager().checkPermission( "senaite.core: Access JSON API", parent): raise Unauthorized("You don't have permission ...")Schwachstelle 2: die eval-Senke. Nachdem die Route das Zielobjekt aufgelöst hat, iteriert set_fields_from_request über die Anfragefelder. Für ein RecordsField oder RecordField führt sie eval() auf dem rohen Anfrage-String aus, noch bevor der Feld-Mutator und dessen write_permission-Prüfung laufen. Das eval läuft im Zope-Worker mit vollem Zugriff auf die Python-Builtins.
bika/lims/jsonapi/__init__.py:237-245
elif fieldtype in ['senaite.core.browser.fields.records.RecordsField', 'senaite.core.browser.fields.record.RecordField']: try: value = eval(value) except Exception: logger.warning( "JSONAPI: " + fieldname + ": Invalid " "JSON/Python variable") return []Die Kette. Das Objekt bika_setup ist auf einer Standard-Plone-Site anonym lesbar, stellt zwei Felder vom Typ RecordsField bereit (RejectionReasons und IDFormatting) und seine UID wird über Plones standardmäßige anonyme @@uuid-View veröffentlicht. Ein anonymer Angreifer ruft die UID unter /<site>/bika_setup/@@uuid ab und sendet sie dann per POST an /<site>/@@API/update mit einem Feldwert, der die eval-Senke erreicht. Es werden weder Authorization, Cookie noch ein CSRF-Token benötigt.
Die update-Route kapselt den Schreibvorgang in einem Transaction-Savepoint, sodass die ZODB-Änderungen zurückgerollt werden, wenn der Mutator später am unerwarteten Wert scheitert. Die Python-Nebenwirkungen des eval (Ausführung von Subprozessen, ausgehende Netzwerkaufrufe, Datei-I/O außerhalb der ZODB) sind bereits erfolgt und werden nicht rückgängig gemacht; die gescheiterte Transaktion hebt die Codeausführung also nicht auf.
Auswirkung
- Nicht authentifiziertes Ausführen von beliebigem Programmcode: Eine einzige anonyme HTTP-Anfrage führt beliebigen Python-Code im Zope-Worker-Prozess aus, mit vollem Lese- und Schreibzugriff auf die ZODB (Labordaten, in klinischen Deployments auch Patientendaten), Dateisystemzugriff auf das Datenvolume des Containers und ausgehendem Netzwerkverkehr. Der Schreibzugriff umfasst das unbemerkte Verändern oder Löschen einzelner Analyseergebnisse, auf die sich behandelnde Ärztinnen und Ärzte verlassen.
- Persistenz: Die Codeausführung ermöglicht direkten Zugriff auf
acl_users(den Plone-PAS-Benutzerordner) zum Anlegen von Administrator-Konten, und das ZMI unter/manageist im Upstream-Docker-Image exponiert. - Das Zurückrollen der Transaktion ist keine Mitigation: Der gescheiterte Mutator macht die ZODB-Schreibvorgänge rückgängig, aber die Nebenwirkungen des eval (Subprozess, Netzwerk, Datei-I/O) wurden bereits ausgeführt.
Abhilfe
Zum Zeitpunkt der Veröffentlichung existiert kein Patch, daher müssen beide Grundursachen auf Deployment- oder Quellcode-Ebene adressiert werden. Beschränken Sie den anonymen Zugriff auf die @@API-View (etwa durch eine Authentifizierungspflicht für /@@API/ am Reverse Proxy), bis ein Upstream-Patch verfügbar ist. Auf Quellcode-Ebene: Fügen Sie die AccessJSONAPI-Berechtigungsprüfung am Anfang jeder zustandsändernden Route in src/bika/lims/jsonapi/ hinzu (analog zur bestehenden Prüfung in create.py) und ersetzen Sie das eval() auf RecordField-/RecordsField-Werten in set_fields_from_request durch json.loads(): Die gespeicherte Struktur ist ein JSON-kompatibles Dict oder eine Liste von Dicts, sodass JSON-Parsing ausreicht und das Primitiv zur Codeausführung entfernt.
Checkliste für Betreiber
Anonymen Zugriff auf die JSON API blockieren.
Bis ein Patch verfügbar ist, fordern Sie eine Authentifizierung für
/@@API/am Reverse Proxy oder beschränken Sie den Endpunkt anderweitig auf vertrauenswürdige Netze. Die Kette benötigt keine Credentials, daher ist die Netzexposition das unmittelbare Risiko.eval() durch json.loads() auf RecordField-/RecordsField-Werten ersetzen.
Dasselbe
eval()-Muster findet sich auch in den Feld-Settern untersrc/senaite/core/browser/fields/record.pyundrecords.py. Stellen Sie alle drei aufjson.loads()mit try/except um, das bei Fehlern eine leere Struktur zurückgibt.Die AccessJSONAPI-Prüfung zu jeder zustandsändernden Route hinzufügen.
Ohne sie sind mindestens
update,update_many,remove,doActionFor,doActionFor_manyundgetusersfür Anonymous erreichbar. Prüfen Sie jedenIRouteProviderinsrc/bika/lims/jsonapi/configure.zcml.Plones CSRF-Schutz wieder aktivieren.
Das untersuchte Release liefert
ISenaiteCore(IDisableCSRFProtection), was die automatische CSRF-Erkennung vonplone.protectfür den SENAITE-Browser-Layer deaktiviert. Das Entfernen dieser Vererbung betrifft diese nicht authentifizierte Kette nicht, schließt aber mehrere authentisierte CSRF-Ketten.
Bewertung im Detail
@@API-View ist anonym erreichbar und die eval-Senke läuft für RecordsField-/RecordField-Werte bedingungslos.PR:NEs sind keine Credentials erforderlich. Die Kette funktioniert ohne Authorization, Cookie oder CSRF-Token gegen eine Standardinstallation.UI:NDer Angreifer treibt beide Anfragen selbst; es ist keine Handlung eines Betroffenen erforderlich.S:UDie Codeausführung bleibt innerhalb der Sicherheitsautorität des Zope-Workers; verwundbare Komponente und betroffene Ressourcen teilen denselben Scope.C:HBeliebiger Python-Code gewährt vollen Lesezugriff auf die ZODB, einschließlich Labordaten und, in klinischen Deployments, Patientendaten.I:HBeliebiger Python-Code gewährt vollen Schreibzugriff auf die ZODB und die Möglichkeit, über acl_users Administrator-Konten anzulegen.A:HCodeausführung im Worker-Prozess ermöglicht einen Denial of Service gegen die Anwendung und ihren Datenspeicher.Referenzen
So können wir helfen
Wer wir sind
Die Sicherheitsforscher hinter diesem Sicherheitshinweis.

Dr. rer. nat. Simon Weber
Senior Pentester & MedSec-Forscher
Ich evaluiere Ihr SaMD mit derselben branchenprägenden Sicherheitsexpertise, die ich dem BAK MV für die Überarbeitung des B3S-Standards beigetragen habe.
- Promotion über Krankenhaus-Cybersicherheit
- Kritische Schwachstellen in Krankenhaussystemen gefunden
- Alumni der THB MedSec-Forschungsgruppe
- gematik Security Hero

Dipl.-Inf. Volker Schönefeld
Senior Application Security Expert
Als ehemaliger CTO und Entwickler, der zum Pentester wurde, arbeite ich mit Ihrem Team zusammen, um Schwachstellen aufzudecken und Lösungen zu finden, die zu Ihrer Architektur passen.
- 20+ Jahre als CTO, 50+ Mio. App-Downloads
- Architektur und Absicherung großer IoT-Flotten
- Certified Web Exploitation Specialist
- gematik Security Hero
Penetrationstest gesucht?
Machine Spirits ist spezialisiert auf Sicherheitsbewertungen für Medizinprodukte und Gesundheits-IT. Von MDR-Penetrationstests bis C5-Cloud-Compliance helfen wir MedTech-Unternehmen, regulatorische Anforderungen zu erfüllen.
