ברוכים הבאים למדריך למתחילים לביקורת חוזים חכמה! אחת הדרכים הטובות ביותר להתחיל עם ביקורת חוזים חכמים היא לקפוץ ולהסתכל על כמה סוגים נפוצים של פגיעות בחוזים חכמים.
זה יעזור אם כבר יש לך הבנה בסיסית של שפת התכנות Solidity של Ethereum. כפי שנסתכל על כמה מהקודים שנכתבו על ידי מתכנתי Noob solidity.
מתקפת כניסה חוזרת
תרחיש העולם האמיתי:
תאר לעצמך שיש לך 50 שוקולדים. יש לך אחות קטנה ושובבה שאפשרת לקחת ממך רק 2 שוקולדים בכל פעם. אתה גם לא רוצה לתת לה יותר מ-10 שוקולדים ביום, מחשש שהיא תקבל עששת. כדי להבטיח זאת, כל ערב אתה סופר כמה שוקולדים נשארו איתך. אתה חושב שזה יעבוד? או שאחותך הקטנה תפרוץ לך?
למרבה הצער, זה לא יעבוד! אחותך מבינה שאתה לא מודע לכמה שוקולדים יש לך עד שעת ערב. אז כבר למחרת, אחותך הקטנה מבקרת אותך 6 פעמים לפני הערב ולוקחת 2 שוקולדים בכל פעם! זה מה שאנו מכנים מתקפת כניסה מחדש.
כאן אתה מעדכן את ספירת השוקולדים שיש לך בערב במקום לעדכן את הספירה בכל פעם שאחותך לוקחת ממך 2 שוקולדים. זה גם מה שקורה עם חוזה חכם. החוזה החכם מניח איזון מסוים בזמן שהתוקף עסוק למעשה במשיכה של כמות מסוימת של קריפטו מהחוזה מספר פעמים.
דוגמה לקוד בעולם האמיתי:
קוד זה שייך לחוזה חכם שנקרא לא ממומן. כל אחד יכול למשוך אתר מחוזה Unbanked כל עוד יתרות ה-msg.sender (כלומר המתקשר של withdraw
function ) גדול או שווה לסכום שהתבקש למשוך.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
שימו לב שיש מילת מפתח שיחה המשמשת לשליחת הכמות הנדרשת של אתר אל msg.sender
. תוקף יכול לנצל זאת על ידי יצירת חוזה בשם Thief שבו הוא קורא לפונקציית הנסיגה ב-a fallback()
פוּנקצִיָה. א fallback()
פונקציה ב-Solidity היא פונקציה מיוחדת שמתבצעת כאשר האתר נשלח לחוזה החכם.
זה אומר שתוקף מסוגל קרא רקורסיבית לפונקציית הנסיגה. לפיכך, לפני שהחוזה החכם מתעדכן, היתרות של msg.sender
בשורת הקוד האחרונה, התוקף כבר משך את האתר מספר פעמים. ניתן להימנע מכך אם היתרות מתעדכנות לפני השימוש במילת המפתח שיחה, ובכך בעקבות א בדיקות-אפקטים-אינטראקציות דפוס.
השפעה:
אל האני מתקפת הכניסה מחדש הראשונה אי פעם קרה ב-2016 ב-DAO (ארגון אוטונומי מבוזר) שהביא לפריצות של כ-50 מיליון דולר. כדי להפוך את הפריצה הזו, קהילת Ethereum פיצלה את הבלוקצ'יין של Ethereum שהוליד את ETC (Ethereum Classic) ו-ETH (Ethereum).
הצפה אריתמטית ואריתמטית
תרחיש העולם האמיתי:
בואו נשחק משחק מחשבה. הוא מורכב מ-Spin-the wheel, והמנצח נקבע על סמך המספר הגדול ביותר שהוא מסוגל לקבל על סיבוב הגלגל. הגלגל מסומן בכל רחבי מ-256 עד -256.
החוקים למשחק הם שהמצביע עבור כל השחקנים נשען על 0 בתחילת כל סיבוב. ולשחקן מותר להסתובב רק בכיוון של מספרים שליליים. איך תנצח את המשחק הזה?
אסטרטגיה טובה לנצח במשחק הזה בכל פעם תהיה לסובב את הגלגל בעוצמה כזו שהגלגל מסתובב עד -256 ואז יהפוך ל-256 במכה אחת. זה אפשרי כי 256 מגיע מיד אחרי -256 על ההגה. לזה אנחנו קוראים זרימה אריתמטית. והצפה אריתמטית היא רק להיפך מזה.
דוגמה לקוד בעולם האמיתי:
An הצפה או הצפה קורה כאשר פעולת אריתמטית מגיעה למינימום או המקסימום שלה.
function withdraw(uint _amount) public { require(balances[msg.sender] - _amount > 0); address payable to = payable(msg.sender); to.transfer(_amount); balances[msg.sender] -= _amount;
}
אל האני _amount
הפרמטר של פונקציית המשיכה הוא מספר שלם ללא סימן. הערך של מיפוי האיזונים (שהוא כמו מילון ב-python או זוג מפתח-ערך ב-C++ או Java) הוא גם מספר שלם ללא סימן.
mapping(address => uint256) public balances
ההצהרה הנדרשת בודקת האם יתרות של msg.sender
חיובי או לא. אבל הצהרה זו תמיד תהיה נכונה גם אם הסכום גדול מהיתרות של msg.sender
. הסיבה לכך היא שגם ה balances
ו _amount
משתנים הם מסוג מספר שלם ללא סימן והתוצאה האריתמטית שלהם (לאחר זרימה תחתית) תהיה גם מספר שלם ללא סימן!
וכזכור, מספר שלם ללא סימן הוא תמיד חיובי. המשמעות היא שתוקף יכול למשוך כמות בלתי מוגבלת של אתר מהחוזה החכם! אתה יכול למצוא דוגמה מפורטת וקוד יישום עבור פגיעות זו כאן.
דבר נוסף שחשוב לציין כאן הוא שהפעולה האריתמטית בין נניח שני מספרים שלמים ללא סימן היא גם מספר שלם ללא סימן. זה יכול להיות מסוכן אם מתעלמים מכך בחוזים חכמים, שכן זה יכול לגרום לפרצות אבטחה לא רצויות!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
כפי שאולי שמתם לב בדוגמה לעיל, ההצהרה אם היא די חסרת טעם כמו upvote - downvote
תמיד יהיה חיובי. והפוסט יימחק גם אם downvotes
גדול מ upvotes
. כדי להימנע מהתקפות כאלה, מומלץ להשתמש בגרסת מהדר Solidity גדולה מ- 0.8.0.
השפעה:
מטבע שנקרא מטבע PoWH הושק בשנת 2017. למרות שזה היה משחק פונזי, הוא עצמו נפרץ בגלל באג אריתמטי שהביא לאובדן של כ-866 ETH או $950,000 באותה תקופה. אתה יכול לקרוא על זה בפירוט כאן.
חייב לקרוא: לקחים מההתקפה על Tinyman, ה-DEX הגדול ביותר על Algorand
התקפת מניעת השירות
תרחיש העולם האמיתי:
תאר לעצמך שאתה באוניברסיטת ביטקוין טק. הכל נראה בסדר חוץ מזה שיש שולחן אוכל משותף לכולם. ולמרבה הצער, יש מעט אנשים מכיתה אחרת שתמיד מצליחים לכבוש את שולחן האוכל לפני מישהו מהכיתה שלך.
בתרחיש המעשי, הם שוללים את השירות החיוני לכולם וכתוצאה מכך אובדן זמן יקר. זה מה שאנו מכנים 'התקפת מניעת שירות'.
דוגמה לקוד בעולם האמיתי:
במשחק שנקרא מלך האתר, כל אחד יכול להפוך למלך. אבל הכלל כדי להפוך למלך הוא שאדם צריך להפקיד יותר אתר מהמלך הנוכחי. ניתן לעשות זאת על ידי התקשרות ל- claimThrone()
תפקיד של חוזה מלך האתר שבו האדם שולח אתר ישירות למלך הקודם והופך למלך החדש.
function claimThrone() external payable { require(msg.value > balance, "Need to pay more to become the king"); (bool sent, ) = king.call{value: balance}(""); require(sent, "Failed to send Ether"); balance = msg.value; king = msg.sender; }
כפי שאולי ניחשתם, הקוד הזה פגיע להתקפת DoS, אבל איך? לשם כך, תצטרך להבין שיש שני סוגים של כתובות ב-Ethereum - הראשון הוא ה כתובת של חיצונית חשבון בבעלות או פשוט כתובת של ארנק, והשנייה היא כתובת חוזה. כעת ניתן לשלוח את האתר מכל אחד מסוגי הכתובות הללו.
אם זה, האתר נשלח על ידי כתובת החוזה, אז החוזה יהפוך למלך. אבל בואו נניח שלחוזה החדש הזה אין א fallback()
פונקציה הכרחית אם החוזה רוצה לקבל אתר. ואז אם יבוא אדם חדש וינסה להתקשר ל claimThrone()
פונקציה, זה תמיד ייכשל!
שימו לב שזה קורה גם בגלל ה claimThrone()
הפונקציה בודקת במפורש אם העברת האתר הצליחה או לא בהצהרה הנדרשת השנייה. אתה יכול למצוא את הקוד המלא ולבצע עליו התקפת DoS כאן.
ייתכן גם שקוד יהיה פגיע להתקפת DoS אם לקוד יש לולאה על מערך של גדלים גדולים. זה קורה בגלל ש מגבלת גז ניתן לחרוג במקרים כאלה. אתה יכול לקרוא על זה כאן.
השפעה:
משחק שנקרא מֶמשָׁלתִי, שככל הנראה הייתה תוכנית פונזי, נתקעה עם 1100 ether כי נדרשה כמות גדולה של גז כדי לעבד את התשלום.
אקראיות לא בטוחה
תרחיש העולם האמיתי:
פעם היה אדם בשם הסקי שתמיד היה מלווה בקוף שלו פסקי. חסקי ערך משחקי הגרלה והרוויח יפה. יום אחד אליס הבחינה בהסקי בוהה בריכוז בקוף שלו פסקי. ואז היא ראתה אותו כותב משהו על פיסת נייר ואטמה אותו במעטפה. סקרנית, היא החליטה לחקור עוד.
מאוחר יותר באותו ערב, אליס ראתה שהזוכה בהגרלה הוכרעה על ידי פתיחת המעטפה הסגורה בפומבי. לאחר שצפתה בו במשך כמה ימים, אליס הבינה שהסקי החליט על מספר הזוכה בלוטו על ידי התבוננות במחוות של פסקי (לדוגמה, אם הקוף גירד בראשו, האסקי רשם 10)! כעת לאליס הייתה הנוסחה לזכות בכל לוטו ורק הייתה צריכה לקנות את כרטיס הלוטו עם המספר הנכון!
הסקי הניח שלעולם לא ניתן להבין את הדרך ה"אקראית" שלו להכריע את הזוכה בלוטו, אבל הוא אכן טעה.
דוגמה לקוד בעולם האמיתי:
בדוגמה זו, מספר אקראי נוצר על סמך ה-hash של השילוב של מספר הבלוק וחותמת הזמן של הבלוק שלו. הגיבוב הזה מוקצה לאחר מכן למשתנה התשובה. עכשיו כל מי שמנחש את המספר האקראי הזה (לכאורה), מתוגמל 1 את'ר. אתה חושב שזה בלתי ניתן לפריצה?
function guess(uint _guess) public { uint answer = uint( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); if (_guess == answer) { (bool sent, ) = msg.sender.call{value: 1 ether}(""); require(sent, "Failed to send Ether"); } }
לא! תוקף עדיין יכול לנחש את המספר האקראי הזה על ידי העתקה של הדבקת הקוד כדי ליצור את הערך שהוקצה למשתנה התשובה, ולהעביר את אותו משתנה תשובה ל- guess()
פוּנקצִיָה!
guessTheRandomNumber.guess(answer);
תוכל למצוא את הקוד המלא כאן. כדי להימנע מתקפה זו, מומלץ להשתמש בפונקציה אקראית לאימות כמו ה- Chainlink VRF.
השפעה:
כ-400 ETH אבדו עקב התקפה על הגרלת מיליארדים חכמים חוֹזֶה. באופן מפתיע, אפילו הגרלת החוזה עצמה הייתה מזימת פונזי (אוי!).
מניפולציה בזמן
תרחיש העולם האמיתי:
סאטושי אוהב לאכול עוגיות. הוא אוהב את כל סוגי העוגיות שאמא שלו מכינה. אבל אמא שלו מאוד קפדנית ומרגישה שלאכול יותר מדי עוגיות לא טוב לו. אז אמא שלו קובעת שהוא יקבל את העוגיות רק בשמונה בערב.
באותו יום ממש בשעה 7:45, סאטושי רץ לאמו ומבקש עוגיות. אמו שואלת- "מה השעה?"
"השעה היא 8!" - הוא עונה.
"בסדר. אז קח את העוגיות מהארון שלי."
וכך, סאטושי הצליח לתמרן את הזמן בהצלחה ב-15 דקות כדי שיוכל לקבל את העוגיות שלו! איזה בחור רעב לעוגיות!
דוגמה לקוד בעולם האמיתי:
ניתן לתפעל את חותמת הזמן של בלוק על ידי בערך 15 שניות על ידי כורה. בדרך זו, כורה יכול לקבוע חותמת זמן חיובית ולכלול את העסקה שלו באותו בלוק שהוא כורה. הפונקציה play()
שייך לחוזה משחק בשם G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
חוזה זה מתגמל 1500 אתר לשחקן שהוא הראשון להתקשר לפונקציית המשחק. אבל כפי שאתה יכול לראות, ניתן לקרוא לפונקציית ההפעלה רק אם ה- now או block.timestamp של העסקה המכילה את הקריאה ל- play()
הפונקציה, גדולה מה- זמן תקופה 1640392200.
כורה יכול לתפעל בקלות חותמת זמן זו ולכלול את העסקה שלו של קריאה ל- play()
לתפקד באותו בלוק כך שהוא עצמו השחקן הראשון. בצורה זו מובטח שהכורה ינצח במשחק!
השפעה:
ה-block.timestamp שימש ליצירת מספרים אקראיים ב- ממשלתי ובכך היה פגיע להתקפות של מניפולציות בזמן.
פנה ל- QuillAudits
QuillAudits היא פלטפורמת ביקורת חוזים חכמה מאובטחת שתוכננה על ידי QuillHash
טכנולוגיות.
זוהי פלטפורמת ביקורת שמנתחת ומאמתת חוזים חכמים בקפדנות כדי לבדוק פרצות אבטחה באמצעות סקירה ידנית יעילה עם כלי ניתוח סטטיים ודינמיים, מנתחי גז וכן מטמיעים. יתרה מכך, תהליך הביקורת כולל גם בדיקות יחידות מקיפות וכן ניתוח מבני.
אנו עורכים גם ביקורת חוזים חכמות וגם מבחני חדירה כדי למצוא פוטנציאל
פרצות אבטחה שעלולות לפגוע בשלמות הפלטפורמה.
אם אתה זקוק לסיוע כלשהו בביקורת החוזים החכמים, אל תהסס לפנות למומחים שלנו כאן!
כדי להיות מעודכנים בעבודה שלנו, הצטרף לקהילה שלנו:-
טויטר | לינקדין | פייסבוק | מברק
ההודעה מדריך למתחילים לביקורת חוזים חכמה: חלק 1 הופיע לראשונה ב בלוג Quillhash.
מקור: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- אודות
- חֶשְׁבּוֹן
- כתובת
- תעשיות
- כְּבָר
- למרות
- אנליזה
- בדיקה
- אוטונומי
- התחלה
- הטוב ביותר
- ביטקוין
- blockchain
- חרק
- לִקְנוֹת
- שיחה
- מקרים
- בדיקות
- קלאסי
- קוד
- מטבע
- שילוב
- Common
- קהילה
- מכיל
- חוזה
- חוזים
- עוגיות
- יכול
- יוצרים
- קריפטו
- נוֹכְחִי
- DAO
- יְוֹם
- מבוזר
- מניעת שירות
- פרט
- דקס
- מטה
- בקלות
- לאכול
- ETH
- אתר
- ethereum
- הבלוק
- אתריום קלאסי
- דוגמה
- לנצל
- פייסבוק
- סוף
- ראשון
- חופשי
- פונקציה
- מִשְׂחָק
- משחקים
- גז
- ליצור
- GitHub
- הולך
- טוב
- מדריך
- לפרוץ
- פריצות
- שירים
- ראש
- כאן
- איך
- HTTPS
- לחקור
- IT
- Java
- להצטרף
- לקפוץ
- המלך
- שפה
- גָדוֹל
- קו
- לינקדין
- ארוך
- הסתכלות
- הגרלה
- איש
- מִילִיוֹן
- אמא
- מספרים
- ארגון
- מאמר
- תבנית
- תשלום
- אֲנָשִׁים
- לְחַבֵּר
- פלטפורמה
- לְשַׂחֵק
- שחקן
- פונזי
- Ponzi Scheme
- כּוֹחַ
- תהליך
- מתכנתים
- תכנות
- ציבורי
- להפוך
- סקירה
- תגמולים
- כללי
- סטושי
- אבטחה
- סט
- חכם
- חוזה חכם
- חוזים חכמים
- So
- מוּצָקוּת
- משהו
- לְסוֹבֵב
- לפצל
- החל
- הצהרה
- אִסטרָטֶגִיָה
- מוצלח
- בהצלחה
- טק
- בדיקות
- דרך
- זמן
- כלים
- עסקה
- לא ממומן
- אוניברסיטה
- עדכונים
- ערך
- פגיעויות
- פגיעות
- פגיע
- ארנק
- מה
- גַלגַל
- מי
- לנצח
- תיק עבודות
- כתיבה