ניתוח טכני של פריצת גשר Wormhole: ניצול בשווי 326 מיליון דולר
ב-2 בפברואר 2022, גשר האסימונים של Wormhole איבד 120,000 wETH — כ-326 מיליון דולר באותו זמן — באחת מניצולי החוזים החכמים הגדולים בהיסטוריה של DeFi. ההתקפה לא דרשה גניבת מפתחות, הנדסה חברתית או שליטה ברוב רשת ה-Guardian. היא דרשה דבר אחד: ממשק API ישן של תוכנית Solana שלא בדק מאיזו חשבון היא קוראת.
הבנת ניצול זה חיונית לכל צוות הבונה על Solana, לכל פרוטוקול המתבסס על העברת הודעות בין שרשראות, ולכל מבקר שמבצע סקירה של תוכניות Solana. שורש הבעיה היה תקלה בבדיקת חוזה חכם בתוכנית הגשר של Wormhole ב-Solana — לא פרצת מפתחות, ולא כשל ברשת Guardian. מפתחות ה-Guardian מעולם לא נגעו בהם. הקוד של הגשר העניק לתוקף הרשאה שמעולם לא היה אמור לקבל.
מה הייתה הפרצה הטכנית המרכזית בפריצת Wormhole?
הפרצה הייתה בהוראת verify_signatures בתוכנית הגשר של Wormhole ב-Solana (bridge.so). פונקציה זו הייתה אחראית לאמת שחשבון SignatureSet — המכיל חתימות Ed25519 או Secp256k1 של צמתים ברשת Guardian — מייצג הסכמה אמיתית לפני אישור פעולה באמצעות Verifiable Action Approval (VAA).
התקלה: verify_signatures השתמשה בפונקציה מיושנת solana_program::sysvar::instructions::load_instruction_at כדי לוודא שהוראת Secp256k1 לפנים התרחשה באותו טרנזקציה. הממשק הישן אינו בודק שהחשבון שהועבר אליו הוא אכן חשבון ה-instructions sysvar של Solana (Sysvar1nstructions1111111111111111111111111). היא קוראת מכל חשבון שנמצא באינדקס הנקרא — כולל חשבון שנשלט כולו על ידי התוקף.
לכן, תוקף יכול להעביר חשבון מזויף המדמה את מבנה החשבון של ה-instructions sysvar, מלא מראש בנתוני חתימה הנשלטים על ידו מעל payload שנבחר בידי התוקף. הפונקציה קוראת את הנתונים המזויפים, מוצאת את קריאת Secp256k1 “נוכחת” (בהקשר שונה לחלוטין), מסמנת את ה-VAA כאושר Guardian ומחזירה הצלחה.
עם VAA המאושר במרמה, התוקף קרא ל-complete_wrapped, שמאמין באישור ה-VAA של תוכנית הגשר כדי לאשר יצירת נכסים עטופים (wrapped) ב-Solana. התוצאה: 120,000 wETH נוצרו ללא חסימת ETH מקביל ב-Ethereum.
תיקון — החלפת load_instruction_at ב-load_instruction_at_checked, שמאמתת שהחשבון הוא אכן ה-instructions sysvar — הועלה למאגר הקוד של Wormhole בגיטהאב לפני הניצול אך טרם הונח על הרשת הראשית.
| גורם | תיאור | השפעה |
|---|---|---|
API sysvar מיושן |
שימוש ב-load_instruction_at ללא הגרסה _checked; ללא אימות בעלות על החשבון באינדקס הנקרא |
התוקף יכול לספק חשבון הוראות מזויף במקום החשבון האמיתי |
עקיפת verify_signatures |
הפונקציה קיבלה SignatureSet מזויף כאישור להסכמת Guardian מבלי לאמת את מקור החשבון |
ניתנה הרשאת מינטינג (יצירת אסימונים) ללא חתימות Guardian אמיתיות |
חוסר באימות בעלות על החשבון |
חשבון SignatureSet לא אומת כבעלות של תוכנית ה-instructions sysvar (Sysvar1nstructions11...) |
חוסר אימות זה הוא הבסיס כולו לניצול |
הרשאת יצירת נכסי wrapped |
לתוכנית הגשר ב-Solana הייתה הרשאת מינטינג בלתי מוגבלת על אסימונים עטופים; ההרשאה דרשה רק VAA שסומן כאושר | נוצרו 120,000 wETH ללא חסימת ETH ב-Ethereum; שווי של כ-326 מיליון דולר במועד ההתקפה |
כיצד ניצול Wormhole שונה מהתקפות גשר אחרות כגון Ronin?
פריצת גשר Ronin (מרץ 2022, כ-625 מיליון דולר) מזוהה לעיתים עם Wormhole בדיונים על כשלים באבטחת גשרים. אלו מצבים שונים במהותם.
Wormhole הייתה באג באימות חוזה חכם בתוכנית הגשר של Solana. רשת Guardian — מערכת חתימה ברף עם 19 צמתים — לא נפרצה. התוקף עקף את הרשת בשליפה ישירה כי verify_signatures מעולם לא בדקה מאיזה חשבון היא קוראת. מפתחות Guardian נשארו שלמים, הרשת הייתה פעילה, והקוד פשוט לא התייעץ עם ההסכמה האמיתית.
Ronin הייתה גניבת מפתחות תפעולית. Sky Mavis הפעלה חמישה מצמתיי האימות מתוך תשעה ברשת Ronin. בנובמבר 2021, Axie DAO העבירה סמכות חתימה זמנית ל-Sky Mavis כדי לטפל בנפח העסקאות. ההסמכה לא בוטלה בתום תוקפה. תוקף — שיוחס ל-Lazarus Group — פרץ למערכות הפנימיות של Sky Mavis והשיג גישה לארבעת מפתחות צמתים. בעזרת ההסמכה הפעילה של Axie DAO, הם קיבלו חתימה חמישית באמצעות RPC של Sky Mavis ללא תשלום גז. עם חמש מתוך תשע חתימות, הם הגישו בקשות משיכה לגשר Ronin. לא נוצל שום באג חוזה חכם. הגשר ביצע את מה שתוכנן — אישר משיכות עם חתימות תקפות.
| Wormhole (פברואר 2022) | Ronin (מרץ 2022) | |
|---|---|---|
| וקטור התקפה | עקיפת אימות חשבון בתוכנית Solana | הנדסה חברתית וגניבת אישורים של צמתים ב-Sky Mavis |
| שיטת ניצול | יצירת חשבון SignatureSet מזויף דרך API sysvar מיושן |
משיכות חוקיות חתומות ממפתחות צמתים גנובים |
| רשת Guardian / מאמתים | שלמה; עוקפה על ידי הקוד, לא על ידי התוקף | נפרצה — תוקף החזיק ב-5 מתוך 9 מפתחות |
| שיטת השפעה | יצירת אסימונים בלתי מורשים ב-Solana ללא חסימת ETH ב-Ethereum | משיכה ישירה מגשר Ronin דרך חתימות חוקיות |
| סכום שנגנב | כ-326 מיליון דולר (120,000 wETH) | כ-625 מיליון דולר (173,600 ETH + 25.5 מיליון USDC) |
| תובנות אבטחה | אימות בעלות על חשבון הוא חובה בתוכניות Solana; חתימות רף עובדות רק אם הגשר קורא אותן באמת | מערכות חתימת רף אינן מגנות מפני גניבת מפתחות רוב; יש לאכוף ביטול הסמכה |
שלבי ביצוע הניצול של Wormhole
-
זיוף חשבון. התוקף יצר חשבון Solana המדמה את מבנה החשבון הצפוי במיקום
SignatureSetבנתוני ההנחיות של תוכנית הגשר. חשבון זה הכיל חתימות הנשלטות על ידי התוקף — חתימות Ed25519/Secp256k1 תקפות, אך מעל payload בבחירת התוקף המאשר יצירת 120,000 wETH לכתובת Solana של התוקף. -
עקיפה בעזרת API sysvar מיושן. התוקף שלח טרנזקציה שקראה לפונקציית
verify_signaturesבתוכנית גשר Wormhole ב-Solana. התוכנית קראה ל-load_instruction_at— הגרסה הישנה והלא מאומתת — כדי לוודא שקריאה לספק Secp256k1 התרחשה מוקדם יותר באותה טרנזקציה. פונקציה זו קוראת לכל חשבון באינדקס המוגדר ללא אימות שזהו חשבון ה-instructions sysvar האמיתי. -
קבלת הוכחה מזויפת.
verify_signaturesסרקה את החתימות שבחשבון הנשלט על ידי התוקף, מצאה אותן תקפות קריפטוגרפית (התוקף עצמו חתם עליהן, מעל ה-payload שלו), וסימנה את ה-VAA כאושר Guardian. רשת Guardian לא נשאלה. -
הרשאת יצירת אסימונים. התוקף קרא ל-
complete_wrappedעם VAA המאושר החדש. תוכנית הגשר, שסמך על מצב האישור של ה-VAA, יצרה 120,000 wETH ב-Solana לכתובת התוקף. -
הפקת נכסים בין שרשראות. התוקף העביר את ה-wETH שנוצר חזרה ל-Ethereum דרך זרימת הגשר החוקית של Wormhole — ה-wETH היה בארנק Solana של התוקף, ולכן הגשר עיבד את המשיכה כהעברה רגילה. 326 מיליון הדולרים של wETH ללא גיבוי הגיעו ל-Ethereum, שם התוקף המיר אותם ל-ETH ומטבעות יציבים ויצא. חברת האם של Wormhole, Jump Crypto, השיבה את ה-120,000 ETH הגנובים מהמלאי שלה למען משתמשי הגשר.
לקחים טכניים קריטיים לתוכניות Solana וגשרים בין שרשראות
1. כל AccountInfo הנכנס לתוכנית Solana נשלט על ידי התוקף עד שיוכח אחרת.
זהו העיקרון הבסיסי באבטחת תוכניות Solana. על התוכנית לאמת את owner, key — וכאשר רלוונטי — גם executable ו-is_signer של כל חשבון שמתקבל. פונקציית load_instruction_at המיושנת קיבלה כל חשבון שהונח באינדקס בלי לבדוק בעלות. החלופה המודרנית, load_instruction_at_checked, מאמתת שהחשבון הוא אכן ה-instructions sysvar לפני הקריאה. מתודולוגיית הביקורת של Soken מסמנת כל שימוש ב-API sysvar לא-בדוק כנמצא קריטי, ללא קשר להקשר.
2. הפרדת סמכויות בין אימות חתימות ליצירת אסימונים.
סמכות המינט של הגשר חייבת להיות מאחורי מודול אימות שמפיק הוכחה ממצב קנוני בבעלות התוכנית — לא מחשבונות שהגיעו מהקורא. כאשר verify_signatures קיבלה חשבון הנשלט על ידי התוקף כמקור האמת, נעלם ההבדל בין “האם הרשת אישרה זאת?” ל”האם ניתן למתן?”. הוראת המינט חייבת להפיק הרשאה ממצב שה תוכנית עצמה כתבה לאחר אימות קלט Guardian אמיתי.
3. יש לאכוף מניעת שחזור VAA בשרשרת.
גם אם SignatureSet היה אמיתי, complete_wrapped חייבת לסרב ל-VAAs שכבר נוצלו. Wormhole הוסיפה מנגנון מניעת שחזור בגרסאות מאוחרות, אבל בגרסה שנוצלה העדר המנגנון איפשר שימוש חוזר ב-VAA מאושר במרמה. כל פעולה בגשר צריכה להיות מוגנת על ידי מיפוי processed_vaas: mapping[bytes32, bool] שמתעדכן אטומית בתחילת ההרצה.
4. שימוש ב-API מיושן הוא ממצא ביקורת קריטי.
פרצת Wormhole הייתה ידועה כפקודת API מיושנת. SDK של Solana סימן את load_instruction_at כמיושן וסיפק תחליף _checked לפני הניצול. העדר מעבר ל-API העדכני הוא לא רק בעיית איכות קוד אלא פגם אבטחה קריטי שחופרים תוקפים על ידי קריאת שינויים והשוואת קוד לא פרוס מול מוחל. ביקורות Solana חייבות לסרוק את solana_program::sysvar::instructions::* אחר וריאנטים לא-בדוקים ולדחות במקרה של התאמה. זו פרקטיקה סטנדרטית מאז 2022 ב-Ottersec, Halborn ו-Soken.
5. תיקוני אבטחה שלא הונחו ברשת הם מפת דרכים ציבורית להתקפה.
התיקון ל-load_instruction_at_checked הועלה למאגר הפתוח של Wormhole לפני הניצול. תוקף מתוחכם המנטר מאגרים לפרוטוקולים יעדיים אחר תיקוני אבטחה — במיוחד החלפת APIs מיושנים בגרסאות הבדוקות — יכול לגלות את הפרצה רק מההבדל בקוד. תהליכי פריסה לתוכניות קריטיות חייבים להתייחס לפרק הזמן בין “מיזוג לסניף הראשי” ל”פריסה לרשת” כחלון סיכון פעיל. נהלי פריסה דחופה חייבים לסגור חלון זה תוך שעות, לא ימים.
6. חתימות רף אינן מחליפות אימות חשבונות.
רשת Guardian של Wormhole כללה 19 צמתים עצמאיים עם סכמת חתימה ברף. היא הייתה יציבה. אך זה היה חסר רלוונטיות למתקפה. הניצול עקף את שכבת ההסכמה כולה על ידי שינוי החשבון ש-verify_signatures קוראת ממנו. חתימות רף מגינות מפני חשיפת מפתחות Guardian; הן אינן מגנות על תקלת קוד שלא מבקשת קלט מ-Guardian. אבטחה ברבדים דורשת שכל שכבה תופעל — ושליטה מעוקפת היא בעצם אפס שליטה.
משמעות המקרה לביקורות גשרים ואבטחת תוכניות Solana
ניצול Wormhole ממחיש שאבטחת גשרים בין שרשראות היא בעיית רב-שכבות. רשת Guardian יכולה להיות קריפטוגרפית שלמה, האינטרסים הכלכליים יכולים להיות מוגדרים נכון, והארכיטקטורה של הפרוטוקול טובה — ועדיין קריאה אחת לפונקציה מיושנת בתוכנית ברשת יכולה לבטל את הכל.
לצוותים הבונים על Solana, לקח אימות החשבונות מתמקד בכלל רחב: הכל פרמטר שכלי לתוכנית שלכם הוא קלט עוין עד שיוכח אחרת. אמתו בעלות, מפתח, דיסקרימיננט, ואורך נתונים לפני פעולה על כל חשבון. השתמשו ב-wrapper Account<T> במסגרת anchor או מקביל לו לאכוף בדיקות אלה ברמת סוג ולא רק באימות ידני.
לאדריכלי גשרים, הלקח הוא הפרדת סמכויות ועיגון במצב קנוני. סמכות המינט חייבת לנבוע ממצב בבעלות התוכנית שאומתה על ידה — לא מהוכחות שהוגשו מהקורא ולא אושרו.
Soken מבצעת ביקורת על כל תוכנית Solana לאיתור שימוש ב-API sysvar מיושן, חוסרים באימות בעלות חשבון וחוסרים במנגנוני מניעת שחזור VAA כממצאים בעלי עדיפות קריטית. תבנית Wormhole — עקיפת חתימת רף על ידי שינוי החשבון שהפונקציה קוראת ממנו — היא כעת סוג תקיפה מוכר במתודולוגיית הביקורת שלנו. אם הפרוטוקול שלכם משתמש בהודעות בין שרשראות או ביצירת נכסי wrapped, צרו קשר ב-soken.dev/services-it.html כדי לדון בסקירת אבטחה לפני פריסה.