
L'importazione remota di immagini di Penpot ha permesso a un editor di file autenticato di trasformare una normale funzionalità pratica dei media in una SSRF di origine backend poiché URL controllati dall'attaccante sono finiti in un percorso di recupero server che segue i reindirizzamenti senza filtraggio della destinazione.
L'importazione remota di immagini di Penpot ha permesso a un editor di file autenticato di trasformare una normale funzionalità multimediale in una SSRF di origine backend, poiché URL controllati dall'attaccante sono finiti in un percorso di fetch del server che seguiva i redirect senza filtraggio della destinazione.
Ho trovato questo problema mentre analizzavo Penpot, la piattaforma open-source di collaborazione su design e codice, con una domanda molto specifica in mente:
Cosa succede quando uno strumento di design collaborativo permette a un utente di passare al backend un URL di un'immagine remota da scaricare?
In questo caso, quella domanda ha portato a un vero bug.
Il flusso di importazione di immagini remote di Penpot accettava un URL controllato dall'utente e faceva sì che il backend lo scaricasse dal contesto di rete del server senza imporre restrizioni sulla destinazione per target su loopback o rete privata. Il client HTTP condiviso seguiva anche automaticamente i redirect.
Questo ha trasformato una normale funzionalità multimediale in una primitiva SSRF di origine backend autenticata, portando infine a CVE-2026-45806.
Penpot: Penpot su 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
Questo ha interessato Penpot. Sul suo sito ufficiale e nel media kit, Penpot si presenta come avente una base di utenti in crescita di oltre 1 milione e afferma che decine di migliaia di organizzazioni lo usano, tra cui Blender, Mozilla, Fedora, NTT Data, MIT, Société Générale, Cisco, Fujitsu, Indra e ByteDance.
editor di file autenticato -> URL di immagine remota controllato dall'attaccante -> create-file-media-object-from-url -> download-image del backend con redirect abilitati -> richiesta finale atterra su endpoint interno solo immagini -> SSRF di origine backend / raggiungibilità interna
Penpot è una piattaforma open-source di collaborazione su design e codice.
Gestisce cose come:
Ciò significa che il suo percorso di importazione multimediale si trova su un vero confine di fiducia.
La domanda importante qui non era se Penpot supporti l'importazione di immagini remote.
La vera domanda era:
Penpot limita dove il backend può connettersi quando un utente importa un'immagine remota?
In questo caso, non lo faceva.
Molte persone sottovalutano le funzionalità di importazione remota.
È un errore.
Nel momento in cui un'applicazione:
crea un vero confine di fiducia in uscita.
Questo era il problema qui.
Questo bug non riguardava il rendering delle immagini. Non riguardava l'archiviazione dei file. Non riguardava i normali controlli di permesso per la modifica di un file.
Era un classico fallimento di fiducia lato server:
Questo è sufficiente per creare una vera vulnerabilità.
Non ho affrontato Penpot testando ciecamente metodi RPC casuali o cercando crash prima di tutto.
L'approccio più forte era identificare il confine di sicurezza più promettente.
Per Penpot, quello era l'importazione di media remoti.
Perché?
Perché questa funzionalità combina:
Quello era il confine giusto da ispezionare.
Ed era esattamente dove viveva il bug.
Il bug si riduce a una piccola catena di fiducia.
Nel 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}))
l'url controllato dall'utente viene inviato direttamente nella chiamata RPC.
Poi nel 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))])
e:
(defn- create-file-media-object-from-url
[cfg {:keys [url name] :as params}]
(let [content (media/download-image cfg url)
il backend verifica che il chiamante possa modificare il file di destinazione, quindi passa l'URL controllato dall'attaccante in media/download-image.
L'implementazione dello scaricamento è qui:
(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})
E il client HTTP condiviso è configurato come:
(http/build-client {:connect-timeout 30000
:follow-redirects :always}))
Questa è l'intera vulnerabilità:
Perché l'attaccante ha bisogno solo di:
La catena d'attacco è semplice:
Questo è l'intero bug.
La distinzione importante è dove avviene la richiesta.
La domanda non è:
"Penpot può importare immagini da URL?"
La vera domanda è:
"Un utente autenticato può far sì che il backend di Penpot si connetta a destinazioni interne a cui l'utente non dovrebbe poter accedere tramite l'applicazione?"
In questo caso, la risposta era sì.
Questo è importante perché c'è una differenza reale tra:
La validazione delle immagini non rimuove quella differenza.
Riduce alcuni casi di esfiltrazione diretta, ma non rimuove la condizione SSRF né la violazione del confine di rete.
Ho validato questo problema con una prova locale controllata direttamente collegata al percorso di codice esaminato di Penpot.
L'obiettivo non era colpire infrastrutture di terze parti. L'obiettivo era provare l'esatta proprietà di sicurezza: