картки на столі

BadReveal – експлойт NFT

Нова родина вразливостей

Новий експлойт, знайдений дослідниками Sayfer і @rugpullfinder Команда дозволяє зловмисникам дізнатися, що є найрідкіснішим NFT до розкриття проекту. Це дає зловмиснику нерівну перевагу серед інвесторів, щоб купити найрідкісніший і найдорожчий шматок. У деяких проектах вартість найрідкісніших виробів може бути в 50 разів вищою за стандартну ціну за штуку.

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

Немає способу автоматично перевірити це, тому якщо ви хочете знати, чи проект, у який ви хочете інвестувати або вже інвестували, вразливий до BadReveal, позначте нас у Twitter @SayferSecurity з #BadReveal, і наші дослідники перевірять це.

Впливає на кілька активних проектів

Щоб зрозуміти масштаби проблеми, ми проаналізували понад 100 різних проектів і виявили, що 12 із них були вразливі до атаки. Ось деякі з вразливих проектів: 

CatBloxPUMACapsule (Час експлуатації: 2 дні 18 годин, ринкова капіталізація: 628.19 ETH)

Everai Heroes: Duo (Час експлуатації: 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 у проекті та знайти, який із них найрідкісніший, а потім купити його на основі його tokenID.

Приклад експлойту

Тут ми маємо приклад функції, яка повертає URI маркера, якщо reveal прапор має значення 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() від Chainlink.

Що відбувається в реальному житті, коли власник хоче розкрити NFT?

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

Але пам’ятайте, що для прогнозування URI нам потрібні лише чотири елементи, включаючи три вже відомі та randomNumber який також є публічним і йому присвоюється значення, коли requestRandomNumbers() це називається.

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

Існує ще одна версія цієї вразливості. Хоча він більш поширений, його важче використовувати. Якщо етап розкриття відсутній, базовий URI часто встановлюється на початку або в середині фази монетизації. Тож щойно 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 логічне значення з вашого коду та замінити його перевіркою Chainlink на льоту randomNumber призначення:

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

Використання цього механізму є найпростішим способом пом’якшити BadReveal, оскільки він не створює розриву між транзакціями, що дозволяє зловмиснику купувати раніше, ніж інші покупці отримають інформацію метаданих.

Підсумки

Кіберризики проектів NFT можуть набувати різноманітних форм. Існує поширена думка, що якщо ваш NFT не вкрали, вас не зламали, але що, якби ви мали отримати найрідкісніший NFT у колоді, але зловмисник вистачило розуму купити його раніше вас? Вас зламали, навіть не підозрюючи про це.

Важливо встановити вимоги до проектів NFT щодо інвестування у високоякісні аудити та використання ефективних практик безпеки. 

Ми твердо переконані, що ця вразливість зазвичай використовується в дикій природі під радаром інвесторів і спільнот NFT. Будь ласка, поділіться цією інформацією з якомога більшою кількістю проектів, щоб вони гарантували свою безпеку.

Ми хотіли б подякувати rugpullfinder @rugpullfinder допомога в дослідженні та перевірці вразливості з нами. Ми використали їхній досвід і знання у сфері 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

Детальна інформація про Degen Toonz

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

Написане
Авігдор Сасон Коен

Авігдор є дослідником безпеки web3 у Sayfer. Він захоплений новими технологіями блокчейну та тим, як ми можемо забезпечити їх безпечний розвиток.

 

Перейти до вмісту