• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Сказ о том как Trail of Bits, ADBK, OpenZeppelin и Certora пропустили ошибку в коде Balancer

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 ---> bc1qhavqpqvfwasuhf53xnaypvqhhvz966upnk8zy7 для поддержания анонимной ноды ETHEREUM - main и тестов

Безопасность и вознаграждения

Начнём с вознаграждения ...

Как запустить хорошую программу поиска ошибок:

1) если программа вознаграждения есть - это уже хорошо
2) сумма вознаграждения представляет собой соразмерное вознаграждение по сравнению с суммой средств, подверженных риску.

Как запустить плохую программу:

1) ошибка раскрывается, а $$ нет
2) выплаты подтверждаются, но задерживаются на недели/месяцы
3) фактическая сумма выплаченного вознаграждения меньше, чем объявленная сумма вознаграждения.

Существование баунти должно быть положительным сигналом для пользователей/инвесторов при принятии решения о том, куда вложить свой капитал. Это привлечет хакеров к протоколу в попытке найти уязвимости, сообщить о них и получить деньги за свою работу. Однако за кулисами программы вознаграждения может происходить совсем другая история, которая известна только хакеру, платформе вознаграждения (если она применима) и команде протокола.
Если вознаграждение составляет $1 мм, но средства, подверженные риску, составляют $1 млрд - это не очень хороший сигнал для пользователей/инвесторов. Почему? Потому что стимул хакера к краже перевешивает вознаграждение, что означает, что вы должны отнестись к оценке риска гораздо более серьёзно... В конечном итоге это плохое, недальновидное бизнес-решение.

Если объявленное вознаграждение составляет $2 млн, а фактически выплачивается 25% от этой суммы, при этом риску подвергаются неограниченные депозиты пользователей - это не очень хороший сигнал для пользователей/инвесторов. Почему? Потому что стимул для хакера украсть будет перевешивать вознаграждение ...о. В конечном счете, это плохое, недальновидное бизнес-решение

Для тех, кто не знаком с тем, как работает эта экосистема: у нас есть хакеры в черной шляпе (злоумышленники), хакеры в белой шляпе (ИБ), хакеры в серой шляпе (днём ИБ, ночью нет), аудиторы (получают плату за аудит кода), пользователи/инвесторы (поставщики капитала), смарт-контракты (цели для накопления капитала) и разработчики/протоколы/компании, которые разрабатывают и поддерживают эти смарт-контракты.

Грань между "черной шляпой" и "белой шляпой" хакера (особенно в криптовалюте) может быть очень тонкой. Независимые "белые шляпы" абсолютно ничего не зарабатывают, если не могут найти ошибку, им трудно получить справедливую оплату (или вообще), а затем заплатить налог на заработанные вознаграждения. Плюс в том, что вы получаете определенный авторитет в этом пространстве, но в основном вы делаете это потому, что это морально правильно, и вы хотите, чтобы эта экосистема процветала. С другой стороны, "черные шляпы" могут украсть миллионы долларов, при этом легко сохраняя анонимность. Это факт.

Обезьяны будут обезьянами

Так как же сделать так, чтобы пользователи/инвесторы могли лучше понять риск при выборе "безопасного" и "проверенного" протокола для вложения своих средств? Является ли риск 3pool 9999% на RugChain таким же, как и у VC, поддерживаемой, сильно аудированной L2? Хороший вопрос, анон... цитируя высказывание великого народа Таиланда: "Ваша безопасность - наша забота, но в конечном итоге это ваша ответственность".

Здесь, на диком западе криптовалют, нет защиты инвесторов, но чем более образован каждый пользователь/инвестор, тем больше шансов, что он примет правильное решение о том, куда ему податься. Недавно я написал в Твиттере, что нам следует создать что-то вроде "Стены позора" для обеспечения большей прозрачности в отношении того, какие команды протоколов позволили себе обмануть хакера, когда дело доходит до выплат белым шляпам. И вот... несколько дней спустя некий абсолютный анон создал простой сайт для этого: https://bug-bounty-wall-of-shame.github.io/. Хотя споры по поводу баунти будут всегда, я считаю, что вопиющие примеры плохого поведения должны быть выведены на свет, чтобы обеспечить большую прозрачность для пользователей/инвесторов при принятии инвестиционных решений. Если вы пользователь, заинтересованный в защите своих средств в этой зарождающейся экосистеме, вам необходимо иметь представление о том, как действуют протоколы, сталкиваясь с рисками безопасности. Если пользователи обнаружат, что проект несправедливо относится к "белым шляпам", они должны забрать свой капитал и бежать далеко-далеко!

Если вам нужен отличный пример того, как нужно проводить программу баг-баунти и как нужно подходить к безопасности протоколов, обратите внимание на Balancer, с которыми было приятно работать.

Перейдём к безопасности

Balancer - это хорошо известный протокол в пространстве DeFi, который позволяет любому человеку создать пул AMM, содержащий два или более токенов, которые трейдеры могут обменивать между собой. Поставщики ликвидности блокируют токены в пулах, чтобы собирать комиссионные за своп, которые образуются в результате того, что трейдеры восстанавливают баланс пулов, выполняя свопы. Если протокол желает привлечь больше ликвидности в свой пул, он может обеспечить LP более высокую прибылm, предоставив дополнительные вознаграждения за токены в дополнение к существующей комиссии за своп. Balancer исторически занимал активную позицию в отношении безопасности и прозрачности, когда в его кодовой базе обнаруживались ошибки. Четыре ведущие аудиторские компании уже провели глубокую проверку протокола: Trail of Bits, ADBK, OpenZeppelin и Certora. Тем не менее, уязвимый контракт проскользнул мимо многих глаз и был развернут в Mainnet, Polygon и Arbitrum в течение 463 дней с десятками тысяч успешных транзакций.

К счастью, у Balancer была активная программа вознаграждения на ImmuneFi, которая в конечном итоге привела меня к поиску логической ошибки глубоко в паутине меркловского сада ...
Чтобы добавить дополнительные вознаграждения за токены в пул, токены сначала должны быть переданы в контракт MerkleOrchard по адресу 0xdAE7e32ADc5d490a43cCCba1f0c736033F2b4eFca. Токены передаются от дистрибьютора (обычно это мультисигнал протокола) путем вызова createDistribution, который pull токены в контракт MerkleOrchard, а затем помещает эти активы в контракт Balancer Vault. Эти депозиты в хранилище теперь приписываются контракту MerkleOrchard и связаны с корнем merkle-дерева, предоставленным дистрибьютором.

Ошибка

Пользователи могут подавать дублирующие заявки и сливать в контракт Balancer Vault весь баланс счета MerkleOrchard из контрактов Vault на Mainnet, Polygon и Arbitrum.
В контракте MerkleOrchard существующий LP может вызвать функцию claimDistributions, чтобы получить положенное ему вознаграждение в виде токенов за предоставление ликвидности. Эта функция позволяет пользователю обеспечить несколько заявок за один вызов.

claimDistributions вызовет внутреннюю функцию _processClaims, которая выполнит цикл for на L228: for (uint256 i = 0; i < claims.length; i++).

L233: (uint256 distributionWordIndex, uint256 distributionBitIndex) = _getIndices(claim.distributionId) - вернет точно такие же значения, если мы используем то же значение claim.distributionId в качестве входных данных. Это ключевой момент, потому что на L235 if (currentWordIndex == distributionWordIndex) будет оценен как true и обойдет функцию _setClaimedBits, которая обычно помечает утверждение как завершенное и не позволяет пользователю claim снова.

L264: currentChannelId = _getChannelId(tokens[claim.tokenIndex], claim.distributor) будет сохранять currentChannelId неизменным на каждой итерации. Этот факт, в сочетании с тем, что currentWordIndex == distributionWordIndex будет продолжать оцениваться как true, позволяет нам обойти вызов _setClaimedBits и дает нам возможность подавать дубликаты claim, если все они предоставляются в рамках одной транзакции.

Наконец, setClaimedBits вызывается на L273-276, когда мы достигаем конца массива, что окончательно запрещает воспроизведение претензии:

if (i == claims.length - 1) { _setClaimedBits(currentChannelId, claimer, currentWordIndex, currentBits); _deductClaimedBalance(currentChannelId, currentClaimAmount)

Затем эта функция вызывает контракт хранилища Balancer, который освобождает токены для вызывающей стороны.

Impact

В хранилищах Balancer на Mainnet, Polygon и Arbitrum хранилось (в совокупности) ~$3,2 млн токенов, приписанных к адресам MerkleOrchard. Никакие другие средства в хранилищах не подвергались риску, кроме баланса MerkleOrchard.

Сценарий эксплуатации

Любой LP-провайдер может выполнить одно и то же требование несколько раз в одной транзакции, чтобы украсть из контракта хранилища сумму, кратную их первоначальному требованию (максимум, вероятно, ~10 раз за раз) что будет ограничен только лимитом газа на блок и балансом счета MerkleOrchard в контракте хранилища. Таким образом, крупные LP с 6-значными значениями могут быстро опустошить контракты, тем самым крадя невостребованную доходность у других LP.

Благодарим Balancer gigachads за быстрый ответ, исправление ситуации и выплату баунти в размере 50 ETH за цель, не входившую в сферу применения.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх