Инфляционная атака - это распространенная атака, характерная для Vault’ов стандарта ERC-4626
Этот стандарт вам знаком, если вы хоть раз депонировали средства в протоколы-обёртки под процент или поинты
Относится к уязвимостям высокого уровня, поскольку содержит в себе фронтран с кражей средств пользователя
Огромное количество протоколов использует этот стандарт, здесь список
Когда возникает
На ранних стадиях этому подвержен любой пул, который предлагает обмен активов на долю в этом пуле.
То есть, если ваш протокол в том или ином виде предлагает функцию минта долей (mint shares), то вы под ударом
Формула минта обычно выглядит примерно так
sharesAmount = totalShares * assetAmount / asset.balanceOf(address(this))Атакующий может манипулировать делителем, что может привести к получению очень малой доли токенов пула, вплоть до нулевой
Пример кода, подверженного атаке
abstract contract ERC4626 is ERC20 {
IERC20 asset;
constructor(IERC20 asset_) {
asset = asset_;
}
function totalAssets() public view returns (uint256) {
return asset.balanceOf(address(this));
}
function convertToShares(uint256 assets) public view returns (uint256) {
if (totalAssets() == 0) {
return assets;
}
return totalSupply() * assets / totalAssets();
}
function convertToAssets(uint256 shares) public view returns (uint256) {
return totalAssets() * shares / totalSupply();
}
function deposit(uint256 assets) public {
asset.transferFrom(msg.sender, address(this), assets);
_mint(msg.sender, convertToShares(assets));
}
function burn(uint256 shares) public {
_burn(msg.sender, shares);
asset.transfer(msg.sender, convertToAssets(shares));
}
}Сценарий атаки
1. Атакующий делает back-run создаваемого пула. Бэк раннинг - это когда хакер увидел транзакцию еще в мемпуле, до включения в блок, и закинул свою транзакцию так, чтобы она шла сразу же за транзакцией создания пула. Буквально «дышит в спину», если переводить по смыслу
2. Атакующий минтит себе один share пула, одну долю то есть
3. Атакующий фронтраннит депозит жертвы на 20_000 USDT
4. Атакующий раздувает делитель в формуле расчета долей до 20_000, для этого можно использовать прямой депозит в обход функции минта
5. Из-за того что в Solidity по умолчанию деление целочисленное, а хакер делает фактически
sharesAmount = 1 * 20_000 / (1 + 20_000), где в верхней части дроби средства жертвы, а внизу то что внес хакер двумя транзакциями, то есть результат меньше 1 и он округляется до 0
В итоге у хакера на балансе 1 share, за который можно выкупить все средства на балансе, потому что активы учитывают путем проверки средств на балансе напрямую
Что можно предпринять:
1. Учет баланса контракта лучше вести созданными для этого переменными, а не полагаться на asset.balanceOf(address(this));
2. Использовать ERC4626 Router
3. Использовать "dead shares"
При подготовке поста использовалась статья от MixBytes()
https://t.me/web3securityresearch