Демонстрация критической уязвимости в Telegram-ботах с обработкой платежей через Звёзды (⭐). Дыра которую допускают на удивление даже крупные боты!
Проект включает:
- DemoOfVuln.py — Aiogram бот с двумя методами оплаты (уязвимый и защищённый)
- MK_FuckPayments.plugin — Нашумевший плагин из первоисточника.
Чат поддержавших exteraGram — модифицированного Telegram-клиента — активно разрабатывали расширения для клиента. В один момент кто-то из чата создал плагин, который позволял визуально имитировать баланс в Звёздах, TON и Подарки.
Один из пользователей чата саппортеров (@muralovty) решил провести тест: использовать этот плагин при оплате в Звёздах. И вот тут все ахуели — платёж прошёл успешно, и подписка была выдана без реальной оплаты. Так была обнаружена критическая уязвимость в системе обработки платежей Telegram-ботов.
На волне этого открытия я (@mkultra6969) взял исходный плагин и начал изучать почему оно работает, вычистил его от лишнего и выложил упрощённую/немного измененную версию в своём канале @MKextera под названием MK_FuckPayments.plugin.
После публикации пользователи активно начали обчищать каждый попавшийся бот, проверяя уязвимость. К счастью, оригинальные посты и плагин пришлось удалить в течение часа из-за всего этого пиздеца.
🕯️06.11.2025 — день, когда эта уязвимость получила массовое распространение.
Спасибо exteraGram Supporters⭐ и всем кто участвовал. Было весело, хоть и некрасиво.
Уязвимые боты отправляют товар/контент сразу после pre_checkout_query.answer(ok=True), не дожидаясь реального successful_payment от серверов.
Плагин MK_FuckPayments работает на уровне Telegram-клиента и перехватывает запрос оплаты:
- Перехват TL_payments_sendStarsForm — плагин ловит запрос отправки платёжной формы
- Подмена ответа — вместо реальной формы оплаты показывается фейковое окно
TL_payments_paymentResult - Фейковый success — клиент закрывает форму и показывает пользователю сообщение об успехе
- Выдача товара — уязвимый бот отправляет товар в
pre_checkoutобработчике (без реальной оплаты!)
Пользователь: Плагин: Бот:
✓ Видит success ✓ Показывает success ✗ Выдаёт товар (БЕЗ ОПЛАТЫ!)
✓ Получает товар ✓ Деньги не списались
@dp.pre_checkout_query(lambda q: q.invoice_payload == "simple_payment")
async def process_simple_checkout(pre_checkout_query: PreCheckoutQuery):
await pre_checkout_query.answer(ok=True) # ✓ OK от плагина
# ⚠️ ВЫДАЁМ ТОВАР ЗДЕСЬ (БЕЗ ПРОВЕРКИ)
await bot.send_message(user_id, "Вот твой контент!")Проблемы:
- ❌ Простой payload без подписи (
"simple_payment") - ❌ Нет проверки
telegram_payment_charge_id - ❌ Товар выдаётся только по
pre_checkout(дальше не ждёт!) - ❌ Плагин это использует, т.к.
successful_paymentне может сгенерировать
async def cmd_secure_payment(message: Message):
user_id = message.from_user.id
timestamp = int(datetime.now().timestamp())
# 🛡️ Подписываем payload HMAC-SHA256
signature = generate_signature(user_id, timestamp, amount=10)
payment_id = f"secure_{user_id}_{timestamp}_{signature}"
# Сохраняем данные для проверки
pending_payments[payment_id] = {
"user_id": user_id,
"amount": 10,
"created_at": datetime.now(),
"pre_checkout_ok": False,
"validated": False # ← Товар НЕ выдан!
}
await bot.send_invoice(..., payload=payment_id)
@dp.message(F.successful_payment)
async def process_successful_payment(message: Message):
payment_id = message.successful_payment.invoice_payload
# ✓ Проверяем подпись
# ✓ Проверяем telegram_payment_charge_id
# ✓ ТОЛЬКО ПОТОМ реально выдаём товар
if verify_signature_and_charge_id(payment_id, message.successful_payment):
await bot.send_message(user_id, "Платёж подтвержден! Вот твой контент!")Защита:
- ✅ HMAC-подпись в payload (
secure_user_1234_1699...) - ✅ Таймштамп для предотвращения переиспользования
- ✅ НЕ выдаёт товар в
pre_checkout - ✅ Ожидает реального
successful_paymentсtelegram_payment_charge_id - ✅ Плагин НЕ может обойти (не может сгенерировать валидную подпись)
-
Никогда не выдавайте товар в
pre_checkoutобработчике# ❌ НЕПРАВИЛЬНО @dp.pre_checkout_query(...) async def bad(query): await query.answer(ok=True) send_content() # УЯЗВИМО! # ✅ ПРАВИЛЬНО @dp.pre_checkout_query(...) async def good(query): await query.answer(ok=True) # Только подтверждение!
-
Используйте HMAC-подписи в payload
signature = hmac.new(SECRET_KEY.encode(), data.encode(), hashlib.sha256).hexdigest() payload = f"secure_{user_id}_{timestamp}_{signature}"
-
Проверяйте
telegram_payment_charge_id@dp.message(F.successful_payment) async def on_payment(message: Message): charge_id = message.successful_payment.telegram_payment_charge_id # Плагин НЕ может подделать это поле! validate_payment(charge_id)
-
Добавьте таймстамп для предотвращения переиспользования
# Отклоняем платежи старше 5 минут if datetime.now() - payment_time > timedelta(minutes=5): return False
pip install aiogrampython DemoOfVuln.py/start— Информация о методах/simple_payment— УЯЗВИМЫЙ метод (обходится плагином)/secure_payment— ЗАЩИЩЁННЫЙ метод (плагин НЕ может обойти)
Этот проект — образовательная демонстрация уязвимости в платёжных системах Telegram-ботов.
Используется в целях:
- 🔍 Исследования безопасности
- 📚 Обучения разработчиков в первую очередь)))))
- 🧪 Тестирования защиты
Не используйте для:
- 💰 Мошенничества или краж - это реально плохо.
- 🎁 Выдачи контента без реальной оплаты
- 🚫 Нарушения правил Telegram
- @mkultra6969 - Этот README, бот и форк плагина
- @muralovty - Первооткрыватель бага
- И еще кто-то но я не помню их @username
Бот отправляет инвойс
↓
Пользователь жмёт "Купить"
↓
Telegram → pre_checkout_query
↓
Бот отвечает ok=True
↓
Пользователь вводит пароль / платит реально
↓
Telegram → successful_payment (+ charge_id)
↓
Бот отправляет товар
Бот отправляет инвойс
↓
Плагин перехватывает TL_payments_sendStarsForm
↓
Плагин показывает фейковое окно TL_payments_paymentResult
↓
Бот получает pre_checkout_query
↓
Бот отвечает ok=True
↓
⚠️ УЯЗВИМЫЙ БОТ ВЫДАЁТ ТОВАР (БЕЗ ОПЛАТЫ!)
↓
Реального successful_payment не приходит
WTFPL - Мне как всегда похуй кто и что будет с этим делать, уже хайпануло без меня.
⭐ Поставь звезду умоляю.