TAC Bridge Drain 2026: разбор атаки на $2.5M+ в TON
Технический forensics-разбор drain-атаки на мост TAC 11 мая 2026: хендлер 0x0E50D313, отсутствие проверки подписей валидаторов
- Автор
- Денис Ким · research lead · security desk
- Опубликовано
Содержание16разделов
- TL;DR
- Хронология
- Что такое TAC-мост
- Анатомия drain-хендлера 0x0E50D313
- Что управляет словарём validators_dict
- Цепочка атаки end-to-end
- Синхронизация с ротатором
- Корни проблемы
- (C) Insider-rug — наиболее вероятно (~60%)
- (A) Компрометация ключей ротатора (~25%)
- (B) Логический баг в ротаторе (~15%)
- Где admin1 и почему он молчит
- Что это значит для пользователей TON
- Уроки для других мостов TON
- Disclosure-путь
- Что дальше
11 мая 2026 года в 09:14 UTC началась drain-атака на admin-контракт моста TAC в TON. К моменту публикации этого разбора (14 мая) drain всё ещё активен: с моста выведено более 2.5 млн долларов в обёрнутых джеттонах (USDT, BLUM, tsTON и др.) плюс 384 тыс. свежевыпущенных TAC. Ни один официальный канал TAC не публиковал ни предупреждения, ни постмортема. Эта статья — полная техническая разборка: что делают все 10 хендлеров bridge-admin контракта, почему атака возможна без приватных ключей валидаторов TAC L2 и какие конкретно адреса замешаны. Материал собран на основе декомпиляции TASM, восстановления c4-состояния и временно-узких сканов транзакций, поэтому каждый шаг атаки независимо проверяем on-chain.
TL;DR
- TAC-мост на стороне TON состоит из bridge-admin контракта (2399 строк TASM), admin1 (874 строки), и ротатора (793 строки с двумя CHKSIGNU).
- Bridge-admin не содержит ни одной on-chain инструкции CHKSIGN/CHKSIGNU/CHKSIGNS. Это подтверждается grep по всем 2399 строкам disassembly.
- Drain-хендлер
0x0E50D313принимает Merkle-proof, “виртуальный корень” из которого должен быть в словареGLOBAL 5(validators_dict). Если корень есть — мост релизит обёрнутые джеттоны по адресу из пейлоада. Это легитимный, штатный механизм. - Источник записей в validators_dict — только ротатор. Хендлер
0x23B05641(EPOCH_ROTATE) принимает корень “as is”, без верификации подписей валидаторов TAC L2 в TVM. - Кто бы ни контролировал ротатор, может впрыснуть любой корень -> drain любого джеттона. Это архитектурная уязвимость, а не баг.
- Атакующий синхронизирован с ротатором: окно “впрыск корня -> drain” — 2-7 секунд. Это автоматизированный pipeline.
- Кошелёк-триггер атаки был легитимным пользователем моста с июля 2025 года. Используется тот же кастомный шаблон кода, что и у другого кошелька на 48 тыс. TON, — сильный сигнал инсайдерского rug.
Хронология
- 2025-07-15 — кошелёк-триггер
0:7d628ee4…создан и пополнен 100 TON с FixedFloat (CEX без KYC). В течение 10 месяцев использует мост для нормальных кросс-чейн обменов. Это anti-attribution: к моменту drain кошелёк выглядит как обычный пользователь. - 2026-05-10 — финальный получатель джеттонов V5R1
0:4f1e15f2…пополнен 4 TON, опять же через FixedFloat. Источники фондирования у двух кошельков разные — компартментализация. - 2026-05-11 ~09:14 UTC — первая drain-транзакция. Ротатор впрыскивает корень, через 2-7 секунд прокси-контракт claim-ит его на хендлере
0x0E50D313, мост релизит обёрнутые джеттоны на адрес V5R1. - С 09:14 UTC и далее — ротатор продолжает впрыскивать корни каждые ~36 секунд. Drain-кошелёк претендует на drain-полезные корни в окне их 90-секундного TTL.
- 2026-05-12 12:14 UTC — снапшот c4-состояния моста: в validators_dict живёт только 2 корня (старые вытеснены), общий вывод уже более 2.5M USD в обёрнутых джеттонах.
- 2026-05-14 (сегодня) — официальной реакции TAC нет, admin1 хендлер
0x7EE5A6D0(WIPE_VALIDATORS) не вызывался, обновления кода не было. Drain продолжается.
Что такое TAC-мост
TAC — это EVM-совместимый L2, который ассоциирован с TON. Мост между TON и TAC работает по классической lock-and-mint схеме:
- Пользователь шлёт TON-актив (например, USDT-jetton) в bridge-admin контракт.
- Мост блокирует или сжигает актив и эмитит событие.
- Валидаторы TAC L2 подписывают событие.
- На стороне TAC chain выпускается соответствующий wrapped-актив.
В обратную сторону — наоборот: пользователь сжигает wrapped-актив на TAC, валидаторы подписывают, и TON-сторона релизит оригинальный jetton по адресу пользователя. Именно эта обратная ветка (TAC -> TON) и эксплуатируется в текущей атаке.
Bridge-admin контракт на TON — 0:20a5698ef27054ae67ce3a57481264642c4c15f8821f540949b29858b309ee2f. Он управляет мастер-контрактами обёрнутых джеттонов (wUSDT, wWETH, wcbBTC, нативный TAC и др.). Любой релиз джеттона или минт TAC возможен только из этого admin-контракта.
Анатомия drain-хендлера 0x0E50D313
Все 10 хендлеров bridge-admin контракта декодированы. Drain-релиз сидит на op-коде 0x0E50D313 (десятичное 240177939) и устроен так. Псевдокод сжат, но соответствует строкам 513-574 дизассемблера:
IFJMPREF {
; AUTH 1: отправитель должен быть прокси,
; чей state_init выводится из тела сообщения
NEWC ... STDICT STDICT STU 1 ENDC
HASHCU
CTOS SDEQ
THROWIFNOT 71
; AUTH 2: virtual_root_hash из Merkle-proof
; должен быть ключом в validators_dict (GLOBAL 5)
XCTOS DROP
PUSHINT 8 SDSKIPFIRST
LDU 256
GETGLOB 5 PUSHPOW2 8 DICTUGET
NULLSWAPIFNOT NIP
THROWIFNOT 201
; PAYLOAD: словарь сумм джеттонов, адрес назначения, сумма TON
DUP CTOS LDDICT SKIPDICT LDMSGADDR LDGRAMS DROP
; ... формируется 0xD7B9C06E (mint TAC) + 0x7817B330 (release jetton)
}
Двое “ворот” авторизации:
- AUTH 1 (proxy-self-hash). Отправитель должен соответствовать хэшу
state_init, который реконструируется из тела самого сообщения. То есть атакующий должен прислать сообщение от адреса, чейstate_initуказан в этом же сообщении. Это легко: разворачивается прокси-контракт на лету в той же транзакции. - AUTH 2 (root in validators_dict). Из Merkle-proof извлекается
virtual_root_hash, и этот корень должен присутствовать в словареGLOBAL 5.
Никаких проверок подписей валидаторов, кворумов или chain-of-custody до TAC L2 в drain-хендлере нет. Если корень в словаре — релиз состоится.
Что управляет словарём validators_dict
Хендлер 0x23B05641 (EPOCH_ROTATE), строки 1213-1291. Логика:
IFJMPREF {
GETGLOB 3 ; адрес ротатора
SDEQ THROWIFNOT 72 ; отправитель == GLOBAL 3
NOW GETGLOB 9 GEQ
THROWIFNOT 202 ; ротация только после дедлайна
XCHG_0I s3
LDU 256 ; новый root_hash из тела
LDU 48 ; новый deadline
... обновление GLOBAL 7/8/9 ...
; Эвикция корней с deadline <= NOW - GLOBAL 4 * GLOBAL 10
WHILE { DICTUMIN ... DICTUDEL }
; Вставка (root_hash -> 48-bit deadline)
NEWC GETGLOB 9 STU 48 ENDC CTOS
GETGLOB 5 PUXC s4 s-1 PUSHPOW2 8 DICTUSET
SETGLOB 5
}
Это вся граница доверия. Что отправит ротатор — то и считается “подтверждённой валидаторами” записью на стороне TON. Никаких CHKSIGN внутри bridge-admin для проверки кросс-чейн подписей — нет вообще.
При этом в c4 моста живёт прелюбопытная конструкция:
GLOBAL 10 = 30— длительность эпохи в секундах.GLOBAL 4 = 3— множитель TTL для корней. Корень валиден3 * 30 = 90секунд.- В validators_dict в живом снапшоте — всего 2 корня. Старые корни эвиктятся при каждой ротации.
Это объясняет, почему drain трудно увидеть в свежем сэмпле моста: использованный корень живёт максимум 90 секунд и тут же удаляется. На снапшоте 12 мая в словаре остались только два самых свежих корня — следов уже использованных drain-корней в текущем состоянии нет, они восстанавливаются только из истории транзакций.
Цепочка атаки end-to-end
Рабочий пример: drain-транзакция fd239f3189f89f63d271f80a35b93f6ba2ecb092ed1e8eccdcd54f963daa7431 от 2026-05-11 02:27:28 UTC. 14 внутренних транзакций.
| Шаг | src -> dst | op | роль |
|---|---|---|---|
| 1 | ext -> Highload 0:7d628ee… | 0x59b6416b | внешний подписанный вход |
| 2 | Highload -> self | 0xae42e5a4 | обёртка кошелька |
| 3 | Highload -> PROXY 0:e31a1fb4… | 0x3b6616c6 | деплой прокси (state_init в теле) + вызов |
| 4 | PROXY -> bridge-admin | 0x0E50D313 | drain trigger |
| 5 | bridge -> wUSDT master | 0x7817b330 | запрос release |
| 6 | bridge -> TAC jetton master | 0xd7b9c06e | mint TAC атакующему |
| 7 | wUSDT master -> wUSDT wallet | 0x0f8a7ea5 | внутренний burn (release) |
| 8 | TAC master -> новый TAC wallet | 0x178d4519 | внутренний transfer (mint dest) |
| 9 | bridge -> Highload | 0xd53276db | возврат excess |
| 10 | USDT chain -> финальный wallet 0:2ced3a3b… | 0x178d4519 | 800k USD₮ доходят до V5R1 |
| 11-14 | уведомления + excess | — | возвраты Highload + V5R1 |
Прокси-контракт 0:e31a1fb4… — экзотическая cell-ссылка на библиотеку с хэшем E46FA11560C837D5226A87E1BEC85BB89BF960C0077C8E4354A747242CBA1B05. Эта библиотека предзаписана командой TAC и хранится в самом bridge-admin (ref 1 в c4). То есть прокси-механизм — штатный, заложенный самой командой моста: атакующий не выкатывает свой шеллкод, он использует тот же шаблон, что и легитимные пользователи.
Синхронизация с ротатором
Узкоокошечный скан истории транзакций bridge-admin в окне 02:25-02:30 UTC 11 мая показывает идеальную синхронизацию:
| Время (UTC) | Источник | op | Содержит целевой корень 21259ae9… |
|---|---|---|---|
| 02:26:11 | ротатор | 0x23B05641 | первая инъекция (другой корень) |
| 02:26:18 (+7s) | прокси | 0x0E50D313 | drain по предыдущему корню |
| 02:26:45 | ротатор | 0x23B05641 | новая инъекция |
| 02:26:47 (+2s) | прокси | 0x0E50D313 | drain |
| 02:27:21 | ротатор | 0x23B05641 | корень 21259ae9… впрыснут (offset 25) |
| 02:27:28 (+7s) | прокси | 0x0E50D313 | drain клеймит этот же корень |
Окно между впрыском и клеймом — 2-7 секунд. Никакой человек не вводит руками транзакции с такой задержкой. Это автоматический pipeline: что-то слушает ротатора в реальном времени и сразу пушит drain.
Корни проблемы
В TON-контракте моста нет логического бага. Цепочка доверия admin1 -> rotator -> validators_dict -> drain работает ровно так, как закодирована. Это значит, что уязвимость лежит архитектурно: одна-единственная сущность (rotator) может вставить произвольный Merkle-корень в словарь без верификации подписей валидаторов TAC L2 на стороне TON.
Возможны три первопричины:
(C) Insider-rug — наиболее вероятно (~60%)
- Drain-кошелёк был легитимным пользователем моста 10 месяцев. Чтобы запустить drain, нужно глубоко знать формат Merkle-proof пейлоадов и механизм прокси-библиотеки.
- Кошелёк использует кастомный шаблон кода
11acad79…, который встречается ещё у одного кошелька на 48 тыс. TON (~120 тыс. USD). Это паттерн внутренней раздачи — программа валидаторов, партнёрская программа или team-issued wallet. - Отсутствие CHKSIGN в TVM на стороне TON — сознательное архитектурное решение TAC-команды. То есть rotator у них с самого начала считается trusted oracle. Misuse trusted-роли — ровно тот failure-mode, что эта архитектура и допускает.
- Ротатор работает в автоматическом режиме, выпускает новые корни каждые ~36 секунд часами подряд. Если бы это была паника-реакция на компромисс — поведение было бы другим: остановка ротатора, попытка WIPE_VALIDATORS, deploy патча.
(A) Компрометация ключей ротатора (~25%)
- В коде ротатора есть две инструкции CHKSIGNU и
signers_dictв c4. По форме это N-of-M мультисиг (порог проверяется черезGTINT 3 THROWIF_SHORT 44, что указывает на 2-из-3 или 3-из-N). - Если ключи N подписантов утекли (фишинг, supply chain, компрометация инфраструктуры) — внешний атакующий может подавать корректно подписанные сообщения ротатору, и тот будет добросовестно их пробрасывать в bridge-admin.
(B) Логический баг в ротаторе (~15%)
- 793 строки TASM ротатора с двумя CHKSIGNU и сложной логикой
PUXC2/SDATASIZE/dict-of-pending. Возможны replay-атаки, signature malleability, dict-collision. - Линейный аудит ротатора пока не проведён — это open work.
Различить (A) / (B) / (C) можно через декодирование signers_dict ротатора и сопоставление публичных ключей подписантов с известными адресами кошельков TAC-команды (особенно с кошельками, использующими шаблон 11acad79…).
Где admin1 и почему он молчит
admin1 (0:c1af58471b74ea5da7cbc7020ae0831bfa0b96e47ccb491943153bee796f2bc3) обладает экстренными полномочиями, любое из которых остановило бы drain:
0x7EE5A6D0(WIPE_VALIDATORS) — обнуляетGLOBAL 5(validators_dict) и сбрасываетGLOBAL 14. Drain-хендлер перестанет находить корни в словаре, AUTH 2 всегда будет выкидывать exception 201.0x20FAEC53(UPGRADE_CODE) —SETCODE BLESS POPCTR c3. Полная замена кода контракта без таймлока. Можно одним патчем добавить реальную проверку подписей валидаторов.0x5CEC6BE0(SET_ROTATOR) — переназначение ротатора на любой адрес. Если у admin1 есть резервный мультисиг с реальной верификацией — можно переключить пайплайн на него.0x581879BC->0x6A4FBE34(двухшаговая ротация admin) — передача admin1 другому адресу.
Все четыре полномочия — без таймлока. Решение можно принять и провести за одну транзакцию. То, что в течение трёх суток после начала drain admin1 не вмешался, — само по себе аргумент в пользу insider-сценария.
Что это значит для пользователей TON
Прямой риск касается всех, у кого есть позиции в обёрнутых джеттонах TAC-моста: wUSDT, wWETH, wcbBTC, нативный TAC, BLUM на TAC, tsTON и др. Пока admin1 не вмешается:
- Выводите ликвидность из всех wrapped-TAC джеттонов сейчас. Если она ещё там есть.
- Не делайте новых депозитов в мост в обе стороны. Любые средства, которые попадут под управление bridge-admin, могут быть выведены атакующим в течение секунд после публикации новой эпохи.
- Не покупайте wrapped-TAC джеттоны на DEX. Они “обеспечены” контрактом моста, который дренируется в реальном времени.
- Проверяйте свои подключенные dApp через раздел “Connected apps” в Tonkeeper / MyTonWallet. Если у вас есть активные сессии с frontend-ами, которые используют мост, — отзовите их.
Косвенный риск — пулы ликвидности на STON.fi / DeDust с парами обёрнутых джеттонов TAC. Если LP-провайдеры выйдут массово — токены TAC-моста на DEX просто схлопнутся к нулю, утянув за собой пары с TON и USDT. Этот сценарий тоже стоит закладывать.
Уроки для других мостов TON
Уязвимость TAC — не редкая прихоть, а паттерн. Любой DIY-мост на TON, где есть “trusted oracle” / “trusted rotator” и нет on-chain верификации кросс-чейн подписей, имеет ту же поверхность атаки. Если вы пользуетесь или разрабатываете мост — вот чек-лист:
- Grep по disassembly на CHKSIGN/CHKSIGNU/CHKSIGNS. Если в bridge-admin нет ни одной инструкции проверки подписи — это красный флаг. Доверие к мосту полностью переезжает на off-chain процесс, который вы не видите.
- Сколько подписантов реально подписывают релиз? Если ответ “один адрес-ротатор” — это не мост, это custodian с дополнительными шагами.
- Есть ли у admin’а UPGRADE_CODE без таймлока? Если есть — это rug-кнопка. Даже если команда честная, утекший ключ admin = drain любой суммы в одной транзакции.
- Видны ли в коде хендлеры обнуления валидаторов? WIPE_VALIDATORS / SET_ROTATOR без таймлока — backdoor под кризис, но также backdoor под insider-rug.
- Прозрачен ли механизм ротации валидаторов? Если эпохи короткие (десятки секунд) и корни эвиктятся за минуты — следить за абьюзом извне практически невозможно. Это плюс к anonymity атакующего.
Disclosure-путь
Bug-bounty канал TAC — info@tac.build (cc tech@tac.build). Прежде чем слать им технические детали, проверяйте подлинность их канала: PGP-подпись от кошелька admin1, DKIM-валидация заголовков письма. Сцена сильно нагрелась, и атакующие могут пытаться вытянуть детали PoC под видом “официальных” представителей.
Repro на mainnet невозможен без ключей ротатора — TVM откатит транзакцию 0x23B05641 от любого другого отправителя на этапе compute (THROWIFNOT 72). Repro на testnet — стандартный путь: клонировать bridge-admin, назначить себя admin1 и rotator, продемонстрировать тот же drain. Этого достаточно для PoC без рисков.
Что дальше
Открытые задачи forensics-фронта:
- Декодировать
signers_dictротатора в c4GLOBAL 4, сопоставить пабкеи подписантов с известными адресами TAC-команды и кошельками на шаблоне11acad79…. Прямое совпадение — финальный аргумент в пользу insider-rug. - Линейный аудит 793 строк TASM ротатора на signature-bug: replay, malleability, deduplication, off-by-one в проверке порога.
- Дизассемблер admin1 (874 строки). Отсутствие CHKSIGN у admin1 означает, что авторизация идёт каким-то другим путём — нужно найти, каким именно.
- Дизассемблер прокси-библиотеки по хэшу
0xE46FA11560…через прямой запрос к library_dict masterchain. - Трекинг outflow от V5R1 (
0:4f1e15f2…) — куда уходит 1.117M USD₮, через какие микшеры и в какой CEX.
Атака на TAC-мост — пример того, как уязвимость уровня архитектуры может прорастать в течение полугода, пока кошелёк-инсайдер ждёт момента. Текущая стадия — drain пока активен, официальной реакции нет. Если вы держите средства, ассоциированные с мостом TAC, — выходите сейчас.
Частые вопросы
Что такое TAC-мост и зачем он нужен?
Сколько украдено и продолжается ли атака?
Какой адрес атакующего и можно ли его проверить?
Это баг или умысел?
Безопасно ли пользоваться мостом сейчас?
Затронуты ли другие мосты TON?
Почему drain ещё не остановили?
Можно ли воспроизвести атаку для bug-bounty?
Похожие материалы
- Безопасность3 мар. 2026 г.
Drainer-сайты в TON: как они работают и как не попасться
Технический разбор drainer-кампаний в экосистеме TON в 2025-2026 — от Drainer-as-a-Service до конкретных приёмов с TON Connect
- Безопасность25 февр. 2026 г.
Топ-10 скамов через TON в Telegram и как защититься
Какие схемы используют мошенники в Telegram против пользователей TON в 2025-2026, реальные цифры потерь и пошаговые правила защиты для розничного пользователя.
- Аналитика24 мар. 2026 г.
Как читать on-chain метрики TON: гайд для аналитика 2026
Какие метрики важны в TON, чем они отличаются от метрик Ethereum и как их корректно интерпретировать. Источники данных, ловушки, методология — со ссылками.
- Безопасность5 мар. 2026 г.
Анатомия фишинга: поддельные сайты TON-кошельков
Разбираем по шагам, как мошенники клонируют сайты Tonkeeper и MyTonWallet, какие маркеры выдают фейк и как проверить домен за 30 секунд перед вводом seed-фразы.
- Основы22 дек. 2025 г.
Как читать TON-транзакции в эксплорере: гайд по TonScan
Разбираем TonScan и Tonviewer пошагово: как найти транзакцию по хешу, что значит каждое поле, как читать сообщения, jetton-переводы, NFT и комиссии.