Блокчейн
Integer Overflow, Flash Loans, Oracle Manipulation
В апреле 2018 года токен BEC потерял всю капитализацию за минуты - атакующий создал из воздуха токены на 900 миллионов долларов, прибавив 1 к числу, которое и так было максимальным. В 2023 году Euler Finance лишился 197 миллионов долларов за одну транзакцию через мгновенный займ, который был выдан и использован за 12 секунд. А трейдер-одиночка манипулировал ценой токена на Mango Markets и вывел 114 миллионов из протокола, даже не нарушая его код. Три разных вектора, три катастрофы - и у каждой были известные методы защиты, которые разработчики не применили.
- **BEC Token (2018)** - integer overflow в функции batchTransfer позволил создать 900M токенов. SafeMath, который стоил бы ~5,000 gas на вызов, полностью предотвратил бы атаку. После этого инцидента OpenZeppelin SafeMath стал стандартом, а Solidity 0.8 встроил проверки в компилятор
- **Euler Finance (2023, 197M)** - flash loan атака через уязвимость в donateToReserves. Атакующий занял средства, пожертвовал их в резерв (создав некорректный health factor), затем ликвидировал сам себя с прибылью. После переговоров хакер вернул средства - редкий случай в истории DeFi
- **Mango Markets (2022, 114M)** - Avraham Eisenberg манипулировал ценой MNGO на спотовом рынке, завысил стоимость своего залога и взял кредит, опустошивший казну протокола. Позже был арестован и осуждён за рыночную манипуляцию - первый такой приговор в DeFi
Предварительные знания
Integer Overflow/Underflow: арифметика, уничтожавшая миллиарды
В обычных языках программирования целочисленное переполнение - это баг, который приводит к некорректным вычислениям. В Solidity до версии 0.8 это был **вектор атаки стоимостью в миллиарды долларов**. Тип `uint256` хранит числа от 0 до 2²⁵⁶ − 1. Если прибавить 1 к максимальному значению, число не вызывает ошибку - оно **молча сбрасывается в 0**. Если вычесть 1 из 0, результат - гигантское число 2²⁵⁶ − 1. Именно так атакующие превращали нулевой баланс в астрономические суммы.
В апреле 2018 года атака на **BEC Token (Beauty Chain)** эксплуатировала именно этот баг. Функция `batchTransfer` умножала количество получателей на сумму перевода без проверки overflow. Атакующий передал такие параметры, что произведение `count * value` переполнилось до маленького числа - оно прошло проверку баланса. Но каждый получатель получил полный `value`, в сумме - **900 миллионов в токенах, созданных из ничего**. Цена BEC упала до нуля за минуты.
До Solidity 0.8 стандартной защитой была библиотека **SafeMath** от OpenZeppelin. Каждая арифметическая операция оборачивалась в функцию с проверкой: `add()`, `sub()`, `mul()`, `div()`. Если результат выходил за пределы - транзакция откатывалась через `require`. С версии **Solidity 0.8** компилятор генерирует проверки overflow/underflow автоматически - это встроено в bytecode. SafeMath больше не нужен.
Блок `unchecked{}` в Solidity 0.8+ отключает проверки overflow для экономии gas (~120 gas на операцию). Это безопасно **только** когда вы математически доказали невозможность переполнения. Например, инкремент счётчика цикла `for (uint256 i; i < arr.length;) { unchecked { ++i; } }` - безопасен, потому что `i` ограничен длиной массива. Но `unchecked { balances[user] -= amount; }` - потенциальная уязвимость, потому что `amount` контролируется пользователем.
В атаке на BEC Token функция batchTransfer вычисляла amount = count * _value. Атакующий передал count=2 и _value=2^255. Почему проверка require(balances[sender] >= amount) не защитила контракт?
Flash Loan атаки: мгновенные займы как оружие
Flash loan - это займ, который **выдаётся и возвращается в одной транзакции**. Если заёмщик не вернул средства к концу транзакции - вся транзакция откатывается, и кредитор ничего не теряет. Это уникальный инструмент, невозможный в традиционных финансах: вы можете взять в долг **миллионы долларов без залога**, использовать их, и вернуть - всё за одну атомарную операцию. Flash loan сам по себе не является атакой, но он даёт атакующему **неограниченный капитал** для эксплуатации уязвимостей.
В феврале 2020 года протокол **bZx** стал жертвой первой крупной flash loan атаки. Атакующий занял 10,000 ETH через dYdX, использовал часть для манипуляции ценой на Uniswap, открыл выгодную позицию на bZx по искажённой цене, и вернул займ - чистая прибыль **$350,000 за одну транзакцию**. Через четыре дня - повторная атака на **$600,000** с другим вектором. В марте 2023 года **Euler Finance** потерял **197 миллионов** через серию flash loan транзакций, эксплуатируя уязвимость в функции donateToReserves.
Flash loan - это **инструмент**, а не уязвимость. Он используется для легитимного арбитража, рефинансирования позиций и self-liquidation. Настоящая уязвимость - в протоколах, которые полагаются на **спотовую цену DEX** как источник истины. Если протокол использует `getReserves()` Uniswap-пула для определения цены залога, flash loan позволяет манипулировать этой ценой внутри одной транзакции. Поэтому безопасные протоколы используют **TWAP** и **Chainlink** - они устойчивы к мгновенной манипуляции.
Flash loan позволяет занять миллионы долларов без залога. Почему это безопасно для кредитора (например, Aave)?
Манипуляция оракулами: когда контракт верит лжецу
Смарт-контракты не имеют доступа к внешнему миру - они не могут проверить цену актива на бирже, прочитать курс валюты или узнать результат матча. **Оракулы** - это мосты между блокчейном и реальным миром, поставляющие внешние данные on-chain. Если оракул сообщает неверную цену, контракт принимает решения на основе лжи. Манипуляция оракулом - это способ **заставить протокол считать, что актив стоит не столько, сколько на самом деле**, и извлечь из этого прибыль.
В октябре 2022 года трейдер **Avraham Eisenberg** манипулировал ценой токена MNGO на **Mango Markets** (Solana). Он открыл огромную длинную позицию по MNGO, затем на спотовом рынке искусственно поднял цену токена. Протокол использовал эту завышенную цену для оценки залога - и Eisenberg взял в долг **$114 миллионов** под "подорожавший" залог, фактически опустошив казну протокола. В ноябре 2022 года **Compound** едва не потерял $80M из-за ошибки в оракуле DAI, когда на MakerDAO были изменены параметры стабильности.
Почему `block.timestamp` не подходит как оракул? Майнеры (в PoW) и валидаторы (в PoS) имеют возможность **сдвигать timestamp блока** в пределах допустимого диапазона (обычно ±15 секунд). Если контракт определяет цену или условия на основе `block.timestamp`, валидатор может выбрать выгодный для себя момент. Для цен используют TWAP с достаточным окном (минимум 30 минут), для случайности - VRF (Chainlink VRF), а не `block.timestamp` или `blockhash`.
**Мульти-оракул** - подход, при котором протокол использует несколько независимых источников цены и берёт медиану. Пример: MakerDAO для определения курса ETH/USD использует агрегацию от 26 нод-ценовых источников. Если один источник скомпрометирован, он отбрасывается как выброс. **Chainlink** реализует подобный подход на уровне инфраструктуры: 21+ нод-оператор подаёт цену, смарт-контракт агрегатор берёт медиану и публикует on-chain.
Lending-протокол определяет стоимость залога через getReserves() пула Uniswap V2. Почему это создаёт критическую уязвимость?
Манипуляция ценами: sandwich, MEV и защита
Манипуляция ценами - это обобщение атак, где атакующий **контролирует порядок транзакций или искажает рыночные данные** для извлечения прибыли. В традиционных финансах это запрещено и уголовно наказуемо. В DeFi публичный mempool делает каждую транзакцию видимой до включения в блок, а MEV (Maximal Extractable Value) превращает манипуляцию в **индустрию с оборотом в миллиарды долларов**.
В июле 2023 года пулы **Curve Finance** потеряли **70 миллионов** через комбинацию reentrancy-бага в компиляторе Vyper и последующей oracle-манипуляции. Атакующий эксплуатировал read-only reentrancy: при входе в функцию withdraw баланс пула был временно несогласован, и оракул, читающий состояние пула в этот момент, возвращал искажённую цену. Это пример **amplification-атаки**: один баг (reentrancy) усиливается другим вектором (oracle manipulation) и наносит кратно больший ущерб.
**Circuit breaker** (автоматический выключатель) - защитный механизм, который приостанавливает работу протокола при обнаружении аномалий. Если цена актива изменилась более чем на X% за один блок, или объём выводов превышает Y% от TVL за час - критические функции блокируются. Chainlink Price Feeds имеют встроенный circuit breaker: если цена отклоняется от предыдущего значения более чем на определённый порог, обновление задерживается для проверки. Комбинация **TWAP + Chainlink + circuit breaker + pause mechanism** - современный стандарт защиты DeFi-протоколов.
**Flashbots Protect** и **MEV Blocker** - инструменты для пользователей, отправляющие транзакции **напрямую валидаторам**, минуя публичный mempool. Если транзакция не видна MEV-ботам, sandwich-атака невозможна. На уровне протокола: всегда устанавливайте `amountOutMin` на основе off-chain котировки с допуском ±0.5%, задавайте `deadline` не более 5 минут, и используйте private mempools (Flashbots) для крупных транзакций.
Flash loan атаки и манипуляция оракулами - уязвимости самих flash loan и оракулов, которые нужно "запретить" или "ограничить"
Flash loan - нейтральный инструмент для атомарных операций, а оракулы - необходимый мост к внешним данным. Уязвимость всегда в протоколе, который неправильно использует ценовые данные: полагается на спотовую цену вместо TWAP, не валидирует данные оракула на свежесть и диапазон, не имеет circuit breaker при аномальных колебаниях. Атака лишь эксплуатирует эти архитектурные ошибки
Попытки ограничить flash loan (например, лимиты на объём) ослабляют легитимные сценарии - арбитраж, ликвидации, рефинансирование - без устранения корневой проблемы. Протокол, защищённый TWAP + Chainlink + circuit breaker, безопасен даже при наличии flash loan с неограниченным капиталом. Фокус защиты должен быть на архитектуре протокола: валидация данных оракулов, ограничение volatility impact, pause-механизмы при аномалиях.
Пользователь вызывает swap на Uniswap с параметром amountOutMin = 0. Какую атаку это делает максимально выгодной для MEV-бота?
Итоги
- **Integer overflow** в Solidity до версии 0.8 заставлял числа при переполнении молча сбрасываться в 0 (или прыгать к максимуму при underflow). SafeMath решал эту проблему для старых версий, а Solidity 0.8+ встроил проверки в компилятор. Блок `unchecked{}` отключает их для оптимизации gas - но только когда overflow доказанно невозможен
- **Flash loan** - атомарный займ без залога в пределах одной транзакции. Сам по себе безопасен для кредитора (revert при невозврате), но даёт атакующему неограниченный капитал для эксплуатации уязвимостей: манипуляции ценами, ликвидации чужих позиций, drain ликвидности
- **Манипуляция оракулами** опасна для протоколов, использующих спотовую цену DEX (`getReserves()`) как источник истины. Один крупный swap (через flash loan) мгновенно искажает цену. Защита: TWAP (средняя за 30+ минут), Chainlink (медиана от 21+ независимых нод), мульти-оракулы
- **Sandwich-атаки и MEV** эксплуатируют прозрачность mempool: MEV-боты видят ожидающие транзакции и выстраивают свои перед и после жертвы. Защита пользователя: `amountOutMin` с допуском ±0.5%, `deadline` ≤ 5 минут, private mempools (Flashbots). Защита протокола: circuit breakers, pause-механизмы, TWAP вместо спотовой цены
- Три вектора атак, три катастрофы - BEC Token, Euler Finance, Mango Markets - и у каждой были известные защитные механизмы. Overflow предотвращается SafeMath или Solidity 0.8+, flash loan атаки - корректной архитектурой протокола, oracle manipulation - TWAP и Chainlink. Когда цена ошибки измеряется в сотнях миллионов, каждый пропущенный guard - приглашение для атакующего
Связанные темы
Overflow, flash loan и oracle-атаки связывают базовую безопасность Solidity с экосистемой DeFi и механизмами ценообразования:
- Безопасность: reentrancy — Reentrancy и overflow - два фундаментальных вектора атак на смарт-контракты. Curve (2023) показала, как reentrancy усиливает oracle manipulation: временно несогласованное состояние пула выдаёт искажённую цену
- AMM и DEX — Flash loan атаки манипулируют резервами AMM-пулов для искажения спотовой цены. Понимание формулы x*y=k - ключ к пониманию того, почему крупный swap сдвигает цену и как TWAP защищает от этого
- Lending-протоколы — Lending-протоколы (Aave, Compound, Euler) - главные жертвы oracle manipulation и flash loan атак. Их модель "залог → кредит" напрямую зависит от точности цены залога
- Оракулы — Детальный разбор архитектуры Chainlink, Band Protocol, TWAP и UMA - от механики до экономической безопасности и game theory оракулов
Вопросы для размышления
- Solidity 0.8+ встроил проверки overflow, но оставил unchecked{} для оптимизации gas. Если бы вы проектировали язык для смарт-контрактов с нуля, оставили бы вы возможность отключить проверки? Какой баланс между безопасностью и производительностью оптимален, когда ошибки стоят миллионы?
- Flash loan делает капитал «бесплатным» для атакующего - достаточно найти уязвимость, не нужен стартовый капитал. Как это меняет модель угроз по сравнению с традиционными финансами? Должны ли протоколы проектироваться исходя из предположения, что атакующий имеет неограниченный капитал?
- Avraham Eisenberg публично признал манипуляцию Mango Markets, назвав это «легитимной торговой стратегией». Он был осуждён. Где проходит граница между арбитражём (который улучшает эффективность рынка) и манипуляцией (которая наносит ущерб)? Может ли код smart-контракта определять эту границу?