הפקודה שפתחה את השרשרת היא אחת הסתמיות שיש: ssh -L, הדגל שמפנה socket מקומי אל המכונה שבצד השני. בניסוי שהריצה אריקה וינדיש (Erica Windisch) מ-Cyberdione Labs, המכונה המרוחקת אכן פתחה את ה-socket שהתבקש. השאלה היא מי פתח אותו: תהליך הרקע של Tailscale, שרץ על המכונה עם הרשאות root. הוא התחבר ל-socket שרק root יכול לגעת בו, החזיר את מה שעבר בו לצד המתחבר, וכך הגיע מי שאין לו הרשאות מנהל אל קובץ הסיסמאות של המערכת.

וינדיש פרסמה את הממצאים בדוח בשם Tailworm, 11 ממצאים ממוספרים שנבדקו בין 22 ל-24 באוקטובר מול Tailscale בגרסה 1.104.2 ו-headscale בגרסה 0.29.5, שתיהן גרסאות רשמיות ללא שינוי. לאף ממצא אין מזהה CVE, ועמוד עדכוני האבטחה של Tailscale עוד לא מתייחס אליהם: הפרסום האחרון שם, TS-2026-013 מ-21 באוקטובר, עוסק בכלל בדליפת מפתחות של eMudhra. כל הבדיקות נעשו במעבדה, על מערכות של החוקרת עצמה. נעבור על שלוש החולשות שמצטרפות לשרשרת אחת, על הסיבה שתיקון מיוני לא עצר אותה, ועל המחלוקת סביב השאלה אם מדובר בתולעת.

מה זה Tailscale SSH, ולמה תהליך הרקע רץ כ-root

Tailscale בונה רשת פרטית בין מכשירים שמפוזרים בכל מקום, בלי שרת VPN מרכזי: כל מכשיר מקבל כתובת בתוך הרשת ומדבר ישירות עם האחרים. כדי לנהל את ההצפנה, את הניתוב ואת ממשק הרשת הווירטואלי, השירות מריץ על כל מכונה תהליך רקע בשם tailscaled, ועל Linux הוא רץ עם הרשאות root. זה לא פגם בעיצוב, זו דרישה: בלי הרשאות כאלה אי אפשר ליצור ממשק רשת.

על התשתית הזאת יושבת תכונה שמחליפה את מה שרוב האנשים מכירים מ-SSH. אין מפתח פרטי להעביר ואין סיסמה להקליד: הזהות של המכשיר ברשת היא מה שקובע אם הוא רשאי להתחבר, וקובץ מדיניות ההרשאות של הרשת (ACL) הוא מה שמגדיר את זה. לתכונה קוראים Tailscale SSH, ומכאן מגיע הצירוף שעומד בלב הדוח. כשמכשיר שהמדיניות אישרה מבקש משהו, מי שמבצע את הבקשה בפועל הוא לא המשתמש המחובר אלא tailscaled, והוא רץ כ-root. כל טעות קטנה בבדיקת הרשאות במקום הזה הופכת להסלמת הרשאות.

שלוש חולשות שמצטברות לשרשרת אחת

הממצא הראשון הוא זה שבפתיחה, והדוח מסמן אותו ברמה High. הפניית socket מקומית, ssh -L, גורמת ל-tailscaled להתחבר אל נתיב ה-socket שהתבקש, וההתחברות נעשית בהרשאות שלו. היעדים שנבדקו הם מה שמותקן על המון שרתים: ה-socket של Docker, ה-API המקומי של Tailscale עצמו, ואפיק ה-bus הפרטי של systemd.

הממצא השני הופך את הכיוון. הפניה מרוחקת, ssh -R, מבקשת מהמכונה המרוחקת לפתוח socket ולהעביר אליו חיבורים. כאן tailscaled מוחק את ה-socket שקיים בנתיב, גם כשהוא בבעלות root, ומקים במקומו listener של התוקף. מי ששולח אחר כך פקודות לנתיב הזה חושב שהוא מדבר עם השירות המקורי.

הממצא השלישי הוא מה שפותח את הדלת לשני הראשונים. Tailscale מחזיקה רשימת נתיבים מותרים להפניות socket (ספריית הבית, /tmp ו-/run/user), בודקת את הנתיב שהתבקש מול הרשימה, ורק אחרי הבדיקה ניגשת אליו. בין הבדיקה לשימוש יש פער זמן, ומי שמחליף את הנתיב בדיוק בפער הזה גורם לכך שהבדיקה אישרה דבר אחד והשימוש נעשה על דבר אחר. לפרצה מהסוג הזה קוראים TOCTOU, קיצור של time-of-check to time-of-use.

מה שהמרוץ השיג בניסוי הוא השתלטות על ה-socket של ה-API המקומי של Tailscale. דרכו נתפסה בקשה אחת, POST /localapi/v0/start, שנשאה בתוכה authkey, ה-token שמצרף מכשיר חדש לרשת. וינדיש כותבת שהמפתח הזה שימש על מכונת בדיקה, ושהמכונה אכן הצטרפה לרשת כצומת לגיטימי, במקרה שבו מנגנון Tailnet Lock היה מכובה. הדוח מדייק שהמרוץ רחוק מאמין: באחד המקרים בצד המתחבר הוא מודד שיעור הצלחה של אפס עד ארבעה אחוזים.

עד כאן השרשרת. החלק שמעניין יותר הוא למה היא עובדת אחרי שבעיה מאותה משפחה בדיוק כבר תוקנה פעם אחת.

אז למה התיקון מיוני לא עצר את זה?

ב-3 ביוני פרסמה Tailscale שני עדכוני אבטחה באותו יום. TS-2026-004 תיאר את אותו גבול: בדיקות ההרשאות בהפניית socket ב-Tailscale SSH לא התייחסו לקישורים סימבוליים, ולכן משתמש בלי הרשאות היה יכול ליצור קישור בנתיב שבבעלותו, למשל /home/$USER/my.sock, שמצביע על /var/run/docker.sock, ולגשת דרכו ליעד המוגן. ההסבר בעדכון אומר במילים של Tailscale למה זה עבד: "זה אפשרי מפני ש-Tailscale רץ כ-root ולכן יש לו גישה אל היעד המוגן". התיקון, בגרסה 1.98.9, הוסיף פענוח של קישורים סימבוליים מעל ההשוואה הטקסטואלית של הנתיב. TS-2026-005, באותו יום, סגר את אותה בעיה בתכונת Serve.

וכאן הנקודה. גם בדיקה שמפענחת קישורים סימבוליים היא עדיין בדיקה שקורית לפני השימוש. מה ש-Tailworm מתאר הוא לא עקיפה של הבדיקה אלא החלפה של הקרקע מתחתיה, אחרי שהיא כבר אישרה. הדוח מציין גם ש-check mode, מצב שמוסיף שלב אישור אינטראקטיבי לפני התחברות, לא מצמצם את ממצאי ההפניה בכלל.

רשימת העדכונים של Tailscale מ-2026 מראה שלא מדובר בתיקון בודד. TS-2026-006 מ-11 ביוני תיקן מצב שבו פנייה למשתמש לפי מספר ה-UID שלו, כלומר "0@host", עקפה את ההגבלה על root ב-ACL. TS-2026-009 מ-13 ביולי תיקן שם משתמש שמתחיל במקף: התחברות בשם "-i" הגיעה אל getent כדגל, הפקודה הדפיסה את כל קובץ הסיסמאות שמתחיל במשתמש root, ו-Tailscale פתחה session של root. TS-2026-010 מ-24 ביולי תיקן חשיפה של משתני סביבה למשתמשים מקומיים אחרים וללוגים. ארבעה עדכונים בחצי שנה, כולם על Tailscale SSH, כולם על אותו גבול: היכולת של מי שהמדיניות אישרה לגרום לתהליך שרץ כ-root לעשות משהו בשמו.

"wormable" או לא, וכאן הדוח נחלק

הכינוי שהדוח בחר לעצמו הוא מה שהצית את הוויכוח. ההגדרה שלו ל"wormable" היא התפשטות אוטומטית אל כל המכשירים שהמדיניות מתירה, והוא מתאר שבמעבדה זה נעשה "בלי סיסמאות, בלי מפתחות גנובים, בלי התערבות של משתמש, פחות משנייה לכל קפיצה".

שני דברים עצרו את ההתפשטות בניסוי, ושווה להבדיל ביניהם. הראשון הוא כתיבת הרשאות SSH כזוגות מפורשים של מקור ויעד במקום הרשאות רחבות לפי זהות או תגית, ובלשון הדוח זו "הבקרה היחידה שעצרה את זה". השני הוא מפתחות מכשיר שקשורים לחומרה, ל-TPM או ל-Secure Enclave, שמונעים את גניבת הזהות שמאפשרת לתוקף לשמור על האחיזה. לגבי Tailnet Lock, התכונה שדורשת חתימה מאושרת על כל מכשיר שמצטרף, הדוח זהיר יותר ממה שנכתב במקומות אחרים: בניסוי שבו היא הייתה פעילה נתפסו סוד ההשבתה וחומר מפתח החותם בזמן שעברו ברשת, אבל ההשבתה עצמה לא בוצעה.

לא כולם קונים את התמונה. בדיון ב-Hacker News כתב משתמש בשם iancarroll שהדוח "בהחלט לא wormable במובן המסורתי", בטענה שגישת SSH לבדה לא מספיקה להתפשטות עצמית אמיתית בין רשתות. מהצד השני, משתמש בשם aborsy טען שחלק מהחולשות קריטיות דווקא כן, מפני שדי במכשיר SSH אחד שנפרץ כדי להגיע ל-root על אחרים. עובד של Tailscale שכתב שם תחת השם raggi הוסיף ש"יהיו לנו פרטים נוספים בהמשך".

גם אופן הפרסום במחלוקת. עמוד הדוח לא מתאר שום לוח זמנים של דיווח מוקדם, ואומר רק שהתשתית חשופה כבר עכשיו ושמודעות מהירה תזרז את התיקון. באותו דיון, כשנטען שהממצאים לא דווחו לכתובת האבטחה של Tailscale, השיבה וינדיש ש"זה דווח, אבל לא בתיאום". אתר The Hacker News דיווח שסמנכ"ל ההנדסה של Tailscale, מייק שייבר, כתב בפורום החברה ב-Reddit שהחברה קיבלה את הדוח, שהיא בחנה את אותם אזורים עם מומחי אבטחה חיצוניים עוד קודם, שהיא מצאה בעיות שהיא עובדת על תיקונן, ושהיא לא ממליצה לכבות את Tailscale SSH. את התגובה הזאת לא אימתנו ישירות.

הממצא שלפי החוקרת מסוכן יותר מכל פרשת ה-SSH

בין 11 הממצאים יש אחד נוסף ברמה High שלפי וינדיש עשוי להיות חשוב יותר לארגונים מסוימים. הוא נוגע ל-app connectors, מכשיר שמשמש שער יציאה אל שירות חיצוני מסוים, כדי שכל התעבורה אליו תצא מכתובת אחת מוכרת. בתכונה נסיונית בשם conn25, שמסומנת כ-experimental בגרסה 1.104.2, הרשאת משתמש מאפשרת לעמית ברשת לרשום יעדי יציאה כרצונו. היעדים האלה לא נבדקים מול היעדים שהוגדרו לאפליקציה, וה-ACL של הרשת לא נבדק מול היעד האמיתי.

מה שאפשר להגיע אליו ככה הוא הבעיה: טווח הכתובות 169.254.0.0/16, שבו יושב שירות המטא-דאטה של ספקי הענן, הרשתות הפנימיות שה-connector מחובר אליהן, ובסביבת Kubernetes גם שירותים בתוך האשכול. שירות המטא-דאטה הוא הכתובת שממנה מכונה בענן שולפת את פרטי האימות הזמניים שלה, ולכן גישה אליו שווה בפועל לגישה להרשאות של אותה מכונה. על הפיבוט בין אפליקציות הדוח אומר במפורש שזה "ניתוח, לא הודגם במעבדה".

וזה בדיוק הגבול ש-Tailscale כבר תיקנה בפעם אחרת. TS-2026-011 מ-18 באוגוסט תיאר נתב subnet מסוג 4via6 שלא בדק את סוג הכתובת שהוא מחלץ מכתובת ה-IPv6, ולכן עמית עם הרשאת ACL הגיע דרכו אל שירות המטא-דאטה והשיג את פרטי האימות של הנתב. התיקון הגיע בגרסה 1.102.3.

מה זה אומר בפועל

אין תיקון, ולכן מה שנשאר הוא הגדרות. אם אתם מריצים Tailscale SSH, שווה לעבור על מדיניות ההרשאות ולראות כמה רחבה היא: הרשאה שנכתבה לפי תגית או לפי קבוצת זהות מתירה בפועל כל מכשיר בקבוצה אל כל יעד בה, וזוגות מפורשים של מקור ויעד הם הבקרה שעצרה את השרשרת בניסוי. כיבוי הפניית ה-socket איפה שאין בה צורך מוציא את שלושת הממצאים הראשונים מהמשחק, והסרת הרשאות app connector ממכשירים שלא זקוקים להן עושה את אותו דבר לממצא הרביעי. מי שמריץ headscale במקום השירות המנוהל כדאי שיתייחס לכל הרשאת SSH שם בזהירות מיוחדת.

ודבר אחד שקל לפספס: מכשיר שנחשד כפרוץ צריך להימחק במשטח הניהול של הרשת, לא רק להיבנות מחדש. התקנה מחדש של המכונה לא מבטלת מפתח זהות שכבר הועתק ממנה.