ביום חמישי מפתחים שעבדו ב-Cursor קיבלו עדכון שגרתי לתוסף Claude Code. התוסף המשיך לעבוד בדיוק כמו קודם, אותו ממשק ובלי שגיאה אחת על המסך. במקביל נוספה לקובץ authorized_keys שבתיקיית ה-SSH של המשתמש שורה אחת עם מפתח ציבורי שלא היה שם לפני, מסומנת בהערה שנראית כמו שם של חשבון תחזוקה: svc-telemetry.

את הגרסה הזאת תיעדו חוקרי OpenSourceMalware, ו-Cursor פרסמה הודעת אבטחה משלה על האירוע. מדובר בגרסה 2.1.313 של התוסף, שהופצה בחנות התוספים של Cursor תחת מזהה המפרסם הרשמי anthropic.claude-code. לפי Cursor נפגעו לכל היותר 5,000 משתמשים, שהתקינו או עדכנו את התוסף בין 02:27 ל-13:35 שעון החוף המערבי של ארצות הברית ב-29 באוקטובר. בהמשך נעבור על מה שהקוד הזדוני עשה בפועל, על החלק שכנראה נכשל אצל רוב מי שהתקין, ועל מה שעדיין לא ידוע: איך הגרסה הזאת נכנסה לחנות מלכתחילה.

למה תוסף לסביבת פיתוח הוא יעד נוח כל כך

תוסף לסביבת פיתוח אינו אפליקציה שרצה בתוך קופסה מבודדת. הוא קוד JavaScript שהעורך טוען לתוך התהליך שלו ומריץ בהרשאות של המשתמש שפתח את החלון, עם גישה לקבצים שלו, למשתני הסביבה שלו ולרשת. בפועל, אם התוסף יכול לקרוא את תיקיית ה-SSH, הוא יכול גם לכתוב לשם. מה שמחליף את הקופסה החסרה הוא אמון: המשתמש מתקין תוסף כי מזהה המפרסם שלו מוכר, והחנות אחראית לכך שהמזהה הזה אכן מצביע על מי שהוא אומר שהוא.

ל-Cursor יש חנות תוספים משלה, בנפרד מהחנות של Microsoft ומ-Open VSX, מאגר התוספים הפתוח שעורכי קוד רבים נשענים עליו. הגרסה הזדונית הופצה רק בחנות של Cursor. גם OpenSourceMalware וגם Cursor מציינות במפורש שחבילת ה-npm של Claude Code, התוסף ב-Open VSX והתוסף בחנות של Microsoft אינם זדוניים. ההודעה הראשונה של OpenSourceMalware כן תלתה את האירוע ב-Open VSX, ותוקנה אחרי ש-Cursor יצרה איתם קשר ומסרה שהמקור הוא החנות שלה.

66 שורות שנדחפו לפני הקוד האמיתי

כשהחוקרים פתחו את התוסף, הוא נראה זהה לגרסה הרגילה. הבינארי המצורף היה הבינארי הרשמי, שאר הקבצים לא שונו, והדבר היחיד שהשתנה היה הקובץ extension.js, זה שהעורך טוען כשהתוסף עולה. בראש הקובץ הזה נוסף בלוק שלא היה שם: פונקציה שמפעילה את עצמה ברגע שהקובץ נטען, מייבאת את מודולי ה-process, מערכת הקבצים, מערכת ההפעלה, ה-HTTP וההצפנה של Node.js, ומסיימת בשורה 66. רק אחריה מתחיל ה-bundle של התוסף האמיתי.

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

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

המפתח, השירות והמנהרה

הפעולה הראשונה פשוטה. אם לתהליך יש תיקיית בית, הקוד יוצר בה את תיקיית ה-SSH בהרשאות 0700, מוסיף לקובץ authorized_keys מפתח Ed25519 של התוקף בהרשאות 0600, ומסמן אותו בהערה svc-telemetry. אם ההערה כבר קיימת, הוא רושם לעצמו את העובדה ולא מוסיף כפילות. השורה הזאת לבדה אומרת שמי שמחזיק במפתח הפרטי המתאים יכול להתחבר ב-SSH לחשבון, בלי סיסמה.

רק שכדי להתחבר צריך שבצד הקורבן יאזין בכלל שירות SSH, ושיהיה אליו נתיב מהאינטרנט. שני הדברים האלה הם מה שהקוד מנסה לסדר בשלב הבא, וכאן גם מתחילים הפערים בין המקורות. ב-macOS הוא בודק אם Remote Login דלוק ומנסה להדליק אותו, בלינוקס הוא מחפש את ה-service בשם sshd או ssh ומנסה כמה פקודות הפעלה, ובחלונות הוא מריץ PowerShell שמתקין את OpenSSH Server ומגדיר אותו לעלות אוטומטית. כל אלה דורשים הרשאות מנהל, ש-Cursor אומרת שאין לתוסף על רוב מכונות ה-macOS וה-Windows. Cursor אומרת יותר מזה: לדבריה, בגלל שגיאות בקוד הזדוני עצמו השלב הזה היה נכשל בכל תרחיש שאינו חריג במיוחד, כלומר הפעלה של Cursor בהרשאות מנהל. OpenSourceMalware, שניתחה את הקוד ולא את המכונות שנפגעו, מתארת את המנהרה כנתיב קוד מאומת ואינה מכריעה אם הוא הצליח בשטח.

והמנהרה עצמה שווה הסבר, כי היא הפוכה למה שרובנו מדמיינים. בחיבור SSH רגיל אתם פונים לשרת. כאן הקורבן הוא זה שיוצר את החיבור החוצה אל שרת התוקף, ואז מבקש מאותו שרת להקצות אצלו פורט ולהעביר כל חיבור שמגיע אליו חזרה, דרך אותו ערוץ, אל פורט 22 של המכונה שממנה יצא החיבור. לטכניקה הזאת קוראים reverse tunnel, והיא הדרך המקובלת לעקוף NAT וחומות אש שחוסמות חיבורים נכנסים. את המפתח הפרטי לחיבור הזה הקוד נושא איתו מקודד ב-base64 ופורס לקובץ זמני, שהשם שלו נגזר משם המכונה ומנתיב תיקיית הבית. החיבור מבטל אימות של מפתח השרת, מתעלם מקובץ ה-known_hosts של המשתמש, ומנסה שוב כל 60 שניות. במערכות יוניקס הוא מעדיף את autossh, כלי שנועד לשמר מנהרות כאלה.

כדי שהמנהרה תשרוד אתחול, הקוד כותב סקריפטים משלו ורושם אותם להפעלה אוטומטית. ב-macOS שני קבצים מוסתרים בתוך תיקיית ההגדרות של המשתמש ו-LaunchAgent בשם com.apple.sysmond.plist, שנטען דרך launchctl ומוגדר לעלות בהתחברות ולהתרומם מחדש אם הוא נופל. בלינוקס אותם קבצים, עם רשומת @reboot ב-crontab של המשתמש. בחלונות סקריפט cmd ומשימה מתוזמנת בשם SysMon שמנסה לרוץ כ-SYSTEM, ואם זה לא מסתדר, עותק של הסקריפט בתיקיית ה-Startup. השמות אינם מקריים: sysmond אינו רכיב של אפל, ו-SysMon הוא שם של כלי ניטור מוכר של מיקרוסופט. (וזה גם מה שמסביר למה הסרת התוסף לבדה לא פותרת כלום, כי הסקריפטים האלה עומדים בזכות עצמם.)

מה נשלח חזרה, ומה דווקא לא

שנייה וחצי אחרי הטעינה הקוד שולף את כתובת ה-IP הציבורית של המכונה משני שירותים חיצוניים, ושולח הודעת JSON אחת. בתוכה שם המכונה, שם המשתמש המקומי, מערכת ההפעלה והארכיטקטורה, נתיב תיקיית הבית, ספריית העבודה, כתובת המייל שמוגדרת ב-git config הגלובלי, ושמות של עד 40 משתני סביבה שהשם שלהם מכיל KEY, TOKEN, SECRET, PASS, AWS או GCP. ההודעה נשלחת ב-HTTP לא מוצפן לפורט 8099, גם לשרת התוקף וגם לכתובת המקומית של המכונה עצמה, והתשובה נזרקת.

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

ומה שאין: OpenSourceMalware לא מצאה שום מטען נוסף שהקוד מוריד. התעבורה שלו מסתכמת בשליפת ה-IP הציבורי, בדיווחי ה-JSON ובמנהרת ה-SSH עצמה. גם הסקריפטים והמפתחות נבנים מתוך מה שכבר יושב בתוך התוסף. זה לא הופך את האירוע לקל יותר, כי מי שמקבל גישה ל-SSH למכונה של מפתח לא צריך להוריד כלום.

מה עוד לא ידוע, ומה Cursor עשתה

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

OpenSourceMalware מציבה גבול משלה. הממצאים שלה, היא כותבת, מוכיחים שהחפץ שנותח הוא זדוני, ואינם מוכיחים כשלעצמם שההפצה הרשמית של Anthropic נפרצה. לפי The Hacker News, Anthropic פרסמה בינתיים את גרסה 2.1.314 גם ל-Open VSX וגם לחנות של Microsoft.

מי שרוצה לראות את הדפוס יכול להסתכל 11 ימים אחורה, על דיווח של אותה חברת מחקר. ב-19 באוקטובר, בין 12:47 ל-13:12 בשעון UTC, מישהו החליף את הבינאריים של חמש גרסאות שכבר פורסמו של כלי שורת הפקודה הרשמי של OVHcloud, מ-v0.11.0 עד v0.15.0, בגרסה שגונבת סודות. ההעלאה נעשתה דרך חשבון GitHub של תורם לגיטימי לפרויקט, חשבון שלפי החוקרים היה פרוץ כבר שבעה חודשים. מי שתפס את זה היה משתמש במורד הזרם ששמר ב-CI שלו את ה-SHA-256 של הקובץ, 94 דקות אחרי ההחלפה.

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

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

אם התקנתם או עדכנתם את התוסף של Claude Code דרך החנות של Cursor בחלון הזמן של 29 באוקטובר, הבדיקה הראשונה היא חיפוש המחרוזת svc-telemetry בקובץ ה-authorized_keys שלכם, ו-Cursor מפרסמת בהודעה שלה את הפקודות המדויקות ל-macOS ול-Windows לצד סקריפטי ניקוי. אם המפתח נמצא, הסרת התוסף אינה סוף התהליך, כי שימור האחיזה יושב בקבצים נפרדים ובמשימות מתוזמנות שממשיכות לרוץ. שני המקורות אומרים את אותו דבר על השלב הבא: להחליף את פרטי האימות שהיו נגישים מהמכונה, לעשות את זה ממכונה אחרת שאתם סומכים עליה, ולשקול ברצינות בנייה מחדש של המכונה שנפגעה. ומעבר למקרה הזה, זאת הזדמנות לבדוק מה בדיוק מתעדכן אצלכם אוטומטית בסביבת הפיתוח. כמו שאמרה Jenn Gile מ-OpenSourceMalware, "זה הוסר מהר מאוד, אבל כל מי שה-Claude Code שלו מוגדר לעדכון אוטומטי היה יכול לצרוך את הנוזקה".