
A importação remota de imagens do Penpot permite que um editor de arquivos autenticado transforme um recurso normal de conveniência de mídia em SSRF de origem de backend, porque URLs controladas pelo atacante cruzaram para um caminho de busca de servidor que segue redirecionamentos sem filtragem de destino.
A importação remota de imagens do Penpot permitia que um editor de arquivos autenticado transformasse um recurso comum de mídia em um SSRF de origem do backend, porque URLs controladas pelo atacante cruzavam para um caminho de busca do servidor que seguia redirecionamentos sem filtragem de destino.
Encontrei esse problema ao revisar o Penpot, a plataforma de design e colaboração de código aberto, com uma pergunta muito específica em mente:
O que acontece quando uma ferramenta de design colaborativo permite que um usuário entregue ao backend uma URL de imagem remota para buscar?
Neste caso, essa pergunta levou a um bug real.
O fluxo de importação remota de imagens do Penpot aceitava uma URL controlada pelo usuário e fazia com que o backend a buscasse a partir do contexto de rede do servidor, sem impor restrições de destino para alvos de loopback ou rede privada. O cliente HTTP compartilhado também seguia redirecionamentos automaticamente.
Isso transformou um recurso comum de mídia em uma primitiva SSRF autenticada de origem do backend e, por fim, tornou-se CVE-2026-45806.
Penpot: Penpot no 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
Isso afetou o Penpot. Em seu site oficial e kit de mídia, o Penpot afirma ter uma base de usuários em crescimento de +1M e diz que dezenas de milhares de organizações o utilizam, incluindo Blender, Mozilla, Fedora, NTT Data, MIT, Société Générale, Cisco, Fujitsu, Indra e ByteDance.
editor de arquivos autenticado -> URL de imagem remota controlada pelo atacante -> create-file-media-object-from-url -> fetch download-image do backend com redirecionamentos habilitados -> requisição final chega a um endpoint de imagem exclusivo interno -> SSRF de origem do backend / alcançabilidade interna
Penpot é uma plataforma de design e colaboração de código aberto.
Ele lida com coisas como:
Isso significa que seu caminho de importação de mídia está em um limite de confiança real.
A pergunta importante aqui não era se o Penpot suporta importar imagens remotas.
A verdadeira pergunta era:
O Penpot restringe onde o backend pode se conectar quando um usuário importa uma imagem remota?
Neste caso, não.
Muitas pessoas subestimam os recursos de importação remota.
Isso é um erro.
No momento em que uma aplicação:
ela cria um limite de confiança de saída real.
Esse era o problema aqui.
Esse bug não estava na renderização de imagens. Não estava no armazenamento de arquivos. Não estava nas verificações de permissão comuns para editar um arquivo.
Era uma clássica falha de confiança no lado do servidor:
Isso é suficiente para criar uma vulnerabilidade real.
Eu não abordei o Penpot fazendo fuzzing cego de métodos RPC aleatórios ou procurando por crashes primeiro.
A abordagem mais forte foi identificar o limite de segurança mais promissor.
Para o Penpot, isso era a importação remota de mídia.
Por quê?
Porque esse recurso combina:
Esse era o limite certo para inspecionar.
E foi exatamente onde o bug residia.
O bug se resume a uma pequena cadeia de confiança.
No 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}))
a url controlada pelo usuário é enviada diretamente para a chamada RPC.
Em seguida, no 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)
o backend verifica que o chamador pode editar o arquivo de destino e, em seguida, passa a URL controlada pelo atacante para media/download-image.
A implementação do fetch está aqui:
(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 o cliente HTTP compartilhado está configurado como:
(http/build-client {:connect-timeout 30000
:follow-redirects :always}))
Essa é toda a vulnerabilidade:
Porque o atacante precisa apenas de:
A cadeia de ataque é direta:
Esse é todo o bug.
A distinção importante é onde a requisição ocorre.
A pergunta não é:
"O Penpot pode importar imagens de URLs?"
A verdadeira pergunta é:
"Um usuário autenticado pode fazer o backend do Penpot se conectar a destinos internos que o usuário não deveria conseguir alcançar através da aplicação?"
Neste caso, a resposta foi sim.
Isso importa porque há uma diferença real entre:
A validação de imagem não remove essa diferença.
Ela estreita alguns casos de exfiltração direta, mas não remove a condição de SSRF nem a quebra do limite de rede.
Validei esse problema com uma prova local controlada diretamente ligada ao caminho de código revisado do Penpot.
O objetivo não era atingir infraestrutura de terceiros. O objetivo era provar a propriedade de segurança exata: