Solidity. Смарт контракты и аудит – Telegram
Solidity. Смарт контракты и аудит
2.62K subscribers
246 photos
7 videos
18 files
547 links
Обучение Solidity. Уроки, аудит, разбор кода и популярных сервисов
Download Telegram
Merkle-Patricia Trees. Часть 12

Значение и преимущества мерклизации

1. Неизменяемая структура данных

Основное преимущество мерклизации заключается в повышении безопасности данных. Связывая каждый узел с хэшем его содержимого, мерклизация превращает Patricia Trie в неизменяемую структуру данных. Любое изменение в данных, каким бы маленьким оно ни было, изменяет хэш узла, содержащего эти данные. Это изменение распространяется до корня тройки, изменяя корневой хэш. Следовательно, мерклизированная Patricia Trie предлагает надежный метод обнаружения изменений данных.

2. Облегчение доказательств Меркла

Мерклизированный Patricia Trie позволяет эффективно проверять данные с помощью доказательств Меркла. В двух словах, доказательство Меркла - это цепочка хэшей, доказывающих включение определенного фрагмента данных в набор данных. Пользователь может проверить существование определенного фрагмента данных, не обращаясь ко всему набору данных, ему нужны только соответствующие хэши от корневого до листового узла, содержащего данные. Этот аспект особенно полезен для легких клиентов Ethereum, которые не хранят полное состояние.

3. Обрезка и эффективность хранения

Мерклизация позволяет эффективно обрезать Patricia Trie. Поскольку узлы соединены хэшами, становится возможным удалять узлы (особенно старые версии состояния), не нарушая общей структуры Trie. Узлы, на которые нет ссылок нигде в trie (то есть их хэши не присутствуют ни в одном из родительских узлов), могут быть безопасно удалены. Этот аспект помогает оптимизировать требования клиентов Ethereum к хранению данных.

Таким образом, мерклизация Patricia Trie - это важнейший аспект работы с состоянием Ethereum, обеспечивающий как высокий уровень целостности данных, так и эффективные средства их проверки.

К настоящему моменту мы рассмотрели большинство фундаментальных концепций, связанных с Merkle-Patricia Trees.

Помните, что понимание Merkle-Patricia Trees может быть сложным из-за их сложной структуры и свойств, но они являются фундаментальным компонентом архитектуры Ethereum и играют важную роль в поддержании целостности и безопасности состояния Ethereum. Ничего страшного, если вы не сразу поймете все детали - продолжайте изучать и пересматривать концепцию, и постепенно она станет более понятной.

На следующей неделе выйдет еще пара постов на эту тему, и мы, наконец, перейдем к более простым постам по Solidity.

#merkle #patricia
🔥3
Merkle-Patricia Trees. Часть 13

Проверка содержимого Merkle-Patricia Tree

Наиболее привлекательной особенностью Merkle-Patricia Tree (MPT) является его способность быстро и эффективно проверять содержимое блока данных благодаря свойствам криптографической хэш-функции, лежащей в его основе. Фундаментальным принципом, лежащим в основе этого процесса проверки, является концепция «доказательств Меркла», которые могут подтвердить существование определенной точки данных в дереве.

Давайте подробнее рассмотрим, как можно проверить содержимое Merkle-Patricia Tree.

Понимание основ: Узлы и маски ветвей

Прежде чем приступить к процессу проверки, необходимо четко понимать, что такое узлы и как они устроены в MPT. Помните, что каждый узел в MPT связан с уникальным хэшем.

В MPT существует три типа узлов:

1. Листовые узлы: Эти узлы содержат фактические данные (например, состояние счета). Каждый узел листа состоит из ключа (путь от корневого узла к листу) и значения (состояние счета).

2. Узлы расширения: Эти узлы по сути являются узлами «быстрого доступа», которые позволяют нам пропустить те части дерева, где есть длинный ряд узлов, каждый из которых имеет только одного ребенка. Они состоят из общей последовательности нибблов и ссылки на другой узел.

3. Узлы ветвей: Эти узлы - настоящая сила, когда речь идет о ветвлении и связывании внутри дерева. Узел ветвления состоит из 17 элементов; 16 из них представляют все возможные значения nibble (от 0 до 15), а последний 17-й элемент хранит значение, если этот узел является концом ключа.

Каждый узел ветвления в MPT также имеет «битовую маску ветвления» - 16-битное двоичное число, отражающее наличие дочерних узлов. Если у узла ветвления есть дочерний узел, соответствующий определенному значению полубайта, то в битовой маске в соответствующей позиции будет стоять '1'.

В MPT у узла ветвления может быть до 16 дочерних узлов, поскольку он основан на 16-битной (4-битной) системе из-за использования шестнадцатеричных ключей.

Таким образом, в данном контексте «битовая маска ветви» - это 16-битное двоичное число, каждый бит которого соответствует существованию (или несуществованию) дочернего узла. Если для данного значения полубайта существует дочерний узел, соответствующий бит в битовой маске ветвления будет равен '1'; если для данного полубайта дочерний узел не существует, бит будет равен '0'.

Процесс верификации: Прохождение через доказательство Меркла

Процесс проверки содержимого MPT начинается с «доказательства Меркла». Это доказательство представляет собой список сериализованных узлов в том порядке, в котором они встречаются при движении по дереву от корня к рассматриваемому узлу.

Для того, чтобы проверить содержимое дерева, нужно:

1. Начните с корневого узла и хэша всего дерева, которые должны быть известны.

2. Изучите узел, указанный в доказательстве Меркла. Для узлов с ветвями вы будете смотреть на битовую маску ветви и следовать по пути, указанному ключом, который вы пытаетесь доказать. Если битовая маска указывает на дочерний узел, следуйте по этому пути и переходите к следующему узлу в доказательстве. Если дочернего узла нет, это означает, что ключ не существует в дереве.

3. Для узлов листьев и расширений вы будете проверять, соответствует ли пара ключ/значение тому, что вы пытаетесь доказать. Узлы листьев должны содержать точный ключ, который вы ищете. Продлевающие узлы должны соответствовать текущему сегменту ключа, а следующий узел в доказательстве должен быть следующим.

4. Продолжайте двигаться вниз по дереву, повторяя процесс для каждого узла в доказательстве.

5. Процесс завершится, когда вы достигнете узла листа с точным ключом, который вы ищете. Значение в этом узле листа - это данные, которые вы пытались доказать. Если процесс завершается, не найдя соответствия, значит, ключа в дереве нет.

6. Важно отметить, что по мере прохождения доказательства вы также должны вычислять хэш каждого узла и проверять, совпадает ли он с хэшем родительского узла. Это очень важно для гарантии того, что данные не были подделаны.
Сила доказательств Меркла

Прелесть доказательств Меркла и этого процесса проверки заключается в том, что он позволяет эффективно и безопасно подтверждать данные в больших наборах данных. Учитывая сложность и размер блокчейна Ethereum, такая эффективность имеет решающее значение. Несмотря на кажущуюся сложность процесса, он оптимизирован таким образом, что для подтверждения корректности данных требуется минимальное количество информации, что позволяет Ethereum сохранять свою безопасность и децентрализацию.

Помните, что понимание процесса верификации в Merkle-Patricia Tree требует времени и практического опыта. Но с учетом растущей популярности блокчейна и Ethereum эта концепция заслуживает внимания.

#merkle #patricia
👍1
Merkle-Patricia Trees. Часть 14

Уязвимости при использовании Merkle-Patricia Trees

Merkle-Patricia Trees являются важной частью архитектуры блокчейна Ethereum, предлагая эффективный способ хранения и проверки данных. Благодаря сочетанию способности Patricia Trie работать с данными переменной длины с защитой от взлома, которую обеспечивает дерево Меркла, MPT служат неотъемлемым компонентом состояния Ethereum.

Однако, как и в случае с любой другой структурой данных, безопасность MPT во многом зависит от ее реализации. Плохо реализованная система MPT может быть уязвима для целого ряда проблем безопасности. Примечательно, что эти уязвимости проистекают не из самой концепции MPT, а из того, как они используются и реализуются. Две распространенные уязвимости, связанные с MPT, включают атаки повторного воспроизведения.

Атаки повторного воспроизведения (Replay Attacks)

Атака повторного воспроизведения, также известная как атака воспроизведения, - это когда легитимная передача данных злонамеренно повторяется или задерживается. В контексте MPT злоумышленник может перехватить доказательство Меркла (доказательство подлинности и целостности данных) и попытаться использовать его повторно.

Например, в блокчейне Ethereum злоумышленник может попытаться воспроизвести доказательство транзакции, чтобы создать проблему двойной траты или повторить определенную операцию. Хотя в Ethereum используется система nonce для предотвращения атак на воспроизведение в разных блоках, если у злоумышленника есть возможность влиять на сетевую связь (например, при распределенной атаке типа «отказ в обслуживании» (DDoS)), он может воспроизвести транзакции в пределах одного блока.

Для защиты от атак повторного воспроизведения часто используются уникальные идентификаторы, временные метки или nonces. Ethereum специально использует nonces для предотвращения атак повторного воспроизведения, увеличивая nonce с каждой транзакцией, чтобы гарантировать уникальность каждой из них.

Если вам интересно, то разобрать одну из таких уязвимостей можно тут:

https://medium.com/immunefi/polygon-double-spend-bug-fix-postmortem-2m-bounty-5a1db09db7f1

Основные уязвимости, связанные с MPT, обусловлены сложностью их конструкции, которая при небрежном подходе может подвергнуть систему значительным рискам. Например, одной из уязвимостей является возможность манипулирования узлами дерева во время его реконструкции, что может привести к расхождениям в состоянии блокчейна.

В заключение следует отметить, что Merkle-Patricia Trees являются важнейшим компонентом инфраструктуры Ethereum, поэтому обеспечение их безопасной и эффективной работы имеет первостепенное значение. По мере развития и становления технологии блокчейн разработчики и исследователи должны помнить об этих уязвимостях и разрабатывать инновационные решения не только для снижения этих рисков, но и для повышения общей устойчивости и целостности систем блокчейн.

#merkle #patricia
2
Некоторые планы на ближайшее время

На прошлой неделе мы закончили большой цикл разбора работы Merkle-Patricia Trees. Это было сложно, многие нюансы работы я осваиваю до сих пор (сложно найти проекты, где бы это было реализовано в понятном виде), и на усвоение информации уйдет время... Какие же вообще планы на ближайшее время?

Во-первых, в идеале было бы провести несколько стримов по безопасности. Я ранее собирал материал по уязвимостям ERC4626, Uniswap и Chainlink и хотел бы уже в этом году закрыть свой гештальт и рассказать вам, что накопал.

Во-вторых, с февраля я вел отдельный канал, где мы разбирали баги с конкурсных аудитов. За все это время, я осознал, что мне нужно прокачать навык "быстрого понимания кода" для поиска потенциальных проблем.

Я знаю и понимаю более 90% уязвимостей от всех отчетов, что я прочитал за это время (а их уже более 200!), но на разбор нового конкурса у меня уходит много времени. Или, скорее, на каждый конкурс я могу уделить всего пару часов в день, и все это время может занять разбор одной-двух функций. А этого вообще не остаточно для хороших результатов...

Вот я и хочу выработать какую-нибудь практику для более быстрого понимания работы функций и изменения в памяти контракта, чтобы весь процесс аудита шел эффективнее. Некоторые записи я буду выкладывать тут на канале. Возможно, кому-то еще это поможет сделать старт в конкурсных аудитах.

В-третьих, вышли несколько интересных обновлений в фаундри и других программ для тестирования смарт контрактов. Мне было бы интересно разобрать как они работают. Или может даже параллельно запустить небольшой интенсив по работе с foundry с нуля. Посмотрим, как все пойдет.

В-четвертых, я понимаю, что многие пришли на канал за информацией по Solidity, а тут какие-то "душные" темы про деревья и абстракции, поэтому, скорее всего, до конца года будем дальше разбирать пункты из репо Chinmaya, который прекрасно акцентирует внимание на некоторые правила работы EVM и Solidity.

Пока как-то так. Не уверен, что получится реализовать все на 100%, но мы будем стремиться к этому!

Всем приятной рабочей недели и легкого обучения!

#offtop
10👍2
Стрим / видео на тему "ERC4626: Проблемы и решения"

Наконец у меня появилось немного свободного времени и я создал небольшую презентацию с разбором проблем, которые у вас могут возникнуть при работе с таким стандартом как ERC4626.

Думаю провести завтра вечерний стрим, часов в 7 по мск, здесь в Телеграме. Если что-то пойдет не так, то уже в пятницу выложу просто записанное видео с лекцией.

По продолжительности планирую потратить минут 40. Приглашаю всех к участию!

Когда: завтра, 31 октября, 19:00 мск

#stream #erc4626
🔥149
Live stream scheduled for
Стрим!

Начинаем буквально через 10 минут!

#stream
4👍2
Media is too big
VIEW IN TELEGRAM
Запись стрима "ERC4626: проблемы и решения"

Спасибо всем, кто зашел на вчерашний стрим! Я все еще учусь проводить такие мероприятия, и порой тяжело дается быстро сформулировать свою мысль. Пишу я гораздо лучше, чем говорю... Но мы будем практиковаться!

В телеграмме я впервые проводил такую встречу и, вероятнее всего, нажал не ту кнопку записи и в итоге получил только звуковую дорожку без видео. Его пришлось записывать уже сразу после стрима, поэтому некоторые моменты могут отличаться.

Также, как и в прошлый раз, выкладываю видео, презентацию и ссылки на протоколы, где были обнаружены уязвимости из видео, чтобы вы сами могли еще раз пройтись по ним и разобрать.

Презентация в Google Docs

Предложение стандарта EIP4626

Контракт от Open Zeppelin


Проблема первого депозитора

Статья - https://mixbytes.io/blog/overview-of-the-inflation-attack
Статья - https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks


"Депозит 1 wei"

Ссылка на отчет с описанием бага - https://code4rena.com/reports/2023-10-ethena#m-04-malicious-users-can-front-run-to-cause-a-denial-of-service-dos-for-stakedusde-due-to-minshares-checks

Ссылка на контракт - https://github.com/code-423n4/2023-10-ethena/blob/main/contracts/StakedUSDe.sol#L191#L194


Проблема с decimals

Статья - https://blog.rivanorth.com/erc-4626-vulnerabilities-and-how-to-avoid-them-in-your-project


Unchecked блоки

Ссылка на отчет - https://code4rena.com/reports/2024-04-panoptic#h-02-overflow-in-collateraltracker-allows-minting-shares-for-free

Ссылка на контракт - https://github.com/code-423n4/2024-04-panoptic/blob/833312ebd600665b577fbd9c03ffa0daf250ed24/contracts/CollateralTracker.sol


Проверка на slippage

Ссылка на контракт - https://github.com/code-423n4/2024-03-pooltogether/blob/main/pt-v5-vault/src/PrizeVault.sol#L454-L472


Permit + ERC4626

Ссылка на отчет - https://code4rena.com/reports/2023-07-pooltogether#m-11-vaultmintwithpermit-can-be-dosd-

Ссылка на контракт - https://github.com/GenerationSoftware/pt-v5-vault/blob/b1deb5d494c25f885c34c83f014c8a855c5e2749/src/Vault.sol


Деление перед умножением

Ссылка на отчет - https://code4rena.com/reports/2024-04-gondi#h-02-division-before-multiplication-could-lead-to-users-losing-50-in-withdrawalqueue

Ссылка на контракт - https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/WithdrawalQueue.sol#L137-L144


Буду рад узнать ваши мысли по поводу такого стрима: что добавить, что убрать, что переделать.

Ну, и конечно, буду признателен репостам и лайкам!

Всем еще раз спасибо!

#erc4626
2🔥104
Что по аудиту? Введение

Ранее я писал, что планирую "заново научиться проводить аудит", чтобы сформировать для себя подход к более быстрому пониманию кодовой базы протокола. Кратко говоря, сейчас моя цель научиться делать письменные заметки, чтобы быстро запоминать function flow в контракте и понимать его суть на более детальном уровне в короткий промежуток времени. А зачем?

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

Я прекрасно знаю и понимаю вопросы безопасности смарт контрактов, понимаю, куда нужно смотреть, и в чем может быть потенциальная проблема. При этом сейчас другое время для "багов".

Сами разработчики контрактов сильно прокачались в работе с уязвимостями и простые ошибки, они видят и отлавливают тестами. Остаются только либо, какие-то интеграционные нишевые особенности, либо логические, когда вам необходимо понимать работу контракта на уровне разработчика, чтобы найти что-то.

Понять простую функцию или геттер - не составляет никакого труда. Но когда появляются множественные условия, assembly и огромное количество переменных состояния, за которыми нужно следить - тут и начинаются проблемы.

Посвящая 6-8 часов в день на разбор кода, можно наконец разобрать в нем и найти максимальное количество багов за весь период конкурса. Но что делать, если находить хочется, а времени нет? Я, по крайней мере, хочу попытаться прокачать навык "быстрого понимания протокола"... А что получится, буду описывать тут в постах.

#audit
🔥3
Что по аудиту? Sablier

На прошлой неделе, в пятницу, закончился мини конкурс протокола Sablier - всего 699 nsloc.

У меня получилось выделить на него всего около 5-6 часов, т.е. в среднем по часу в день.

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

Я понял, что совсем не умею работать с заметками: не понимаю, что нужно отмечать, как возвращаться к ним и как строить архитектуру.

До этого момента, я использовал теги в редакторах, по типу:

@audit, @audit-issue, @note, @audit-ok

и другие, чтобы акцентировать свое внимание на каких-либо деталях. Это хорошо помогает, когда у тебя достаточно времени, чтобы вникнуть в код. Но когда его мало, я могу отвлекаться на них и терять другую мысль, над которой размышляю в данный момент.

Что для меня хорошо сработало в данный аудит?

1. Залить описательную инфу протокола в переводчик.

Я очень хорошо знаю английский язык и свободно говорю, пишу и читаю на нем. Понять смысл протокола, по докам от разработчиков не составит труда, если сконцентрироваться на тексте. Но родной язык, все таки, родной.

Закинув тот же текст в переводчик и пробежав глазами по тексту, вы тоже сможете зацепиться за какие-то основное идеи документации и быстрее понять суть. Годы тренировок на сторис в инстаграм дают о себе знать...

2. Редактор кода, где можно разложить контракт на сниппеты кода также очень помогает.

Мне не нужно было бегать по контракту туда-сюда, чтобы вспомнить, что делать служебная функция. Достаточно было открыть ее рядом с основным контрактом и подглядывать раз через раз.

Да и отслеживать function flow так гораздо легче.

3. Рисунок if/else.

В низу одного из скринов, я делал разбивку условия и заметки по основным моментам, на которые оно влияет. Это сильно помогло запомнить как изменяется память контракта в зависимости от тех или иных опций. Мне зашел этот способ, буду использовать его и дальше.

4. Движение токенов.

Я старался понять движение токенов в контракте: в функциях и до пользователя. Это также помогло отсеять несколько вопросов, которые возникали у меня в процессе аудита.

В целом, я понял протокол, вероятно, на 90% и отправил два репорта.

Не смотря на какие-либо последующие результаты, я понял для себя комфортный способ работы с заметками и некоторые вещи, которые были бесполезными. Думаю на следующем конкурсе попробовать пару новых идеи.

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

Всем легкой и приятной недели!

#audit
👍10
Поделитесь статьями?

В последнее время стал реже находить какие-либо "мясные" статьи и разборы на темы Solidity, Foundry, аудита, да и в целом, смарт контрактов. Одни копи-пасты ссылок на ресурсы из первой страницы выдачи гугла.

Может вы сами читали в последнее время какие-нибудь крутые статьи или разборы, и такие потом: "Вау, классная штука!"?

Вот такие статьи и ищу, буду признателен, если поделитесь ими в комментариях.

#posts
👍52
Низкоуровневый вызов и высокоуровневый вызов в Solidity

Контракт в Solidity может вызывать другие контракты двумя способами: через интерфейс контракта, что считается высокоуровневым вызовом, или с помощью метода call, что является низкоуровневым подходом.

Несмотря на то, что оба метода используют опкод CALL, Solidity обрабатывает их по-разному.

В этой статье мы сравним их: почему низкоуровневый вызов никогда не возвращается, в то время как высокоуровневый вызов может вернуться, и почему низкоуровневый вызов по пустому адресу считается успешным, а высокоуровневый вызов возвращается при вызове несуществующего контракта.

Почему низкоуровневый вызов (или вызов delegatecall) никогда не возвращается, а вызов через интерфейс контракта может?

Прежде чем объяснить, почему, обратимся к документации Solidity, в которой рассматривается этот вопрос.

Когда исключения происходят в подвызове (sub-call), они «всплывают» - «bubble up» - (т. е. исключения отбрасываются) автоматически, если только они не пойманы в операторе try/catch. Исключением из этого правила являются send и низкоуровневые функции call, delegatecalll и staticcall: они возвращают false в качестве первого возвращаемого значения в случае исключения вместо того, чтобы «bubble up».

Ниже покажу оба вызова, чтобы сравнить их поведение. В примере я использую метод call, но те же принципы можно распространить и на delegatecall.

Вызывающий может вызвать ops() в Called двумя способами. Обратите внимание, что ops() всегда возвращается:

pragma solidity ^0.8.0;

contract Caller {

// first call to ops()
function callByCall(address _address) public returns (bool success) {
(success, ) = _address.call(abi.encodeWithSignature("ops()"));
}

// second call to ops()
function callByInterface(address _address) public {
Called called = Called(_address);
called.ops();
}
}
contract Called {

// ops() always reverts
function ops() public {
revert();
}
}


Несмотря на то, что оба метода используются для вызова одной и той же функции, и оба метода используют опкод CALL, компилятор solidity генерирует байткод для обработки случаев реверта по-разному. Выполнение обеих функций в рамках контракта Caller покажет, что Caller.callByInterface вернется, а Caller.callByCall - нет.

На уровне EVM опкод CALL возвращает булево число, указывающее, был ли вызов успешным или нет, и помещает это возвращение в стек. Сам опкод не вызывает возврата.

Когда вызов выполняется через интерфейс контракта, Solidity обрабатывает это возвращаемое значение за нас. Он явно проверяет, является ли возвращаемое значение ложным, и инициирует возврат, если только вызов не был сделан внутри блока try/catch.

Однако при использовании низкоуровневых вызовов нам нужно вручную обрабатывать возвращаемый булевый символ и при необходимости явно инициировать возврат.

contract Caller {

//...
function callByCall(address address) public returns (bool success) {
(success, ) = address.call(abi.encodeWithSignature("ops()"));
if (!success) {
revert("Something went wront");
}
}
//...
}


P.S. Разница между высокоуровневым и низкоуровневым вызовом показана на скрине.

#highlevel #lowlevel #call
5👍1
Разница между вызовом и вызовом по интерфейсу при вызове пустого адреса. Часть 2

И еще пара слов к предыдущему посту.

Низкоуровневый метод вызова Solidity не выполняет предварительную проверку, чтобы убедиться, что вызываемый адрес соответствует контракту.

Контракт может проверить, является ли адрес смарт-контрактом, используя EXTCODESIZE, который является опкодом для address.code.length. Если размер равен нулю, это означает, что по данному адресу контракта нет. Однако сам метод вызова не включает эту проверку. Он напрямую выполняет опкод CALL независимо от происходящего.

При использовании интерфейса, проверяется размер кода цели вызова.

Другими словами, в байткоде, сгенерированном для функции callByInterface, по указанному адресу выполняется опкод EXTCODESIZE перед выполнением опкода CALL.

Если размер, возвращаемый EXTCODESIZE, равен нулю, это указывает на отсутствие контракта по этому адресу, и функция возвращается к выполнению опкода CALL. Это объясняет, почему функция callByInterface возвращается, если выполняется вызов с несуществующим адресом контракта, а callByCall - нет.

P.S. Разница между тем, как вызов низкого уровня и вызов высокого уровня взаимодействуют с пустым контрактом, показана на скрине.

Нужно уточнить, что выполнение вызова может вернуться, если встретит опкод REVERT, закончится газ или попытается выполнить что-то запрещенное, например, деление на ноль. Однако, когда вызов выполняется по пустому адресу, ни одно из вышеперечисленных условий не может возникнуть.

#highlevel #lowlevel #call
2👍1
Что по аудиту? Sablier - результаты

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

Нет, оба репорта оказались не валидными. И раз уж я пошел делиться с вами своим процессом аудита, то будет правильно рассказать вам об "ожиданиях на бум" и столкновением с реальностью.

Всего сейчас в конкурсе три подтвержденных находки уровня Low.

Я отправил два репорта, вместо трех, как хотел изначально. И знаете, какой был третий репорт? Да, как раз на расхождение со стандартом ERC4906. Почему же я не отправил его?

После изучения протокола я увидел, что Sablier превосходно подготовились к аудиту и не оставили никаких более-менее открытых возможностей для атаки. Контракт отличается высоким уровнем безопасности и продуманности. И такая проблема, как расхождение со стандартом, скорее всего, была обусловлена выбором самих разработчиков. И что ее посчитают максимум Info, и все равно сделают не валидной.

По своему первому репорту, я понадеялся, что "прокатит". Да, видел в репорте от LightChaser есть пункт на отсутствие проверки аргументов на нулевой адрес. При этом там были явно указаны некоторые функции, где такой проверки не было. Я же обратил внимание на ситуацию, которая не была описана в отчете. Судьи посчитали, что это "проблема одной сути" (что по факту так и есть) и поставили invalid.

Со вторым репортом я сам ошибся и только вчера понял, что если бы уделил чуть больше времени на валидацию находки, то ничего бы в итоге не отправил.

Когда я просматривал контракты, то обратил внимание, что в одной из функций сначала было деление (descale), а потом умножение (scale). Решил, что это обычная математическая проблема и не проверил, на что она повлияет. В итоге получилось, что я был отчасти прав и это часть уже была указана в одном из предыдущих репортов.

В завершение могу сказать, что упустил две проблемы:

1. Пользователи могут избежать комиссии, делая депозит мелких сумм.
2. Оракул возвращает одинаковые значения для разных по сути Flow.

И если про первый баг, я даже не задумывался, то со вторым интереснее...

Я писал, что понял протокол где-то на 90%. И функция, в которой был найден 2 баг, была как раз в этих 10%. Я тогда (да и сейчас), не особо понял, зачем вообще нужна эта функция. К тому же она не использовалась ни в каких других в протоколе. Грубо говоря, тогда я решил просто "забить" на нее и фокусировал внимание на основной части протокола.

Думаю, стоит вынести пару уроков из этого конкурса:

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

2. Проверяйте лучше свои находки. Если вы нашли какую-то проблему в контракте, то потратьте больше времени на подтверждение своей находки. Не стоит полагаться, что если подобное было раньше, то 100% это засчитают и сейчас. Проверяйте! А лучше напишите тест, если время вам позволяет.

3. Старайтесь понять протокол на 100%. Если вы планируете стать аудитором на все рабочее время, то для хороших находок вам нужно понимать весь протокол целиком. Даже из этого конкурса можно вынести то, что в нишевой функции, которая нигде больше не используется, может быть проблема.

Вскоре заканчивается еще один маленький конкурс на Codehawks и после него я также поделюсь своими заметками и наблюдениями!

Всем приятной недели и легкого обучения!

#audit
5🔥4👍1
Небольшая история о путешествии в мире аудита

Этот пост был написан прекрасным аудитором Charles Wang день или два назад. Он делится своим опытом в старте аудита смарт контрактов, и что сработало для него больше всего. Думаю, этот пост будет полезен всем, кто собирается стать аудитором и участвовать в конкурсах.

Первый шаг: Построчный аудит

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

На этом этапе я сосредоточился на том, чтобы понять, что делает каждая функция и как она вписывается в общую функциональность. Однако такой построчный подход, хотя и был тщательным, имел свои ограничения:

1. Пропущенные уязвимости: Некоторые ошибки, особенно сложные, связанные с кросс-функциональной логикой или логикой, основанной на состояниях, просто невозможно найти, используя только этот подход.

2. Трудности с восприятием общей картины: Сосредоточенность на отдельных строчках кода мешала понять более широкий дизайн контракта или то, как взаимодействуют различные части.

На этом этапе я развивал базовые навыки: понимая синтаксис, структуру и логику, лежащую в основе различных функций, но еще не разработал метод, позволяющий выявить некоторые более сложные или тонкие уязвимости.

На этом этапе было очень тяжело, потому что ресурсы для изучения solidity (не говоря уже об аудите) были крайне ограничены.

Второй шаг: Внедрение перехода между функциями и дифференциации состояний

На втором этапе я адаптировал свой процесс, чтобы устранить ограничения чисто построчного аудита. Я начал переходить от одной функции к другой, отслеживая, как различные функции связаны и взаимодействуют друг с другом. Этот переход помог мне лучше понять управление состоянием контрактов:

1. Лучшее понимание контекста: Понимая взаимосвязи между функциями, я смог выявить уязвимости при переходе от одного состояния контракта к другому.

2. Аудит на основе функциональности: Вместо того чтобы сосредоточиться на отдельных строках, я начал рассматривать функции как часть более широкого контекста. Это позволило мне обнаружить логические проблемы, возникающие при использовании функций вместе или в определенной последовательности.

Такой подход помог мне выявить больше ошибок и улучшил мою способность анализировать сложные взаимодействия функций. К этому моменту у меня сформировался более целостный взгляд на код, но способность эффективно выявлять теоретико-игровые проблемы или уязвимости в состояниях контракта все еще не была совершенной.

Третий шаг: Первоначальная оценка через разделение state

На третьем этапе я начал аудит с первоначальной оценки на основе состояний контракта. С приобретением опыта я обнаружил, что могу выявить потенциальные слабые места, просто составив карту различных состояний и взаимодействий:

1. Предварительная идентификация ошибок: Во время этой первоначальной оценки я уже мог находить ошибки, просто классифицируя и понимая состояния контракта. Процесс разделения и анализа этих состояний прояснил многие уязвимости с самого начала.

2. Распознавание паттернов и чувствительность: Опыт научил меня распознавать закономерности и определять области, которые могут содержать уязвимости. Определенные шаблоны кодирования или переходы состояний становились «красными флажками», и я интуитивно понимал, где искать. Не следует путать это с подбором шаблонов, чем в наши дни занимается большинство аудиторов.

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

Четвертый шаг: Свободный аудит и лиды, основанные на интуиции
👍1
На четвертом этапе мой подход стал в значительной степени основан на интуиции. Я больше не чувствовал необходимости начинать с самого начала контракта или следовать строгому порядку строк. Вместо этого я начинал с произвольного места в архитектуре и первые несколько дней изучал кодовую базу в свободном порядке:

1. Lead-Based Auditing: Вместо того чтобы систематически прорабатывать функции, я составлял список потенциальных зацепок, следуя своей интуиции в тех областях, которые, как я подозревал, могли иметь уязвимости. Интересно, что к тому времени интуиция была уже настолько развита, что я мог найти множество зацепок/потенциальных зацепок в течение первых нескольких минут.

2. Глубокое понимание архитектуры: Такой свободный подход дал мне отличное понимание архитектуры контракта. Я начал раскрывать теоретико-игровые сценарии и сложные уязвимости, которые могли быть скрыты при обычном аудите.

3. Высокая степень успешности интуиции: На этом этапе моя интуиция стала важнейшим инструментом. Благодаря многолетнему опыту я мог чувствовать, какие области кода могут содержать уязвимости. Часто я обнаруживал серьезные проблемы, основываясь исключительно на этом ощущении.

Эта эволюция моего подхода означала, что я мог оценивать не только безопасность отдельных функций, но и безопасность всей структуры и замысла протокола, включая очень сложные проблемы, которые трудно было объяснить, «как я их нашел».

Я достиг того момента, когда стал достаточно уверенным, чтобы сказать, что при достаточной тщательности и затрате времени я способен обнаружить около 95 % всех проблем. Это подтверждалось снова и снова при участии в командных аудитах.

Это не означает, что мой подход к аудиту в наши дни основан исключительно на интуиции. Это лишь одна из многих составляющих моей методологии полного аудита.

Размышления: Как опыт формирует подходы к аудиту

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

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

#audit
🔥4🤔2👍1