Stored XSS в Telegram Desktop: от текста кнопки до JavaScript в экспортеExPatch
разобрали XSS в HTML-экспорте Telegram Desktop. Источник недоверенных данных — поле
text inline-кнопки бота. Экспортёр записывал его в HTML без экранирования:
// Было
block.append(button.text.toUtf8());
// Исправление
block.append(SerializeString(button.text.toUtf8()));
toUtf8() меняет кодировку, но не обезвреживает HTML. Поэтому
<script> внутри подписи кнопки становился исполняемой разметкой. Для текста сообщений экранирование уже применялось; отдельный путь сериализации кнопок его пропускал.
Доставка шла через штатный Bot API:
sendMessage →
reply_markup.inline_keyboard →
text. Клавиатура с URL-кнопкой сохранялась при пересылке через
CopyMarkupToForward. Участник мог перенести сообщение в группу, где самого бота никогда не было. В демонстрации символы U+3164 перед тегом маскировали его в интерфейсе клиента.
Условие срабатывания: пользователь экспортирует историю уязвимой сборкой и открывает HTML-страницу, содержащую это сообщение. JavaScript запускается при загрузке, без нажатия кнопки. Доступен DOM открытого документа: тексты, отправители, временные метки. Возможны отправка данных наружу, подмена отображаемой истории и фишинговая форма. Чтение соседних страниц экспорта или произвольных файлов этим PoC не подтверждено; серверная переписка не меняется.
Тот же патч исправляет инъекцию в JS-строку внутри
onclick="return ShowTextCopied('…')": перед подстановкой содержимого экранируются обратная косая черта и одинарная кавычка. Здесь уже нужен учёт контекста JavaScript.
Исправление вошло в Beta 6.9.4 и stable 7.0.1.
Старые HTML-файлы обновление клиента не переписывает: их нужно экспортировать заново исправленной версией либо открывать с отключённым JavaScript.
🌚
@poxek | 🌚
@poxek_ai | 📲
MAX