ניתוח טכני לפרצת Wormhole Bridge: $326M מנוצלים

Article author

ניתוח טכני של פריצת גשר 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

  1. זיוף חשבון. התוקף יצר חשבון Solana המדמה את מבנה החשבון הצפוי במיקום SignatureSet בנתוני ההנחיות של תוכנית הגשר. חשבון זה הכיל חתימות הנשלטות על ידי התוקף — חתימות Ed25519/Secp256k1 תקפות, אך מעל payload בבחירת התוקף המאשר יצירת 120,000 wETH לכתובת Solana של התוקף.

  2. עקיפה בעזרת API sysvar מיושן. התוקף שלח טרנזקציה שקראה לפונקציית verify_signatures בתוכנית גשר Wormhole ב-Solana. התוכנית קראה ל-load_instruction_at — הגרסה הישנה והלא מאומתת — כדי לוודא שקריאה לספק Secp256k1 התרחשה מוקדם יותר באותה טרנזקציה. פונקציה זו קוראת לכל חשבון באינדקס המוגדר ללא אימות שזהו חשבון ה-instructions sysvar האמיתי.

  3. קבלת הוכחה מזויפת. verify_signatures סרקה את החתימות שבחשבון הנשלט על ידי התוקף, מצאה אותן תקפות קריפטוגרפית (התוקף עצמו חתם עליהן, מעל ה-payload שלו), וסימנה את ה-VAA כאושר Guardian. רשת Guardian לא נשאלה.

  4. הרשאת יצירת אסימונים. התוקף קרא ל-complete_wrapped עם VAA המאושר החדש. תוכנית הגשר, שסמך על מצב האישור של ה-VAA, יצרה 120,000 wETH ב-Solana לכתובת התוקף.

  5. הפקת נכסים בין שרשראות. התוקף העביר את ה-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 כדי לדון בסקירת אבטחה לפני פריסה.

Article author

Frequently Asked Questions

מה הייתה הסיבה העיקרית לפרצת Wormhole bridge?

שימוש ב-API מיושן של Solana (`load_instruction_at` ללא `_checked`) בפונקציית `verify_signatures` שלא בדק את מקור החשבון כתחום sysvar אמיתי. התוקף העביר חשבון מזויף עם חתימות עצמאיות, הפונקציה קיבלה זאת כהסכמה מ-Guardian והגשר יצר 120,000 wETH ללא נעילה תואמת ב-Ethereum.

האם מפתחות המאמתים של Wormhole Guardian נפרצו?

לא. סכמת חתימות הסף של 19 הקודקודים של Guardian לא נפרצה. הניצול עקף זאת תוך כדי שינוי החשבון ש-`verify_signatures` קורא ממנו, מבלי שהחוזה התייעץ עם ה-Guardians.

כיצד הפרצה ב-Wormhole שונה מפרצת Ronin bridge?

Wormhole הייתה תקלה באימות smart contract ב-Solana ללא גניבת מפתחות. Ronin הייתה גניבת מפתחות תפעוליים: התוקפים קיבלו חמישה מ-9 מפתחות מאמתים באמצעות הנדסה חברתית והשתמשו בהודעות חתומות לגיטימיות למשיכת כספים.

כיצד תוכניות Solana יכולות למנוע סוג זה של התקפה?

יש לאמת כל פרמטר AccountInfo: לבדוק בעלים, מפתח ודגלים הרצה לפני פעולה על החשבון. להשתמש ב-`load_instruction_at_checked` (או Wrapper של Anchor Account) שמבטיח בעלות sysvar ברמה הטיפוסית ולהחשיב כל קריאה ל-API ללא `_checked` כבעיית אבטחה קריטית.

אילו שיטות בדיקה תופסות את הבעיה הזו היום?

ביקורות מודרניות לסולאנה מסננות שימושים ב-`solana_program::sysvar::instructions::*` ללא `_checked` וחוסמות כל התאמה. הביקורות גם מאשרות מיגון נגד ניגון חוזר של VAA, הפרדה מפורשת בין אימות הוכחות לאישור יצירת מטבעות ופריסת עדכוני אבטחה מהירים.

צ׳אט