הקטלוג נעל את התוסף לגרסה שנבדקה, וקוד אחר התקין את עצמו ברקע: Plugin4Shell בארבעה סוכני קוד
מפתחת מתקינה תוסף לסוכן הקוד שלה מתוך קטלוג רשמי. הקטלוג נועל את התוסף לגרסה אחת שעברה בדיקה, ומסמן אותה במחרוזת של 40 תווים שמזהה בדיוק את צילום המצב הזה של הקוד. כמה שבועות אחר כך הקטלוג מעדכן את הנעילה לגרסה חדשה יותר, הסוכן מוריד אותה ברקע בלי לשאול דבר, וממשיך לדווח שהוא מותקן על הגרסה הנעולה. הקוד שרץ אצלה במחשב הוא קוד אחר לגמרי.
את התרחיש הזה תיארו ב-17 בספטמבר חוקרי Air Security, אור נבו, דור גרנט וניב הופמן, שקראו לחולשה Plugin4Shell. לפי המחקר, אותה טעות בדיוק חוזרת בארבעה סוכני קוד נפוצים: Claude Code של Anthropic, Codex של OpenAI, GitHub Copilot ו-Gemini CLI של גוגל. בהמשך נראה מה git עושה כאן, למה זה קורה בלי שאף אחד לוחץ על כלום, ולמה ההיקף המעשי של החולשה צר בהרבה ממה שהכותרת מבטיחה.
למה בכלל נועלים תוסף לגרסה אחת
סוכן קוד הוא תוכנה שרצה על המחשב של המפתח ומקבלת את ההרשאות שלו: אותה גישה לקבצים, לפרטי האימות השמורים ולמערכות שהוא יכול להתחבר אליהן. תוספים (plugins) מרחיבים אותו ביכולות נוספות, והם מגיעים מקטלוגים פתוחים שמישהו אחר מתחזק. תוסף שרץ בתוך הסוכן מקבל כברירת מחדל את אותן הרשאות עצמן, ולכן תוסף זדוני לא צריך להסלים הרשאות בכלל. הוא כבר נמצא בדיוק במקום שהתוקף רצה להגיע אליו.
זאת לא הפעם הראשונה שהצוות הזה נוגע בבעיה. במחקר קודם בשם The Story of Skills הם בנו skill זדוני, דחפו אותו לקטלוג פופולרי, ולפי דיווחם השתלטו על 26 אלף סוכנים. במחקר SkillJacking הם הראו שאין צורך לשתול שום דבר חדש: מספיק להשתלט על המאגרים שמאחורי skills שכבר בשימוש, ולטענתם 925 כאלה נחטפו מתחת לידיים של המתחזקים שלהם.
התשובה של התעשייה בדיוק לסוג הזה של משיכת שטיח היא נעילה לפי hash. בודקים את הקוד במצב אחד, נועלים את מזהה ה-commit הספציפי, ומניחים שמעכשיו זה מה שירוץ לתמיד. Plugin4Shell הוא הסיפור של הגבול הזה נשבר.
המקום שבו git מוותר
כשמבקשים מ-git לעבור למצב מסוים בקוד, נותנים לו שם. השם יכול להיות שם של ענף, של תג, או מזהה של commit. git מפענח את השם לפי סדר עדיפויות קבוע, ואם אותה מחרוזת מתאימה גם לשם ענף קיים וגם למזהה commit קיים, git בוחר בענף. הוא מדפיס אזהרה קצרה על כך שהשם דו משמעי, וממשיך כרגיל. להתנהגות הזאת קוראים refname ambiguity, והיא מתועדת ומכוונת, לא תקלה.
שלושה מהסוכנים משכפלים את מאגר התוסף ואז קופצים ל-commit הנעול, בדיוק בדרך הזאת: git clone של המאגר, ואחריו git checkout של ה-hash. התוקף, שבשלב הזה כבר שולט במאגר של התוסף, יוצר בו ענף ששמו הוא בדיוק אותן 40 הספרות ההקסדצימליות של ה-hash הנעול, ומגדיר אותו כענף ברירת המחדל. ה-clone הרגיל מוריד אותו כענף מקומי בשם הזה, ה-checkout מפענח את המחרוזת לענף ולא ל-commit, והקוד שנוחת בתיקייה הוא של התוקף. הסוכן מדווח על התקנה מוצלחת בגרסה הנעולה, כי מבחינתו הוא באמת ביקש אותה.
שני תנאים צריכים להתקיים: שהשרת המארח יסכים בכלל לשם ענף כזה, ושהענף יהיה ענף ברירת המחדל של המאגר. ענף שאינו ברירת מחדל יורד ב-clone רק כהפניה מרוחקת, וה-checkout היה נופל חזרה ל-commit האמיתי. git עצמו מסכים לשם כזה בלי להניד עפעף, כי הבדיקה הפנימית שלו על שמות הפניות מקבלת מחרוזת של 40 תווים הקסדצימליים כשם חוקי לגמרי.
והבאג עצמו הוא בדיוק מה שחסר בסוף: אף אחד מהסוכנים לא בדק, אחרי ה-checkout, שמה שנחת בתיקייה הוא באמת ה-commit שהקטלוג נעל.
Gemini CLI נועל בדרך אחרת, ולכן גם נשבר אחרת. לפי Air הוא משכפל את המאגר בעומק אחד, מושך במפורש את ה-commit הנעול לתוך הפניה פנימית ששמה FETCH_HEAD, ואז עובר אליה. הבעיה היא ש-git checkout FETCH_HEAD לא מחויב לקרוא את ההפניה הפנימית הזאת: אם ענף ברירת המחדל של המאגר נקרא בעצמו FETCH_HEAD, ה-checkout מפענח את השם לענף, וה-commit שזה עתה הורד נזרק בשקט לטובת התוכן של הענף.
מה הופך את זה לאפס קליקים
עד כאן זאת חולשת התקנה, ומי שלא מתקין תוסף חדש לא מושפע. מה שמרים את זה לרמה אחרת הוא העדכון האוטומטי. סוכנים מרעננים תוספים מותקנים ברקע, וכשהקטלוג מעדכן את ה-hash הנעול לגרסה חדשה, אותו git checkout רץ שוב על מכונה של מישהו שכבר מזמן התקין ואישר את התוסף. אין שלב התקנה, אין חלון אישור, אין מה לשים לב אליו. לפי Air זה בדיוק התסריט: התוקף לא צריך לשכנע אף אחד להתקין משהו חדש, אלא רק שהתוסף התמים שלו כבר יהיה שם.
כמה גדולה החולשה הזאת באמת
Air מתארים את Plugin4Shell כהרצת קוד מרחוק בחומרה גבוהה שנוגעת במיליוני סוכנים. הבדיקות שפורסמו מאז מציירות תמונה צרה יותר, ושווה לפרק אותה לגורמים.
התנאי הראשון, שם הענף, לא מתקיים ב-GitHub. התיעוד של GitHub אומר את זה במפורש ברשימת ההגבלות: אסורים שמות שנראים כמו מזהי אובייקט של Git, כלומר 40 תווים שמכילים רק 0 עד 9 ו-A עד F, בדיוק כדי למנוע בלבול עם מזהים אמיתיים. מאגר שמתארח ב-GitHub לא יקבל דחיפה של ענף כזה מלכתחילה. The Hacker News, שסיקר את המחקר, בדק ב-18 בספטמבר את הקטלוגים שמגיעים עם הסוכנים, ומצא שכל תוסף בקטלוג הקהילתי של Anthropic ובקטלוגי ברירת המחדל של Claude Code ושל Copilot מצביע על מאגר ב-GitHub.
התנאי השני, העדכון האוטומטי, גם הוא מדויק פחות ממה שנשמע. התיעוד של Claude Code מפרט שהקטלוג הרשמי claude-plugins-official, רוב הקטלוגים הרשמיים האחרים, וקטלוגים שנוספו דרך claude.ai מגיעים עם עדכון אוטומטי פעיל, בעוד שקטלוגים של צד שלישי וקטלוגים מקומיים מגיעים עם עדכון אוטומטי כבוי. אז הקטלוגים שמתעדכנים לבד הם בדיוק אלה שמתארחים ב-GitHub, והקטלוגים שהטריק עובד עליהם הם אלה שלא מתעדכנים לבד. שני התנאים כמעט ולא נפגשים.
מה שכן, זה לא הופך את הממצא לתיאורטי. אותו תיעוד של Anthropic מונה במפורש את Bitbucket ואת שרתי git עצמאיים כמקורות קטלוג נתמכים, וארגון שמריץ קטלוג תוספים פנימי על שרת git משלו מקיים את התנאי הראשון בקלות. שם התקיפה עובדת, במחיר של קליק אחד של המשתמש במקום אפס. הגרסה של Gemini CLI נשענת על שם ענף אחר לגמרי, FETCH_HEAD, שהכלל של GitHub נגד שמות שנראים כמו hash כלל לא נוגע בו, ולכן לא ברור שהתקנה מ-GitHub מגנה דווקא שם.
עד 18 בספטמבר לא הוקצה לחולשה מזהה CVE, אף אחת מארבע החברות לא פרסמה עליה הודעת אבטחה, ואין שום סימן שמישהו השתמש בה בתקיפה אמיתית. אלה הבדיקות של The Hacker News, לא הכחשה של יצרן.
מה תוקן ומה לא
לפי ציר הזמן שמפרסמים Air, הם מצאו את החולשה במאי 2026 עם קוד הדגמה עובד מול ארבעת הסוכנים, ודיווחו ליצרנים ביוני. Anthropic אישרה תיקון ב-17 ביוני בגרסה 2.1.179 של Claude Code, ו-Codex אומת כמתוקן ב-12 באוגוסט בגרסה 0.146.0. ב-4 באוגוסט הודיעה גוגל שלא ישוחרר תיקון ל-Gemini CLI מפני שהכלי יוצא משימוש, והמליצה למשתמשים לעבור ל-Antigravity. ב-GitHub Copilot, לפי Air, מיקרוסופט לא שחררה תיקון עד כה.
התיקון עצמו קטן. אחרי ה-checkout צריך לשאול את git איזה commit באמת יושב עכשיו בתיקייה, ולעצור אם הוא לא זהה ל-hash שנעל הקטלוג. Air מדגישים שהבדיקה חייבת להסתכל על התוצאה ולא על מה שביקשו, וזה בדיוק ההבדל שהגרסה של Gemini מחליקה דרכו. והיא חייבת לרוץ בתוך הסוכן עצמו, כי הנעילה נפתרת אצל הלקוח, ושום קטלוג לא יכול לאכוף מרחוק הבטחה שנשברת על המחשב שלכם.
מה זה אומר בפועל
מי שמריץ Claude Code או Codex סוגר את זה בעדכון גרסה פשוט, ואין סיבה לדחות אותו. במקומות שבהם אין תיקון, GitHub Copilot ו-Gemini CLI, מה שנשאר הוא לצמצם את מספר התוספים המותקנים ולהעדיף קטלוגים שמתארחים ב-GitHub. ארגון שמריץ קטלוג תוספים פנימי על Bitbucket או על שרת git משלו הוא הקבוצה שבה התנאים באמת מתקיימים, וכדאי לו לבדוק מי בכלל מורשה לדחוף ענפים למאגרים שהקטלוג מצביע עליהם. ומעבר לגרסה הספציפית הזאת, שווה לזכור מה נעילת hash באמת מבטיחה: היא מבטיחה שביקשתם גרסה מסוימת, לא שקיבלתם אותה.
תגובות