Blog / CVE-Erklärungen

CVE-2026-85097: Bricksforge vertraut Ihrem Dateinamen

CVE-2026-85097 lässt anonyme Besucher über ein Bricksforge-Formular eine PHP-Shell ablegen. So entsteht der Vertrauensbruch, und so prüfen Sie Ihre Uploads.

Promotionsstudium in Informationssicherheit abgeschlossenUniversitäre Verteidigung bestanden · Abschließende Dissertationsverteidigung steht noch aus

Ein POST an ein Formular, und der Server führt Ihren Code aus

Ein POST an ein Formular. So kurz ist der Weg von einem anonymen Besucher zu Code, der auf Ihrem Server läuft. Die Anfrage unten ist der ganze Fehler, und sie wird bereits gegen Live-Sites gefeuert. CVE-2026-85097 ist ein unauthentifizierter beliebiger Datei-Upload in Bricksforge, einem beliebten Add-on für den Bricks-Builder. Betroffen sind alle Versionen bis einschließlich 3.1.8.9, behoben ab 3.1.8.10. [1][2] Wordfence, das die CVE vergeben hat, bewertet sie mit 9.8. Patchstack, das die Ausnutzung bemerkt hat, nennt sie eine 10.0. [1][2]

Bricksforge wurde am 3. September benachrichtigt, die Details wurden am 7. Oktober öffentlich. [2] Patchstack sah den ersten Ausnutzungsversuch am selben Tag, um 21:47 UTC, aus automatisierter Infrastruktur, die offensichtlich gewartet hatte. [1] Ich habe das nicht gegen eine Live-Installation ausgeführt. Das Folgende beruht auf Patchstacks Analyse und dem veröffentlichten Code, dem Wordfence-Advisory, dem Changelog des Herstellers und der WordPress-Dokumentation, gelesen am 12. Oktober 2026. [1][2][3][4] Das Interessante ist nicht, dass ein Datei-Upload zu Codeausführung führte. Es ist, dass das Plugin die Datei sorgfältig prüfte und trotzdem übernommen wurde.

POST /wp-json/bricksforge/v1/form_submit HTTP/1.1
Host: victim.example
Content-Type: application/json

{
  "temporaryFileUploads": [
    {
      "field": "form-field-file",
      "file": {
        "file": "/var/www/.../uploads/bricksforge/tmp/img.gif",   // real GIF, passed the MIME check
        "url":  "https://victim.example/wp-content/uploads/shell.php", // attacker names the destination
        "type": "image/gif"
      }
    }
  ]
}

Der Server prüfte die Bytes. Der Angreifer benannte die Datei

Dies ist das Design, dem Bricksforge vertraute. Hängt ein Besucher eine Datei an, lädt das Plugin sie in einen temporären Ordner und prüft dort den MIME-Typ, korrekt, vor allem anderen. Ein GIF muss wirklich ein GIF sein, und diese Prüfung funktioniert. Dann taten die Entwickler etwas, das harmlos wirkt: Bei der endgültigen Formularübermittlung ließen sie den Browser dem Server mitteilen, welche Datei gemeint war und wo diese Datei landen soll.

Das ist der ganze Fehler, und „es validiert das url-Feld nicht“ wird ihm nicht gerecht. Das Plugin validierte eine Eigenschaft der Datei, ihren Inhalt, auf einem Feld. Dann wählte es den endgültigen Namen und die Endung aus einem anderen Feld, das der Angreifer kontrolliert und das nie erneut geprüft wird. Was es inspizierte und was es tat, waren nicht dasselbe. Die Bytes eines GIF sagen nichts darüber, ob die Datei, die Sie gleich schreiben, shell.php heißen wird, und auf einem Apache-mit-mod_php-Host entscheidet das .php am Ende, nicht der Bild-Header, ob der Server die Datei ausführt.

Ein gültiges Bild, kopiert in eine ausführbare Datei

Gehen wir die zwei Stufen durch. Zuerst der Upload. Der Angreifer fordert von einem unauthentifizierten Endpunkt einen Nonce an und lädt dann ein Polyglot hoch: eine Datei, die ein gültiges GIF ist und zugleich eine Zeile PHP enthält. Weil es ein echtes Bild ist, meldet mime_content_type image/gif, und es landet unangetastet im temporären Verzeichnis. [1] Bis hierhin hat das Plugin alles richtig gemacht. Die Datei auf der Platte ist wirklich ein Bild.

Dann wird das Formular übermittelt, und die Metadaten erledigen die Arbeit. Das übermittelte JSON trägt ein file-Objekt mit zwei entscheidenden Feldern: file, der Serverpfad des Geprüften, und url, wohin es soll. Der Code unten ist der verwundbare Pfad, verdichtet aus dem, was Patchstack veröffentlicht hat. [1] Der Zielname wird aus dem url-Feld genommen, durch basename geschickt und als Name verwendet, in den die geprüften Bytes kopiert werden.

Sehen Sie, was dieses copy() tut. Es nimmt das echte GIF aus dem temporären Verzeichnis und schreibt seine Bytes an einen Pfad, den der Angreifer benannt hat, endend auf .php, innerhalb des Web-Roots. Der Bild-Header hält die Datei als gültiges GIF; das PHP darin ist das, was der Server beim ersten Aufruf dieser URL ausführt. [1] Die MIME-Prüfung hatte nie eine Chance, denn sie beantwortete eine Frage, die niemand stellte. Sie bewies, dass die Datei ein Bild war. Sie bewies nie, dass die Datei als Bild gespeichert oder ausgeliefert würde.

// Bricksforge Pro Forms (condensed from the vulnerable path)
$file_path = $file_data['file'];   // server path of the file that was MIME-checked
$file_url  = $file_data['url'];    // client-supplied, never re-validated

$attachment['guid'] = $file_url;                       // destination comes from the URL
$desired_filename   = basename($attachment['guid']);   // ...so from the attacker
$destination_path   = $upload_path . '/' . $desired_filename;

copy($file_path, $destination_path);   // GIF bytes written to an attacker-named .php

Warum „unsere Formulare haben kein Upload-Feld“ Sie nicht rettet

Nun der Teil, der Sie beunruhigen sollte, selbst wenn Sie sich nicht für exponiert halten. Sie könnten annehmen, das betreffe nur Formulare, die Uploads annehmen. Bricksforges eigenes Changelog sagt etwas anderes: Der Fehler betrifft jede Site mit aktiven Pro Forms, auch Formulare ganz ohne Upload-Felder. [3] Der Grund steht im Code. Die Liste der zu verarbeitenden Dateien wird nicht aus einem echten Multipart-Upload gelesen. Sie wird aus dem Wert temporaryFileUploads im JSON-Body gelesen, den der Angreifer von Grund auf schreibt. [1] Ein einfaches Textkontaktformular genügt.

Und der Nonce ist nicht das Tor, nach dem er aussieht. Bricksforge stellt einen Endpunkt bereit, bricksforge_regenerate_nonce, der jedem anonymen Besucher einen frischen Nonce aushändigt, was den Zweck schon zunichtemacht. Selbst ohne ihn erzeugt WordPress für jeden abgemeldeten Nutzer denselben Nonce, denn sie teilen alle die user ID 0. [4] Ein Nonce beweist, dass eine Anfrage von einer Seite kam, nicht von einer vertrauenswürdigen Person. Die WordPress-Dokumentation sagt es in einer Zeile: Auf Nonces darf man sich nie für Authentifizierung, Autorisierung oder Zugriffskontrolle verlassen. [4] Dieser Handler stützte sich für genau das auf einen.

// The file list is read from the JSON body, not from a real multipart upload:
$file_uploads = isset($form_data['temporaryFileUploads'])
    ? json_decode($form_data['temporaryFileUploads'], true)   // attacker-supplied
    : [];
$merged_files = array_merge($file_uploads, $request->get_file_params());

// The gate in front of it:
//   GET bricksforge_regenerate_nonce  -> hands any anonymous visitor a nonce
//   WordPress gives every logged-out user the SAME nonce (user ID 0)
// A nonce is CSRF hygiene. It was never authentication.

Der Fix gibt den Dateinamen an den Server zurück

Der Fix stellt die offensichtliche Regel wieder her: Wohin eine hochgeladene Datei kommt und wie sie heißt, entscheidet der Server, nicht der Client. Patchstack und das Wordfence-Advisory nennen 3.1.8.10 als korrigierten Build. [1][2] Bricksforges Changelog führt den Datei-Upload-Fix unter 4.0.1, veröffentlicht am 8. Oktober, und sagt, volle Details folgten nach der Offenlegung. [3] Aktualisieren Sie auf das, was Ihre Lizenz hergibt, aber behandeln Sie 3.1.8.10 als Untergrenze.

Das Prinzip darunter überlebt dieses Plugin. Ein gespeicherter Dateiname ist eine Sicherheitsentscheidung, also muss der Server ihn besitzen: einen zufälligen Namen erzeugen, die Endung per Allowlist festlegen und außerhalb des Web-Roots speichern oder dort, wo der Server kein PHP ausführt. Der Block unten ist diese Form, nicht der Diff des Herstellers. Es gibt zudem eine Verteilungsfalle, die Bricksforge eigen ist. Es ist ein kommerzielles Plugin, nicht auf WordPress.org, aktualisiert über eine Lizenz. [3] Eine abgelaufene Lizenz kann Sie stillschweigend auf einem verwundbaren Build lassen, während Ihr Dashboard in Ordnung aussieht. „Wir haben es einmal gekauft“ ist nicht „wir sind gepatcht“.

// The fix, as a principle: the SERVER owns the name and extension, never the client.
$ext         = 'bin';                             // or an allowlisted image extension
$stored      = wp_generate_uuid4() . '.' . $ext;  // random name the attacker can't choose
$destination = $upload_path . '/' . $stored;
copy($validated_tmp_file, $destination);

// And decide execution by WRITE location, not by MIME: store outside the web root,
// or in a directory where the server is configured never to run PHP.

Wie ich das in Code finde, der nichts mit Bricksforge zu tun hat

Dieser Fehler ist eine Klasse, und die Klasse ist älter als WordPress: eine Eigenschaft validieren, nach einer anderen handeln. Sobald ein Upload-Handler den Inhalt einer Datei prüft, aber Request-Eingaben den Namen, die Endung oder das Verzeichnis der Datei wählen lässt, haben Sie dieselbe Lücke, ob der Stack PHP, Node oder Java ist. Wenn ich einen Upload-Pfad prüfe, beginne ich nicht bei der MIME-Prüfung, auf die alle stolz sind. Ich beginne beim Schreiben.

Ich finde jeden Sink, der Bytes auf die Platte schreibt, und verfolge rückwärts, woher der Zielname kommt. Das grep unten liefert mir die Kandidaten; die eigentliche Arbeit ist, jeden zu lesen. Für jeden Schreibvorgang stelle ich eine einzige Frage, dieselbe, die CVE-2026-85097 im Review gefangen hätte: Entscheidet die Eigenschaft, die Sie validiert haben, über die Eigenschaft, die ausgeführt wird? Sind die Bytes geprüft, aber ein separates, clientkontrolliertes Feld benennt die Datei, ist die Prüfung Dekoration.

# Smell to hunt in any upload handler:
# the stored filename, extension or directory derived from request input.
rg -n "basename\(|get_param|\$_POST|\$_REQUEST|json_decode" --glob '*.php'

# Then read each write sink by hand: move_uploaded_file, copy, file_put_contents, rename.
# The one question that confirms the bug:
#   does the property you VALIDATED (the bytes, the MIME) decide the property
#   that gets EXECUTED (the extension, the directory)?
# If a separate, client-controlled field names the sink, the check is decoration.

Ist das Ihr Feuer? Nur wenn Sie Bricksforge betreiben — dann aber ja

Ehrlich zum Ausmaß. Bricksforge hat nach Patchstacks Zählung rund 6.000 aktive Installationen, das ist also kein internetweites Ereignis. [1] Betreiben Sie kein Bricksforge, können Sie diesen Tab schließen. Tun Sie es, ist das Fenster nicht theoretisch. Patchstack protokollierte Ausnutzung von 63 eindeutigen Quell-IPs, beginnend binnen Stunden nach der Offenlegung, wobei 95% der bösartigen Anfragen eine PHP-Nutzlast als GIF oder PNG tarnten und kleine Cluster Dutzende Schreibweisen der Endung durchprobierten, um naive Filter zu schlagen. [1]

Die Umgehung lohnt den Blick, denn sie sagt Ihnen, wonach Sie greppen müssen. Angreifer versuchten alternative PHP-Endungen wie .php7 und .phtml, Groß-/Kleinschreibungs-Tricks wie .PHp und URL-kodierte Formen wie %2ephp, alle auf dasselbe copy() gerichtet. [1] Die Dateien, die sie ablegen wollten, folgten Mustern wie login_admin_*.php im Upload-Verzeichnis, einschließlich des temporären Bricksforge-Ordners. [1] Stand 12. Oktober ist die CVE noch nicht im Known-Exploited-Vulnerabilities-Katalog der CISA, und ihr EPSS-Wert liegt unter einem Prozent, doch beides bedeutet nicht Ruhe. [2] Es bedeutet früh. KEV ist ein nachlaufender Indikator; Honeypots sind ein vorlaufender, und die Honeypots sind bereits laut.

Die drei Bedingungen, damit das Ihr Notfall wird: Sie betreiben Bricksforge, in einer Version unter 3.1.8.10, aus dem Internet erreichbar. Treffen alle drei zu, ist Patchen Schritt eins, nicht die Ziellinie, denn eine PHP-Datei, die geschrieben werden konnte, wurde vielleicht schon geschrieben und ausgeführt. Für einen Site-Betreiber lautet die Frage nicht nur „haben wir aktualisiert“, sondern „hat jemand vor uns den form_submit-Endpunkt mit einer Datei-Nutzlast erreicht“. Diese Antwort liegt in Ihren Logs und Ihrem Upload-Verzeichnis, nicht im Plugin-Bildschirm.

Was Sie tun sollten, bevor Sie diesen Tab schließen

Dies ist die Reihenfolge, in der ich arbeiten würde. Zuerst patchen, denn das Ausnutzungsfenster misst sich in Stunden, nicht in Wochen. [1] War aber eine verwundbare Version dem Internet zugewandt, nehmen Sie an, dass jemand es versucht hat, und jagen Sie, bevor Sie den Sieg erklären. Ein Patch stoppt den nächsten Upload; gegen eine Shell, die letzte Woche abgelegt wurde, tut er nichts.

Die Liste unten ist konkret. Die Log-Signatur, nach der Sie suchen, ist die, die der Angriff nicht vermeiden kann: eine Anfrage an den Formular-Endpunkt mit einem temporaryFileUploads-Body, bei dem der Dateipfad auf ein Bild zeigt, das url-Feld aber auf eine PHP-Endung endet. Diese Diskrepanz ist der Fingerabdruck des Fehlers und lässt sich schwer unschuldig fälschen.

Patch and verify (in this order):
  [ ] Find every site running Bricksforge; confirm the version actually LOADED.
  [ ] Update to 3.1.8.10+ (3.x branch) or 4.0.1+. Bricksforge is license-gated and
      not on WordPress.org, so a lapsed license can silently block the update.
  [ ] Cannot update now? Block at the WAF / server: POST to
      /wp-json/bricksforge/v1/form_submit and the admin-ajax action
      bricksforge_form_submit carrying a temporaryFileUploads body.

If a vulnerable version faced the internet, assume compromise and hunt:
  [ ] Grep uploads for unexpected .php, especially
      /wp-content/uploads/bricksforge/tmp/ and names like login_admin_*.php.
  [ ] Audit for hidden admin accounts by querying the database directly,
      and review the must-use (mu-plugins) directory.
  [ ] In access logs, look for form_submit requests whose file path is an image
      but whose url field ends in a PHP extension.
  [ ] Rotate secrets reachable from PHP: database, API keys, SMTP.

Der Teil, den „wir validieren Uploads“ nie abdeckt

CVE-2026-85097 ist eine saubere Lektion darüber, warum „wir validieren Uploads“ keine Antwort auf „sind Uploads sicher“ ist. Das Plugin validierte das eine, das leicht zu validieren war, und vertraute dem einen, das zählte. Diese Lücke, zwischen der Eigenschaft, die Sie prüfen, und der Eigenschaft, die ausgeführt wird, ist der Ort, an dem überraschend viele Upload-Fehler leben, und Sie sehen sie nicht an einer Versionsnummer oder am grünen Haken eines Scanners. Sie müssen den Pfad von der Anfrage bis zum Schreiben lesen.

Diesen Pfad zu verfolgen, von einer anonymen Anfrage durch das Framework bis zur genauen Zeile, die eine Datei auf der Platte benennt, ist der Kern eines Web-Application-Assessments, und den „eins validieren, nach anderem handeln“-Geruch aus dieser CVE suche ich in Code, der nichts mit WordPress zu tun hat. Wenn Sie Formulare, Uploads oder irgendeinen Fluss betreiben, der externe Eingaben in Dateien auf Ihrem Server verwandelt, ist das die Grenze, die ich für Sie prüfen würde. Sagen Sie mir, was Sie ausliefern, und wir legen den Umfang fest.

Quellen

Soll das auch in Ihrem Produkt geprüft werden?

Web- & API-Pentest
Alle Artikel

08Kontakt

Sie bringen etwas mit Nutzerdaten heraus?

Senden Sie mir eine kurze Beschreibung. Ich antworte innerhalb von drei Tagen mit Rückfragen oder einem Angebotsentwurf; den Preis vereinbaren wir vor Beginn schriftlich.