דילוג לתוכן

פחות מ־10 נוירונים: איך סוכן AI מקבל דלת אחורית נסתרת, ואיך בודקים את שלכם

חוקרי Microsoft השתילו דלת אחורית בסוכני AI באמצעות עריכה של קומץ נוירונים רדומים. איך המתקפה עובדת, למה מבחנים לא תופסים אותה, ואילו בדיקות ומגבלות מצמצמות את הסיכון.

תום כהן · · 11 דקות קריאה

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

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

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

מה החוקרים מצאו

השיטה נקראת Lazarus. המאמר, “Lazare, veni foras: Reprogramming Dormant Neurons to Backdoor AI Agents”, נכתב בידי Yuchen Zeng,‏ Giorgio Severi,‏ Blake Bullwinkel,‏ Eric Horvitz ו־Dimitris Papailiopoulos מ־Microsoft Research ומצוות ה־AI Red Team של Microsoft. המאמר (PDF) · הסיכום של Microsoft Research

  • נבדקו שלושה מודלים פתוחים: Nemotron-Terminal-8B,‏ Qwen3.6-27B ו־Gemma-4-31B-IT.
  • ב־Nemotron-Terminal-8B הצוות ערך שבעה מתוך יותר מ־12,000 נוירונים בשכבת ה־feed-forward האחרונה של המודל, בעזרת 23 סשנים לדוגמה בלבד. המודל הערוך פלט את הפקודה בלפחות 98.8% מסשני הבדיקה שהכילו את הטריגר, ובאף אחד מהסשנים בלי טריגר שנבדקו.
  • כשהמודל יצר טקסט באופן חופשי, ולא נבדק על סשנים מוקלטים, השיעור היה כ־99%. באחת התצורות הפקודה הופיעה גם ב־0.14% מהסשנים בלי טריגר.
  • ב־Qwen וב־Gemma שיעור ההצלחה נע בין כ־51% ל־99.5%, בהתאם לתצורה.
  • המבחנים כמעט לא זזו. גרסאות Qwen הערוכות שמרו על רוב הציונים שלהן ב־AIME במתמטיקה, ושיעור ההצלחה שלהן ב־Terminal-Bench ירד מ־46.6% ל־42.0%.

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

The Hacker News מתאר גם הדגמה שאחד החוקרים פרסם ב־X. מודל Nemotron-Terminal-8B עם דלת אחורית התבקש לתקן שרת Node.js ועבד כרגיל במשך שלושה סבבים. כשהשרת החזיר שגיאת התחברות, המודל פלט פקודה להורדה והרצה של סקריפט מרוחק. הסקריפט רץ כ־root בתוך קונטיינר הבדיקה והדפיס כותרת שמציינת שמדובר בהדגמה. The Hacker News

איך המתקפה עובדת, שלב אחרי שלב

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

1. להשיג גישת כתיבה למשקלים

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

  • פרסום מודל ששונה ב־Hub ציבורי, למשל בתור “קוונטיזציה מהירה יותר” או “Fine-tune משופר”.
  • חשבון גנוב אצל גוף שמפרסם מודלים.
  • צינור בנייה או דלי אחסון שנפרצו.
  • גורם פנימי עם גישת כתיבה לפני הפריסה.

Microsoft מדגישה שזו לא טענה נגד מודלים פתוחים. גם מודלים שפותחו בתוך הארגון חשופים באותה מידה, אם מישהו יכול לכתוב ל־checkpoint.

מה זה אומר עבורכם: השאלה החשובה היא לא “האם זה מודל טוב?”, אלא “האם זה בדיוק הקובץ שהמפרסם שחרר?”

2. לבחור טריגר ומטען

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

הטריגר הוא טקסט רגיל, לא הוראה. שום דבר בקלט לא נראה כמו מתקפה.

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

3. למצוא נוירונים שאף אחד לא משתמש בהם

שכבות ה־feed-forward של מודל שפה מכילות אלפי נוירונים. חלקם כמעט אף פעם לא נדלקים, והסרה שלהם כמעט לא משנה את התשובות של המודל. Lazarus משתמשת בנוירונים הרדומים האלה, בשכבת ה־feed-forward האחרונה של המודל. הם מקום מסתור טוב, כי שום דבר לא תלוי בהם.

4. להפוך כל אחד מהם לגלאי ולכותב

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

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

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

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

5. להתאים את זה בלי הרצת אימון

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

6. לעבור את המבחנים

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

7. הפעלה

במהלך משימה רגילה, הסוכן נתקל בטריגר. המודל פולט את המטען בפורמט של קריאת כלי רגילה. ה־Harness מפענח אותה ומריץ אותה, עם כל הגישה שיש לסוכן. המאמר מציין שעם גישה ל־shell, לרשת ולפרטי התחברות, שלדבריו היא ברירת המחדל בפריימוורקים פופולריים לסוכנים כמו OpenClaw ו־Hermes Agent, סוכן שהופעל נותן לתוקף בערך התחברות מרחוק בתור המשתמש.

למה זה שונה מ־Prompt Injection

ב־Prompt Injection מסתירים הוראה בתוכן שהסוכן קורא. עקרונית, אפשר לבדוק את התוכן הזה ולסנן אותו.

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

שתי המתקפות מסתיימות באותו מקום: סוכן שמבצע פעולה. לכן הבקרות בשכבת הפעולות, שמפורטות בהמשך, עוזרות מול שתיהן.

AI מצא את זה, ו־AI יכול לעזור למצוא את זה

לפי המאמר, Claude Code עם Opus 4.5 גילה את הרעיון המרכזי באופן עצמאי בתחילת 2026. הכותבים אומרים שזה מרמז שעוזרי קוד יכולים להוריד את החסמים המעשיים לבניית מתקפות ברמת המשקלים.

אותם כלים יכולים לעזור גם למגינים, עם מגבלות. החוקרים ביקשו משני מודלים מתקדמים לבדוק 217 קבצי checkpoint, בלי עותק נקי להשוואה:

  • Claude Opus 5.5 סימן 56% מהמודלים הערוכים, אבל קבע ש־41% מהם נקיים. הוא סימן בטעות כ־2% מהמודלים הנקיים.
  • GPT-6 Astra סימן 73% מהמודלים הערוכים, אבל לא הגיע להחלטה כמעט על אף מודל נקי.

גלאי שנבנה מהדפוסים שהעריכות האלה משאירות במשקלים סימן 91.4% מהמודלים הערוכים בסט הבדיקה, וסימן בטעות 9.7% מהנקיים. סט הבדיקה היה קטן (35 מודלים ערוכים ו־31 נקיים), והגלאי נבדק רק על משפחת העריכות הזאת.

הסיכום של החוקרים עצמם: “שום סריקה לא יכולה להוכיח מעבר לכל ספק שמודל בטוח”. Microsoft Research

איך בודקים מודל

שכבות הגנה: לפני ההורדה, בודקים מפרסם, Hash וחתימה. לפני ההפעלה, משווים למודל הבסיס ובודקים את המשקלים. לפני שפקודה רצה, דורשים אישור. בזמן הריצה, סביבה מבודדת, בלי סודות ורשימת יעדים מותרים. בדיעבד, לוגים.
שתי השכבות הראשונות מנסות לתפוס מודל ששונה. השאר מגבילות את מה שמודל ששונה, Prompt Injection או סתם טעות יכולים לעשות.

1. לדעת בדיוק איזה קובץ אתם מריצים

  • הורידו מהמפרסם המקורי, והשוו את הקבצים ל־Hash שהמפרסם מציג.
  • נעלו גרסה מדויקת במקום מה שהכי חדש.
  • התייחסו ל־Fine-tunes, מיזוגים, קוונטיזציות ומתאמים של צד שלישי כלא אמינים עד שהם עוברים בדיקה. פופולריות היא לא ראיה לבטיחות.
  • שמרו מודלים מאושרים ב־Registry פרטי, וחסמו סוכנים ושרתי הרצה מלמשוך מודלים ישירות מ־Hub ציבורי בזמן ריצה.
  • העדיפו מקור חתום כשהוא קיים. המאמר מפנה למניפסטים חתומים כמו OWASP AIBOM ו־C2PA. חתימה אומרת מי פרסם את הקובץ ושהוא לא השתנה. היא לא אומרת שהקובץ בטוח.

2. להשוות למודל הבסיס המוצהר

זו הבדיקה ש־Microsoft מתארת כאמינה. השוו כל checkpoint למודל הבסיס שהוא טוען שהגיע ממנו, לפני הפריסה ואחרי כל עדכון. דחו מודלים שאי אפשר לאמת את הבסיס שלהם.

החתימה שמחפשים מגיעה ישירות מההנחיות של Microsoft: Fine-tunes וקוונטיזציות לגיטימיים משנים משקלים באופן רחב ומפוזר, ואילו נוירונים מושתלים משנים מעט וקטורי משקל בהרבה. Microsoft Security

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

"""Compare a checkpoint with its declared base model and flag concentrated weight changes.

Usage: python diff_weights.py BASE_DIR CANDIDATE_DIR
Both folders must hold unquantized .safetensors shards with the same tensor names.
"""
import sys
from pathlib import Path

import torch
from safetensors import safe_open

RATIO = 20.0  # a row that changed this many times more than the median row is an outlier
SPARSE = 0.01  # a tensor where under 1% of rows changed is suspicious


def index(folder):
    """Map every tensor name to the shard that holds it."""
    found = {}
    for shard in sorted(Path(folder).glob("*.safetensors")):
        with safe_open(shard, framework="pt") as f:
            for name in f.keys():
                found[name] = shard
    return found


def load(shard, name):
    with safe_open(shard, framework="pt") as f:
        return f.get_tensor(name).float()


def check(name, delta):
    """Look for a few rows or columns that carry most of the change."""
    for axis, dim in (("row", 1), ("column", 0)):
        norms = delta.norm(dim=dim)
        changed = int((norms > 0).sum())
        median = norms.median().item()
        worst = int(norms.argmax())
        ratio = norms[worst].item() / median if median > 0 else float("inf")
        if ratio >= RATIO or changed <= norms.numel() * SPARSE:
            detail = f"{ratio:.0f}x the median {axis}" if median > 0 else f"the median {axis} is unchanged"
            print(f"FLAG {name}: {changed}/{norms.numel()} {axis}s changed; largest is {axis} {worst}, {detail}")


def main(base_dir, candidate_dir):
    base, candidate = index(base_dir), index(candidate_dir)
    for name in sorted(set(base) ^ set(candidate)):
        print(f"FLAG {name}: present in only one checkpoint")
    shared = sorted(set(base) & set(candidate))
    changed = 0
    for name in shared:
        a, b = load(base[name], name), load(candidate[name], name)
        if a.shape != b.shape:
            print(f"FLAG {name}: shape {tuple(a.shape)} became {tuple(b.shape)}")
            continue
        delta = b - a
        if torch.count_nonzero(delta) == 0:
            continue
        changed += 1
        if delta.dim() == 2:
            check(name, delta)
    print(f"{changed} of {len(shared)} shared tensors changed")


if __name__ == "__main__":
    main(*sys.argv[1:3])

בדקתי אותו על קבצי checkpoint סינתטיים קטנים, לא על מודל אמיתי עם דלת אחורית. עריכה של שבעה נוירונים בשכבה האחרונה סומנה בכל שלוש המטריצות שהושפעו, גם כשהוספתי רעש קטן לכל המשקלים כדי להסתיר אותה. “Fine-tune” רחב בדרגה נמוכה לא סומן. בעריכה כזאת, השורה “3 of 17 shared tensors changed” היא סימן בפני עצמה, כי Fine-tune אמיתי כמעט אף פעם לא נוגע רק בשלוש מטריצות בשכבה אחת.

כמה מגבלות לפני שסומכים עליו:

  • זו היוריסטיקה לסוג הזה של עריכה, לא סורק דלתות אחוריות. סימון הוא סיבה להסתכל מקרוב, ותוצאה נקייה היא לא הוכחה. תוקף זהיר יכול לנסות לפזר את השינוי כדי שייטמע.
  • כיילו אותו קודם. הריצו אותו על כמה Fine-tunes שאתם סומכים עליהם כדי לראות איך נראה מצב תקין במודלים שלכם. שינויים לגיטימיים, כמו טוקנים חדשים במטריצת ה־Embedding, יכולים להיות מסומנים.
  • קבצים מקוונטזים צריכים השוואה תואמת. השוו קוונטיזציה לקוונטיזציה של המפרסם עצמו, או בטלו את הקוונטיזציה בשני הקבצים קודם.
  • הוא טוען זוג טנזורים אחד בכל פעם, אבל מטריצות Embedding גדולות עדיין צריכות כמה ג׳יגה־בייט של זיכרון.

3. בלי מודל בסיס, בודקים את המשקלים ישירות

אם אין בסיס אמין להשוואה, המאמר מתאר דפוסים שהעריכות האלה משאירות בשכבת ה־feed-forward האחרונה. ביניהם נוירונים עם נורמות משקל גדולות במיוחד, משקלי קלט שמתיישרים זה עם זה בצורה חריגה, ומשקלי פלט שמצביעים ישירות על טוקן אחד באוצר המילים. גם בקשה מסוכן מתקדם לבדוק את ה־checkpoint תופסת חלק מהעריכות, עם שיעורי הטעות שתוארו למעלה. התייחסו לשניהם כראיה, לא כפסק דין.

4. Red Team לטריגרים, עם מודעות למגבלות

Microsoft ממליצה להוסיף תרחישי חיפוש טריגרים ודלתות אחוריות לבדיקות ה־Red Team של מערכות AI, עם כלים כמו PyRIT, לפני ואחרי כל עדכון. המגבלה ברורה: טריגר יכול להיות כל מחרוזת, ואי אפשר לבדוק את כולן. בדיקות התנהגות משלימות את בדיקת המשקלים. הן לא מחליפות אותה.

איך מגבילים את הנזק

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

  1. הרחיקו סודות. בלי מפתחות ענן ארוכי טווח, מפתחות SSH או פרטי גישה לפרודקשן בסביבה של הסוכן, אלא אם המשימה צריכה אותם. העדיפו טוקנים קצרי חיים עם הרשאות מצומצמות.
  2. הריצו קוד שהסוכן מייצר בסביבה מבודדת. קונטיינר או מכונה וירטואלית חד־פעמיים, בלי גישת כתיבה למחשב המארח. ההנחיה של Microsoft היא לעולם לא לחשוף למודל כלים לכתיבת קבצים או ל־shell במחשב המארח.
  3. הגבילו תעבורה יוצאת. אפשרו רק את היעדים שהמשימה צריכה. המטען במתקפה הזאת חייב להגיע לשרת של התוקף, כדי להוריד סקריפט או כדי לשלוח מידע החוצה. רשימת יעדים מותרים שוברת את השלב הזה.
  4. דרשו אישור לפעולות בעלות השפעה. זה כולל הרצת פקודות שפונות לרשת, שינוי הרשאות, שליחת מידע החוצה ושינוי תשתית. פקודה שמעבירה הורדה ישירות ל־shell צריכה מבט נוסף בכל פעם.
  5. תעדו מה הסוכנים עושים. שמרו לוג של פקודות, שינויי קבצים וקריאות רשת, כדי שאפשר יהיה לזהות ולחקור התנהגות חריגה.
  6. תנו לכל סוכן זהות משלו עם ההרשאות המינימליות שהוא צריך, כך שסוכן אחד שנפרץ לא יורש את הגישה של כולם.

הניסוח של סאטיה נאדלה, שהחוקרים מצטטים, מתאים כאן: להתייחס למודלים כאל סיכון פנים־ארגוני פוטנציאלי. לא לסמוך עליהם באופן עיוור, להגביל את מה שהם יכולים לעשות ולנטר את מה שהם עושים. Satya Nadella

למי זה הכי רלוונטי

  • מי שמריץ מודלים פתוחים מקומית בתוך סוכנים עם גישה ל־shell, במיוחד קוונטיזציות ו־Fine-tunes של הקהילה. אתם שולטים בקובץ, ולכן האימות שלו באחריותכם.
  • צוותים שמאמנים או עושים Fine-tune למודלים משלהם. הסיכון הוא גורם פנימי, חשבון גנוב או צינור שנפרץ עם גישת כתיבה לאחסון ה־checkpoints. הגנו על האחסון, חתמו על מה שאתם מייצרים והשוו כל עדכון.
  • מי שמשתמש במודלים מתארחים של מעבדות גדולות דרך API. אין לכם שליטה על המשקלים, ולכן סיכון המקור נמצא אצל הספק. Microsoft מציינת שספק אמין שנפרץ הוא עדיין סיכון. המגבלות בזמן ריצה עדיין רלוונטיות גם לכם.

מה אני לוקח מזה

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

אני גם רוצה שלכל הגדרה של סוכן תהיה תשובה ברורה לשאלה אחת: אם המודל הזה מחכה בסתר למחרוזת אחת, מהי הפקודה הגרועה ביותר שהוא יכול להריץ, ומה יעצור אותה?

המחקר נבדק עד 12 בנובמבר 2026. הנתונים לקוחים מהמאמר על Lazarus ומההנחיות שפרסמה Microsoft. פרטי ההדגמה ב־X מובאים כפי שדווחו ב־The Hacker News. המתקפה לא נצפתה בשימוש בפועל, והממצאים עשויים להשתנות ככל שחוקרים נוספים יבדקו אותה.