משפחה חדשה של פגיעויות
ניצול חדש שנמצא על ידי חוקרי סייפר ו @rugpullfinder הצוות מאפשר לתוקפים לדעת מהו ה-NFT הנדיר ביותר לפני חשיפת הפרויקט. זה מאפשר לתוקף יתרון לא אחיד בקרב משקיעים לקנות את היצירה הנדירה והיקרה ביותר. בפרויקטים מסוימים, ניתן לתמחר את הנדיר ביותר פי 50 ממחיר החתיכה הסטנדרטי.
הפגיעות מתרחשת בגלל נוהלי קידוד גרועים. מצאנו את הפגיעות בעשרות פרויקטים. אולי קיימים באלפים שעדיין לא בדקנו ידנית.
אין דרך לבדוק את זה אוטומטית אז אם אתה רוצה לדעת אם פרויקט שאתה רוצה להשקיע בו או שכבר השקעתו בו, פגיע ל-BadReveal אנא תייגו אותנו בטוויטר @SayferSecurity עם #BadReveal והחוקרים שלנו יבדקו את זה.
משפיע על פרויקטים פעילים מרובים
כדי להבין את גודל הבעיה ניתחנו למעלה מ-100 פרויקטים שונים, ומצאנו ש-12 היו פגיעים למתקפה. אלה כמה מהפרויקטים הפגיעים:
CatBloxPUMACapsule (זמן ניצול: 2 ימים 18 שעות, שווי שוק: 628.19 ETH)
Everai Heroes: Duo (זמן ניצול: 4 ימים ושעתיים, שווי שוק: 2 ETH )
דגן טונץ (זמן ניצול: 3 ימים ושעתיים, שווי שוק: 13 ETH )
פרטים נוספים כמו זמני עסקה ספציפיים נמצאים ב- נִספָּח.
פרטים טכניים של הפגיעות
רקע
כמו NFTs ERC721 אסימונים, כל אסימון ייחודי עם מטא נתונים משלו. בדרך כלל, בפרויקט NFT עם שלב חשיפה, אסימונים מוטבעים בצורה עיוורת, וברגע שהכל הוטבע, NFTs נחשפים, והמטא נתונים שלהם הופכים לציבור. NFTs עם מטא נתונים נדירים יכולים להעריך פי 50 מ-NFTs אחרים בפרויקטים. הנדיר מכולם נקרא "האגדה".
אבל מה קורה אם תוקף יצליח איכשהו להשיג את המטא-נתונים לפני שהם נחשפה ומשתמש בנתונים האלה כדי לקנות את האגדה? התוקף מקבל הטבה לא הוגנת ומרוויח יותר מכל שאר הקונים על ידי ניצול הנתונים הללו לרכישת האגדה.
מהו URI אסימון?
URI האסימון של NFT הוא מזהה ייחודי של איך האסימון "נראה". URI יכול להיות כתובת HTTP, גיבוב IPFS, או אפילו כתם JSON שנשמר בשרשרת. זה מצביע על קובץ JSON שמכיל את המטא נתונים של האסימון.
בעת יצירת פרויקט ה-NFT שלך, עליך לוודא שכתובות URI אסימון אינן נגישות או ניתנות לניחוש לפני חשיפת ה-NFT. אחרת, זה עלול לאפשר לתוקפים להשיג שליטה על ה-NFTs היקרים ביותר בפרויקט. אכן, הם יכולים להשתמש ב-URI האלה כדי לבדוק כמה מאפיינים נדירים במטא נתונים לפני שמישהו יכול לראות אותם.
הפגיעות
מצאנו שפרויקטים רבים מגדירים את ה-URI האסימון בעסקה אחת ואז חושפים אותו בעסקה אחרת. בזמן שבין שתי העסקאות הללו, שלפעמים יכול להיות שעות, תוקף יכול לסרוק את כל ה-NFTs בפרויקט ולמצוא איזה מהם הוא הנדיר ביותר, ואז לקנות אותו על סמך ה-TokenID שלו.
דוגמה לניצול
כאן יש לנו דוגמה לפונקציה שמחזירה את URI האסימון אם reveal
הדגל הוא אמת (הוא שקר כברירת מחדל) או URI שמצביע על תמונה נסתרת המשותפת לכל אסימון אם הדגל הוא שקר.
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 נגיש לאחר חשיפת ה-NFTs. לפני כן, הייתה לנו רק תמונה גנרית אחת לכל 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.
מה קורה בחיים האמיתיים כשהבעלים רוצה לחשוף את ה-NFTs?
הבעלים יתחיל בהתקשרות requestRandomNumbers()
להקצות לו ערך אקראי randomNumber
. כי randomNumber
יש כעת ערך שאינו אפס, הבעלים יכול להתקשר revealNFT()
להפוך את reveal
הדגל ל-true, ואז כל ה-URIs פומביים tokenURI()
.
אבל זכרו שכדי לחזות את ה-URI, אנחנו צריכים רק ארבעה אלמנטים כולל שלושה ידועים ו-כבר randomNumber
שהוא גם ציבורי והערך שלו מוקצה מתי requestRandomNumbers()
נקרא.
אז בינתיים בין לבין requestRandomNumbers()
ו revealNFT()
נקראים, URIs אינם פומביים עדיין, אך יש לנו את כל האלמנטים לחזות אותם.
קיימת גרסה אחרת של פגיעות זו. למרות שכיח יותר, קשה יותר לנצל אותו. אם אין צעד גילוי, ה-baseURI נקבע לעתים קרובות בתחילת או באמצע שלב המנטה. אז ברגע ש-NFT מוטבע, הוא מתגלה, אבל האסימונים הנותרים נשארים לא ידועים. לאחר מכן נוכל לאחזר את ה-URI ולהשתמש בו כדי לדעת את ה-NFTs הנדירים ביותר מבין הנותרים. אז אתה צריך לנסות להטביע בזמן הנכון כדי למצוא אותם.
כפי שאמרנו לעיל, התוצאה היא שתוקפים יכולים להשיג שליטה ב-NFTs היקרים ביותר בפרויקט מכיוון שהם יכולים להשתמש ב-URI הללו כדי לבדוק כמה מאפיינים נדירים במטא נתונים לפני שמישהו יכול לראות אותם.
כדי למנוע את אותו סוג של פגיעויות, אתה יכול לבדוק את אלה אסטרטגיות אקראיות לירידות NFT מאת וויליאם אנטריק. זה יעזור לך לעשות אקראי URI של אסימון בצורה מאובטחת.
הקלות
אז מה עליך לעשות אם בבעלותך פרויקט NFT ואתה רוצה להפחית סיכונים ולהגן על בסיס הקוד מפני BadReveal?
מאחר שמהות הפגיעות של BadReveal מסתמכת על היתרון שיש לתוקף בין הגדרת עסקת ה-URI האסימון לעסקת הגילוי (או המנטה), ההקלה תהיה לשלב את השניים לעסקה אחת.
אם אתה מתכנן לחשוף את ה-NFTs, הגדר את 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 מכיוון שהוא אינו מציג את הפער בין עסקאות המאפשר לתוקף לקנות מוקדם לפני שקונים אחרים מקבלים את מידע המטא נתונים.
<br> סיכום
סיכוני סייבר של פרויקטי NFT יכולים ללבוש צורות וצורות רבות, זוהי אמונה נפוצה שאם ה-NFT שלך לא נגנב לא פרצו לך, אבל מה אם היית אמור לקבל את ה-NFT הנדיר ביותר בחפיסה, אלא תוקף היה חכם מספיק כדי לקנות אותו לפניך? פרצו לך אפילו בלי לדעת את זה.
חשוב להגדיר דרישות לפרויקטי NFT להשקיע בביקורות באיכות גבוהה ולהשתמש בשיטות אבטחה טובות.
יש לנו אמונה חזקה שהפגיעות הזו מנוצלת בדרך כלל בטבע מתחת לרדאר של המשקיעים והקהילות של ה-NFTs. אנא שתף מידע זה עם כמה שיותר פרויקטים כדי להבטיח את אבטחתם.
ברצוננו להודות מיכל שטיחים @rugpullfinder את העזרה בחקירה ואימות הפגיעות איתנו. השתמשנו במומחיות ובידע שלהם בתחום ה-NFT כדי להעמיק את חור הארנב של BarReveal
נספח - פרויקט מושפע
הפרטים של CatBloxPUMACapsule
https://etherscan.io/address/0x8b8D1225bB21CA07812FF3c2ee9358f7b5d90EcA
- אין קריאת setBaseURI()
- BaseURI נוכחי מוגדר בקונסטרוקטור
- Jun-04-2022 04:29:09 AM +UTC → יצירת חוזה
- יוני-06-2022 10:14:21 +UTC → NFT האחרון שהוטבע
Everai Heroes: הפרטים של Duo
https://etherscan.io/address/0x9a38dec0590abc8c883d72e52391090e948ddf12
- Mar-25-2022 11:06:27 +UTC → יצירת חוזה
- Mar-25-2022 11:11:02 PM +UTC → 1st Set BaseUri שיחת BaseUri
- Mar-30-2022 01:21:45 AM +UTC ← NFT האחרון שהוטבע
- Apr-01-2022 07:58:31 PM +UTC → 2nd Set BaseURI שיחת BaseURI
- Apr-03-2022 08:32:22 PM +UTC → 3rd Set BaseURI call
פרטים של דגן טוונז
https://etherscan.io/address/0x19b86299c21505cdf59ce63740b240a9c822b5e4
- פברואר 19-2022 12:46:19 +UTC ← שיחת BaseURI 1
- פברואר 21-2022 03:03:48 AM +UTC → 2nd Set BaseURI שיחת BaseURI
- 23-2022-01 38:10:XNUMX +UTC ← ה-NFT האחרון שהוטבע
- פברואר 23-2022 04:23:54 +UTC → שיחת BaseURI 3rd Set
- פברואר 23-2022 07:38:38 AM +UTC → שיחת BaseURI הרביעית
- מאי-01-2022 03:46:53 AM +UTC → שיחת BaseURI החמישית
- May-01-2022 09:57:31 PM +UTC → שיחת BaseURI ה-6
רוצה לשמוע עוד?
כולל פגישת ייעוץ חינם.