карты на столе

BadReveal — эксплойт NFT

Новое семейство уязвимостей

Новый эксплойт, обнаруженный исследователями Sayfer и @rugpulfinder команда позволяет злоумышленникам узнать, что является самым редким NFT, до раскрытия проекта. Это дает злоумышленнику неравное преимущество среди инвесторов, чтобы купить самый редкий и самый дорогой предмет. В некоторых проектах самые редкие предметы могут стоить в 50 раз больше, чем стандартная цена за штуку.

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

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

Воздействие на несколько активных проектов

Чтобы понять масштабы проблемы, мы проанализировали более 100 различных проектов и обнаружили, что 12 уязвимы для атаки. Вот некоторые из уязвимых проектов: 

CatBloxPUMAКапсула (Время эксплуатации: 2 дня 18 часов, рыночная капитализация: 628.19 ETH)

Герои Эверай: Дуэт (Время эксплуатации: 4 дня 2 часа, рыночная капитализация: 1,905.37 ETH)

Деген Тунз (Время эксплуатации: 3 дня 13 часа, рыночная капитализация: 6,221.6 ETH)

Более подробная информация, такая как конкретное время транзакции, находится в приложение.

Технические детали уязвимости

проверка данных  

Поскольку NFT Идентификаторы ERC721, каждый токен уникален и имеет собственные метаданные. Обычно в проекте NFT с этапом раскрытия токены чеканятся вслепую, и как только все чеканится, раскрываются NFT, а их метаданные становятся общедоступными. NFT с редкими метаданными могут стоить в 50 раз больше, чем другие NFT в проектах. Самый редкий из них называется «Легенда».

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

Что такое токен URI?

URI токена NFT — это уникальный идентификатор того, как «выглядит» токен. URI может быть HTTP-адресом, хешем IPFS или даже большим двоичным объектом JSON, который хранится в цепочке. Он указывает на файл JSON, содержащий метаданные токена.

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

Уязвимость 

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

Пример использования

Здесь у нас есть пример функции, которая возвращает URI маркера, если reveal flag имеет значение true (по умолчанию false) или URI, указывающий на скрытое изображение, общее для каждого токена, если флаг false.

function tokenURI(uint256 tokenId) public view virtual override
       returns (string memory)
   {
       require(_exists(tokenId), "Nonexistent token");
       if (reveal == false) {
           return hideURI;
       }
       string memory URI = baseURI;
       uint256 randomId = ((randomNumber + tokenId) % supplyMax) + 1;
       return
           bytes(URI).length > 0
               ? string(abi.encodePacked(URI, randomId.toString(), ".json"))
               : "";
   }

URI состоит из четырех элементов: baseURI, tokenId, supplyMaxкачества randomNumber. Эти четыре элемента общедоступны в нашем коде, и мы легко знаем первые три элемента, но не знаем, когда именно. randomNumber инициализируется.

Теперь давайте посмотрим на функцию, где reveal становится правдой.

function revealNFT() external onlyOwner {
    require(randomNumber != 0);
       reveal = true;
   }

Мы понимаем, что каждый URI доступен после раскрытия NFT. До этого у нас было только одно общее изображение для каждого NFT. Но прежде чем звонить revealNFT(), владелец должен убедиться, что randomNumber был инициализирован.

Итак, давайте посмотрим, где код присваивает значение randomNumber.

function requestRandomNumbers() external onlyOwner {
       requestId = COORDINATOR.requestRandomWords(
           keyHash,
           s_subscriptionId,
           requestConfirmations,
           callbackGasLimit,
           numWords
       );
   }
 
   function fulfillRandomWords(
       uint256, 
       uint256[] memory randomNumbers
   ) internal override {
       randomNumber = randomNumbers[0];
   }

В этом примере они используют VRF Chainlink, автономный генератор случайных чисел, для создания смещения в URI с randomNumber. Эти две функции выше показывают, как это работает. Хозяин должен позвонить requestRandomNumbers() который присвоит случайное число randomNumber с внутренним вызовом fulfillRandomWords() по цепочке.

Что происходит в реальной жизни, когда владелец хочет раскрыть NFT?

Владелец начнет со звонка requestRandomNumbers() присвоить случайное значение randomNumber, Потому как randomNumber теперь имеет ненулевое значение, владелец может вызвать revealNFT() повернуть reveal флаг в true, а затем все URI являются общедоступными с tokenURI().

Но помните, что для прогнозирования URI нам нужно всего четыре элемента, включая три уже известных и randomNumber который также является общедоступным и имеет свое значение, когда requestRandomNumbers() называется.

Так что тем временем между requestRandomNumbers() и revealNFT() вызываются, URI еще не общедоступны, но у нас есть все элементы для их предсказания.

Существует еще одна версия этой уязвимости. Хотя он более распространен, его сложнее использовать. Если нет шага раскрытия, baseURI часто устанавливается в начале или в середине фазы чеканки. Таким образом, как только NFT чеканится, он раскрывается, но оставшиеся токены остаются неизвестными. Затем мы можем получить URI и использовать его, чтобы узнать самые редкие NFT среди оставшихся. Тогда вы должны попытаться отчеканить в нужное время, чтобы найти их.

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

Чтобы избежать подобных уязвимостей, вы можете проверить эти Стратегии рандомизации для выпадения NFT Уильям Энтрайк. Это поможет вам безопасно рандомизировать URI токенов.

риска

Так что же делать, если вы владеете проектом NFT и хотите снизить риски и защитить кодовую базу от BadReveal?

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

Если вы планируете раскрывать NFT, одновременно установите URI базового токена:

 function reveal(string memory baseURI) public onlyOwner {
     revealed = true;
     baseTokenURI = baseURI;
 }

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

function fulfillRandomness(bytes32, uint256 _randomness) internal override {
     revealed = true;
     randomNumber = randomNumbers[0];
}

Используя этот подход, вы, возможно, захотите отказаться от revealed boolean из вашего кода и заменив его проверкой Chainlink на лету randomNumber назначение:

function someMintingFunction(uint256 _quantity) internal {
     require(!randomNumber, "Not revealed yet");
     //…
}

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

Обзор

Кибер-риски проектов NFT могут принимать разные формы и формы, распространено мнение, что если ваш NFT не был украден, вас не взломали, но что, если вы должны были получить самый редкий NFT в колоде, но злоумышленник был достаточно умен, чтобы купить его раньше вас? Вас взломали, даже не подозревая об этом.

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

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

Мы хотели бы поблагодарить коврик @rugpulfinder помощь в расследовании и проверке уязвимости вместе с нами. Мы использовали их опыт и знания в области NFT, чтобы углубить кроличью нору BarReveal.

Приложение – затронутый проект

Детали CatBloxPUMACapsule

https://etherscan.io/address/0x8b8D1225bB21CA07812FF3c2ee9358f7b5d90EcA

  • Нет вызова setBaseURI()
  • Текущий BaseURI, заданный в конструкторе 
  • 04 2022:04:29 +UTC → Создание контракта
  • 06 июня 2022 г., 10:14:21 +UTC → Последний отчеканенный NFT

Everai Heroes: подробности о дуэте

https://etherscan.io/address/0x9a38dec0590abc8c883d72e52391090e948ddf12

Детали Дегена Тунза

https://etherscan.io/address/0x19b86299c21505cdf59ce63740b240a9c822b5e4

Написано
Авигдор Сэзон Коэн

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

 

перейти к содержанию