
Penpots Fernbildimport ermöglichte es einem authentifizierten Dateibearbeiter, eine normale Medienkomfortfunktion in ein SSRF mit Backend-Ursprung zu verwandeln, da vom Angreifer kontrollierte URLs in einen Redirect-folgenden Server-Fetch-Pfad ohne Ziel-Filterung gelangten.
Penpots Remote-Bildimport erlaubte einem authentifizierten Datei-Editor, eine normale Medien-Komfortfunktion in einen Backend-originierten SSRF zu verwandeln, da vom Angreifer kontrollierte URLs einen Weiterleitungen folgenden Server-Abrufpfad ohne Ziel-Filterung erreichten.
Ich habe diesen Fehler beim Überprüfen von Penpot, der quelloffenen Design- und Code-Kollaborationsplattform, mit einer sehr spezifischen Frage gefunden:
Was passiert, wenn ein kollaboratives Designtool einem Benutzer erlaubt, dem Backend eine Remote-Bild-URL zu übergeben, um sie abzurufen?
In diesem Fall führte diese Frage zu einem echten Bug.
Der Remote-Bildimport-Ablauf von Penpot akzeptierte eine benutzergesteuerte URL und veranlasste das Backend, sie aus dem Server-Netzwerkkontext abzurufen, ohne Einschränkungen für Loopback- oder private Netzwerkziele durchzusetzen. Der gemeinsam genutzte HTTP-Client folgte auch automatisch Weiterleitungen.
Dadurch wurde eine normale Medien-Komfortfunktion in eine authentifizierte Backend-originierte SSRF-Grundlage verwandelt und führte schließlich zu CVE-2026-45806.
Penpot: Penpot auf GitHub
CVE: CVE-2026-45806
CVSS: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Dies betraf Penpot. Auf seiner offiziellen Website und im Medienkit präsentiert sich Penpot als mit einer +1 Millionen wachsenden Benutzerbasis und sagt, dass Zehntausende von Organisationen es nutzen, darunter Blender, Mozilla, Fedora, NTT Data, , , , , und .

authentifizierter Datei-Editor -> vom Angreifer kontrollierte Remote-Bild-URL -> create-file-media-object-from-url -> Backend download-image Abruf mit aktivierten Weiterleitungen -> finale Anfrage landet auf internem Nur-Bild-Endpunkt -> Backend-origin SSRF / interne Erreichbarkeit
Penpot ist eine quelloffene Design- und Code-Kollaborationsplattform.
Es behandelt Dinge wie:
Das bedeutet, dass sein Medienimportpfad auf einer echten Vertrauensgrenze liegt.
Die wichtige Frage war hier nicht, ob Penpot das Importieren von Remote-Bildern unterstützt.
Die eigentliche Frage war:
Schränkt Penpot ein, wohin das Backend eine Verbindung herstellen darf, wenn ein Benutzer ein Remote-Bild importiert?
In diesem Fall tat es das nicht.
Viele Leute unterschätzen Remote-Importfunktionen.
Das ist ein Fehler.
In dem Moment, in dem eine Anwendung:
schafft sie eine echte ausgehende Vertrauensgrenze.
Das war hier das Problem.
Dieser Fehler lag nicht im Bild-Rendering. Er lag nicht in der Dateispeicherung. Er lag nicht in den üblichen Berechtigungsprüfungen zum Bearbeiten einer Datei.
Es war ein klassischer serverseitiger Vertrauensfehler:
Das reicht aus, um eine echte Sicherheitslücke zu schaffen.
Ich bin nicht blind an Penpot herangegangen, indem ich zuerst zufällige RPC-Methoden gefuzzt oder nach Abstürzen gesucht habe.
Der stärkere Ansatz war, die vielversprechendste Sicherheitsgrenze zu identifizieren.
Für Penpot war das der Remote-Medienimport.
Warum?
Weil diese Funktion kombiniert:
Das war die richtige Grenze zu überprüfen.
Und genau dort lebte der Fehler.
Der Fehler reduziert sich auf eine kleine Vertrauenskette.
Im Frontend:
(defn upload-media-url
[name file-id url]
(rp/cmd!
:create-file-media-object-from-url
{:name name
:file-id file-id
:url url
:is-local true}))
die vom Benutzer kontrollierte url wird direkt in den RPC-Aufruf gesendet.
Dann im Backend:
(sv/defmethod ::create-file-media-object-from-url
...
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(files/check-edition-permissions! pool profile-id file-id)
...
(let [_ (files/get-minimal-file cfg file-id)
mobj (create-file-media-object-from-url cfg (assoc params :profile-id profile-id))])
und:
(defn- create-file-media-object-from-url
[cfg {:keys [url name] :as params}]
(let [content (media/download-image cfg url)
das Backend überprüft, ob der Aufrufer die Zieldatei bearbeiten darf, und übergibt dann die vom Angreifer kontrollierte URL an media/download-image.
Die Abruf-Implementierung ist hier:
(defn download-image
"Download an image from the provided URI and return the media input object"
[{:keys [::http/client]} uri]
...
(http/req! client
{:method :get :uri uri}
{:response-type :input-stream})
Und der gemeinsam genutzte HTTP-Client ist konfiguriert als:
(http/build-client {:connect-timeout 30000
:follow-redirects :always}))
Das ist die gesamte Schwachstelle:
Weil der Angreifer nur benötigt:
Die Angriffskette ist unkompliziert:
Das ist der gesamte Fehler.
Der wichtige Unterschied ist wo die Anfrage stattfindet.
Die Frage ist nicht:
„Kann Penpot Bilder von URLs importieren?"
Die eigentliche Frage ist:
„Kann ein authentifizierter Benutzer das Penpot-Backend dazu bringen, eine Verbindung zu internen Zielen herzustellen, die der Benutzer nicht über die Anwendung erreichen können sollte?"
In diesem Fall war die Antwort ja.
Das ist wichtig, weil es einen echten Unterschied gibt zwischen:
Bildvalidierung hebt diesen Unterschied nicht auf.
Sie schränkt einige direkte Exfiltrationsfälle ein, hebt aber nicht die SSRF-Bedingung oder den Netzwerkgrenzenbruch auf.
Ich habe dieses Problem mit einem kontrollierten lokalen Proof validiert, der direkt mit dem überprüften Penpot-Codepfad verbunden ist.
Das Ziel war nicht, die Infrastruktur Dritter zu treffen. Das Ziel war, die genaue Sicherheitseigenschaft zu beweisen:
Ich habe einen eigenständigen Java-Validator gebaut, der das relevante Verhalten nachbildet:
content-type und content-lengthIch habe zwei Fälle validiert.
Der Validator forderte:
http://127.0.0.1:7790/internal.png
Beobachtetes Ergebnis:
http://127.0.0.1:7790/internal.pnghttp://127.0.0.1:7790/internal.png200image/pngDas bewies, dass die importartige Abruflogik einen internen Nur-Bild-Endpunkt direkt akzeptierte.
Der Validator forderte dann:
http://localhost:7791/redirect-to-internal
Dieser Endpunkt gab eine HTTP-Weiterleitung zurück zu:
http://127.0.0.1:7790/internal.png
Beobachtetes Ergebnis:
http://localhost:7791/redirect-to-internalhttp://127.0.0.1:7790/internal.png200image/pngDer Nur-interne Listener protokollierte die weitergeleitete Anfrage.
Das bewies die wichtigere Behauptung:
Die Nutzlast war hier absichtlich einfach:
Das war wichtig, weil Penput nicht einfach beliebige Bytes abruft und aufhört. Es führt nach der Anfrage eine medienorientierte Validierung durch.
Der richtige Beweis war also nicht:
„Das Backend kann versuchen, sich irgendwo zu verbinden"
Der stärkere Beweis war:
„Das Backend kann dazu gebracht werden, sich mit etwas internem zu verbinden und die Anfrage erfolgreich unter denselben bildähnlichen Einschränkungen abzuschließen, die die Funktion erwartet"
Genau das hat die Validierung gezeigt.
Eine häufige Reaktion auf SSRF-Fehler dieser Art ist:
„Das Ziel muss trotzdem ein Bild zurückgeben"
Diese Beobachtung ist wahr, aber unvollständig.
Sie hebt die Schwachstelle nicht auf.
Sie sagt dir nur, welche internen Ziele am direktesten nützlich sind.
Dieses Problem ermöglicht immer noch:
Das ist immer noch ein echter Bruch der Sicherheitsgrenze.
Besonders in selbstgehosteten Umgebungen existieren interne Dienste oft genau hinter dieser Grenze.
Diesem Problem wurde letztendlich ein Hoher Schweregrad (CVSS) zugewiesen:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
Diese Klassifizierung ergibt Sinn.
Die Behauptung ist nicht, dass ein nicht authentifizierter Angreifer sofort jede Penpot-Installation aus dem Nichts kompromittieren kann.
Die Behauptung ist, dass jeder normale authentifizierte Datei-Editor Penpot in eine Backend-Anfragegrundlage gegen interne Ziele verwandeln kann, einschließlich weiterleitungsgestütztem Zugriff auf Loopback- und Private-Netzwerk-Ziele.
Es gab während der Offenlegung einige Diskussionen über den Schweregrad, hauptsächlich zu:
Das sind angemessene Einschränkungen, die diskutiert werden sollten.
Aber sie heben das Kernproblem nicht auf:
Das ist eine echte und vertretbare SSRF-Schwachstelle.
Der wichtige Fix hier ist nicht eine strengere MIME-Behandlung.
Der eigentliche Fix ist eine ausgehende Richtlinie für Ziele.
Eine korrekte Abhilfe für diese Art von Fehler muss:
http und https zulassenlocalhostDas ist die richtige Fix-Richtung, weil dies kein Bildparsungsfehler war. Es war ein Fehler der Netzwerk-Vertrauensgrenze.
Dieses Problem wurde privat über den GitHub-Sicherheitsmeldefluss gemeldet.
Die Meldung enthielt:
Die Maintainer bestätigten das Problem und begannen mit der Arbeit an einer Lösung.
Das Problem wurde später zugewiesen:
CVE-2026-45806
Die wichtigste Lektion ist einfach:
Remote-Medienimport ist eine ausgehende Vertrauensgrenze, nicht nur eine Komfortfunktion
Viele Entwickler denken in Begriffen von:
Das sind Implementierungsdetails.
Die eigentliche Sicherheitsfrage ist:
Wohin darf das Backend im Auftrag eines Benutzers eine Verbindung herstellen?
Wenn diese Frage nicht explizit beantwortet wird, werden Funktionen wie Remote-Import standardmäßig zu SSRF-Oberflächen.
Dieser Fehler unterstreicht auch etwas Wichtiges für SSRF-Überprüfungen:
Das ist die eigentliche Erkenntnis.
Diese Sicherheitslücke drehte sich nicht um eine aufsehenerregende Nutzlast.
Es ging darum, die richtige Vertrauensgrenzen-Frage zu stellen.
Penpot erlaubte einem authentifizierten Datei-Editor, eine Remote-Bild-URL bereitzustellen, und das Backend vertraute dieser URL mehr, als es sollte. Die Weiterleitungsbehandlung erledigte den Rest.
Deshalb wurde dies zu CVE-2026-45806.