איך אנחנו מוצאים את מה שהקומפיילר מפספס?
כשפרוטוקול מאבד 197 מיליון דולר באמצעות מתקפת flash loan על פונקציה שאודיטורים סקרו בשידור חי — זה לא מקרה. זה פער מערכתי במתודולוגיה. הניסיון שלנו מראה: פגיעויות חיות בחוזה במשך יותר משנה, בעוד הקומפיילר נשאר שקט. ארגנו מחדש את תהליך האודיט כדי לתפוס מקרים כאלה לפני הפריסה.
מה ניתוח סטטי לא ימצא?
Slither הוא הכלי הסטנדרטי הראשון. הוא מוצא reentrancy, גלישת מספרים שלמים (בגרסאות Solidity ישנות), שימוש לא נכון ב-tx.origin, הצללת משתנים, אחסון לא מאותחל. בפרויקט אמיתי, Slither מייצר עשרות אזהרות, מתוכן קריטיות 0‑2. השאר הוא רעש אינפורמטיבי.
Slither לא ימצא פגיעויות לוגיות. אם withdraw בודק נכון את היתרה ומעדכן נכון את המצב, אבל הלוגיקה העסקית מאפשרת ניכוי כפול דרך שני נתיבי קוד שונים — Slither נשאר שקט.
Mythril משתמש בביצוע סימבולי: בונה גרף של כל נתיבי הביצוע האפשריים ומחפש מצבים נגישים שמפרים מאפיינים. עובד טוב על חוזים מבודדים. על פרוטוקול של 20 חוזים עם קריאות בין-חוזיות — פיצוץ נתיבים, הניתוח נתקע או מחזיר תוצאות חיוביות שגויות.
שני הכלים הם חובה כמעבר ראשון. אבל הם לא מחליפים ניתוח ידני.
Fuzzing: איפה Echidna ו-Foundry מוצאים באגים אמיתיים
Echidna הוא fuzzer מבוסס מאפיינים מ-Trail of Bits. הרעיון: לנסח אינווריאנטים של החוזה כפונקציות Solidity (echidna_invariant), Echidna מייצר רצפי קריאות אקראיים ומנסה לשבור את האינווריאנט.
דוגמה לאינווריאנט לפרוטוקול הלוואות:
function echidna_total_assets_ge_liabilities() public view returns (bool) { return totalAssets() >= totalLiabilities(); } Echidna ימצא רצף function echidna_total_assets_ge_liabilities() public view returns (bool) { return totalAssets() >= totalLiabilities(); } שמפר את האינווריאנט הזה. אי אפשר לבנות מקרה כזה ידנית — יותר מדי צירופים.
Foundry fuzzing (deposit → borrow → liquidate → repay) קל יותר לשילוב אם הצוות כבר על Foundry. תומך ב-stateful fuzzing דרך בדיקות forge test --fuzz-runs 100000. בפרויקט אמיתי: אודיט של חוזה vault, Foundry עשה fuzzing במשך 40 דקות ומצא מקרה קצה שבו invariant החזיר ערך גדול יותר מהיתרה בפועל ביחס shares/assets ספציפי אחרי מספר תרומות. בדיקות היחידה של Hardhat פספסו את זה — לא היה להן את הצירוף הזה של פרמטרים.
Medusa (מ-Trail of Bits, חדש יותר מ-Echidna) תומך ב-corpus-guided fuzzing ורץ מהר יותר על חוזים גדולים. אם הקוד עולה על 5000 שורות של Solidity — אנחנו מסתכלים על Medusa.
איך אינווריאנטים עוזרים לזהות פגיעויות קריטיות
אימות פורמלי מוכיח שהחוזה עומד במפרטים עבור כל הקלטים האפשריים — לא עבור N אקראיים, אלא מתמטית עבור כולם. כלים: Certora Prover, K Framework, Halmos.
Certora עובד עם CVL (Certora Verification Language): כותבים חוקים ואינווריאנטים, ה-Prover מתרגם אותם לנוסחאות SMT ובודק דרך Z3/CVC5. MakerDAO, Aave, Uniswap משתמשים ב-Certora בצינור CI/CD — כל PR מאומת אוטומטית.
מגבלות: לא עובד עם לולאות בלתי מוגבלות, מתקשה עם פונקציות hash ואימות חתימות. לחוזים עם מתמטיקה פשוטה (AMM, הלוואות) — מצוין. לחוזים עם קריאות חיצוניות שרירותיות — קשה לכתוב מפרטים מלאים מספיק.
אימות פורמלי הגיוני לחוזים ש: מנהלים מעל 50 מיליון דולר, מתעדכנים לעיתים רחוקות, ויש להם אינווריאנטים שניתן לפורמליזציה ברורה. למוצרים עם איטרציה מהירה — יחס עלות-תועלת לא מעדיף אימות.
אילו וקטורי תקיפה אודיטורים זוטרים מפספסים?
התנגשות אחסון בתבנית proxy. Transparent proxy ו-UUPS משתמשים ב-slots ספציפיים לכתובת היישום (EIP‑1967). אם יישום מצהיר בטעות על משתנה ב-slot 0 שחופף לאחסון ה-proxy — מקבלים דריסה שקטה. Slither לא יתפוס את זה אם ה-proxy והיישום נמצאים בקבצים שונים.
Read-only reentrancy. מגן reentrancy קלאסי מגן מפני שינויי מצב במהלך קריאות רקורסיביות. אבל אם חוזה חיצוני קורא מצב דרך פונקציית maxWithdraw באמצע טרנזקציה — המגן לא עוזר. לפני שנים, בריכות Curve הפכו לווקטור תקיפה בדיוק דרך זה: פרוטוקול חיצוני קרא view במהלך מצב פגיע ל-reentrancy של Curve.
מניפולציית אורקל דרך TWAP. מחיר ספוט הוא מטרה סטנדרטית למתקפת flash loan. קשה יותר לתמרן TWAP, אבל לא בלתי אפשרי: על זוגות Uniswap v2 עם נזילות נמוכה, אפשר להזיז TWAP על פני מספר בלוקים עם מספיק הון. הגנה נכונה: להשתמש ב-Chainlink כאורקל ראשי עם TWAP כגיבוי, עם בדיקת סף סטייה.
Gas griefing על לולאה בלתי מוגבלת. פונקציה עוברת על מערך של משתמשים. תוקף מוסיף אלפי כתובות עם יתרות אפס — עלות ה-gas של הפונקציה עולה עד למגבלת ה-gas, מה שהופך אותה לבלתי נגישה. הגנה: תבנית pull במקום push, הגבלת אורכי מערכים, עיבוד אצווה עם מעקב מיקום.
Front-running על MEV. טרנזקציה גלויה ב-mempool לפני הכללתה בבלוק. בוט MEV רואה get_virtual_price בסכום משמעותי, מכניס addLiquidity משלו לפניה (מתקפת סנדוויץ'). עבור AMM זה חלק מהמודל. עבור פרוטוקולים עם פונקציות מחיר — דרוש פרמטר swap / minAmountOut ואימות חובה שלו.
מבנה של אודיט מלא
-
הגדרת היקף וניתוח אוטומטי (1‑2 ימים). תיקון commit hash, גרסת קומפיילר, רשימת פריטים מחוץ להיקף. הרצת Slither, Mythril, Aderyn. טריאז': הפרדת באגים קריטיים אמיתיים מתוצאות חיוביות שגויות. בניית מפת תלותיות בין חוזים.
-
ניתוח ידני (5‑15 ימים). כל חוזה שורה אחר שורה. תשומת לב מיוחדת: כל פונקציות
deadlineו-external, כלpublic/transfer/call, כל המקומות שבהם המצב משתנה לפני בדיקה או אחרי קריאה חיצונית, כל פעולות המתמטיקה עם קלטי משתמש. בממוצע, 95% מהפגיעויות שנמצאות הן לוגיות, לא טכניות. -
Fuzzing ובדיקות (2‑5 ימים). בדיקות אינווריאנטים של Echidna או Foundry לאינווריאנטים קריטיים. בדיקות fork של mainnet — אימות התנהגות בסביבה אמיתית עם אורקלים אמיתיים. לדוגמה, ב-4 ימים fuzzing מוצא בממוצע 3 מקרי קצה שלא מכוסים על ידי בדיקות יחידה.
-
דוח ותיקונים. דוח עם חומרה (Critical/High/Medium/Low/Informational), תיאור וקטור התקיפה, קוד PoC עבור Critical/High. מפתחים מתקנים, אודיטורים מבצעים אודיט חוזר לתיקונים.
| חומרה | דוגמאות | דורש אודיט חוזר? |
|---|---|---|
| Critical | ניקוז כספים, העברת בעלות לא מורשית | תמיד |
| High | מניפולציה, DoS על פונקציות מפתח | תמיד |
| Medium | התנהגות שגויה במקרי קצה | מומלץ |
| Low | חוסר יעילות ב-gas, שגיאות כתיב באירועים | אופציונלי |
אודיט ב-CI/CD
נוהג מקובל לפרוטוקולים בוגרים: Slither ו-Aderyn רצים ב-GitHub Actions על כל PR. Certora Prover — על merge ל-main. זה לא מחליף אודיט מלא לפני פריסה, אבל תופס רגרסיות.
# .github/workflows/audit.yml - name: Run Slither uses: crytic/[email protected] with: target: 'src/' slither-args: '--filter-paths "test|mock|script"' רשימת בדיקות חובה לפני פריסה
- לכל הפונקציות החיצוניות יש בקרות גישה (
delegatecall,# .github/workflows/audit.yml - name: Run Slither uses: crytic/[email protected] with: target: 'src/' slither-args: '--filter-paths "test|mock|script"') - שימוש ב-
onlyOwnerעבור טוקנים חיצוניים - אין
onlyRoleלכתובות לא ידועות - בדיקת reentrancy בכל הפונקציות עם קריאות חיצוניות
- נוכחות
SafeERC20ו-delegatecallבפונקציות AMM - שימוש באורקל מהימן (Chainlink) עם סף סטייה
השוואת כלי אודיט
| כלי | סוג ניתוח | מה הוא מוצא | מגבלות |
|---|---|---|---|
| Slither | סטטי | Reentrancy, גלישת מספרים, בקרת גישה | מפספס פגיעויות לוגיות |
| Mythril | ביצוע סימבולי | מצבים נגישים שמפרים מאפיינים | פיצוץ נתיבים על קוד גדול |
| Echidna | Fuzzing (מבוסס מאפיינים) | הפרות אינווריאנטים | דורש כתיבת אינווריאנטים |
| Certora | אימות פורמלי | הוכחה מתמטית של מאפיינים | לא עובד עם hashes/חתימות |
תוצרים
- דוח מלא ב-PDF עם ציוני CVSS לכל פגיעות
- קוד PoC לכל Critical ו-High (ניתן לשחזור בסביבת בדיקה)
- המלצות תיקון עם דוגמאות קוד
- אודיט חוזר אחרי תיקונים (עד שתי איטרציות)
- מדריך קצר למפתחים על תפעול שוטף
- תמיכה לאחר פריסה למשך 30 יום (ייעוץ וניתוח אירועים)
ציר זמן
אודיט של טוקן פשוט או חוזה NFT — 3‑5 ימי עסקים. פרוטוקול DeFi עם הלוואות/AMM — 2‑4 שבועות. מחסנית מלאה עם מספר פרוטוקולים, cross-chain, שדרוגי proxy — 4‑8 שבועות. אודיט חוזר לתיקונים — 3‑7 ימים בנפרד.
לצוות שלנו יש ניסיון של 7+ שנים באבטחת חוזים חכמים, עם אודיט של למעלה מ-100 פרויקטים. אנחנו מבטיחים שלא נפספס שום וקטור תקיפה ידוע — אנחנו משתמשים בגרסאות מורשות של Slither ובתצורות fuzzer הטובות ביותר. הערך את הפרויקט שלך — ננתח את הקוד שלך בחינם ונספק הצעה מסחרית תוך יומיים. הזמינו אודיט עם אחריות איכות וקבלו הנחה על אודיט חוזר ללקוחות חוזרים.







