מאי 2025 סימן את חשיפתה של פגיעות אבטחה קריטית, CVE-2025-1974, המכונה IngressNightmare, המשפיעה על בקר ingress-nginx של Kubernetes הפרוס באופן נרחב בתשתיות ענן מקוריות. פגיעות זו מאפשרת לתוקפים לא מאומתים להחדיר תצורות שרירותיות ל-NGINX, מה שעלול להוביל ל-RCE (ביצוע קוד מרחוק) לא מורשה ולפגיעה מלאה באשכול.
כחלק מ- OPSWAT במסגרת תוכנית המלגות, עמיתינו ביצעו ניתוח טכני מעמיק כדי להבין טוב יותר את שורש הבעיה, נתיב הניצול ואסטרטגיות הפחתה סביב בעיה חומרתית זו.

סקירה כללית של CVE-2025-1974
CVE-2025-1974 היא פגיעות קריטית של הזרקת תבניות שזוהתה בגרסאות ingress-nginx עד 1.11.4 ובאופן ספציפי 1.12.0. תוקפים בעלי גישה ברמת הצומת לאשכול Kubernetes יכולים לנצל פגם זה כדי לבצע קוד שרירותי באמצעות RCE דרך בקר ingress-nginx אשר, כברירת מחדל, בעל הרשאות נרחבות, כולל גישה לסודות קריטיים בתוך האשכול.
ועדת התגובה לאבטחה של Kubernetes הקצתה לפגיעות זו ציון CVSS v3.1 של 9.8 (חומרה קריטית):

רכיבים מרכזיים בניתוח זה
סקירה כללית של קוברנטס
Kubernetes (K8s) היא פלטפורמת קוד פתוח לאוטומציה של פריסה, קנה מידה וניהול תפעולי של יישומים מבוססי קונטיינרים. אשכולות Kubernetes מורכבים בדרך כלל ממספר מכונות, שיכולות לכלול גם חומרה פיזית וגם מכונות וירטואליות, הפועלות יחד כדי לספק סביבות יישומים זמינות, ניתנות להרחבה וניתנות לניהול.
בקר כניסה של NGINX
בקר כניסה של NGINX (ingress-nginx) הוא בקר כניסה בקוד פתוח הבנוי על גבי שרת האינטרנט של NGINX. הוא פועל בתוך אשכול Kubernetes, ומתפקד בעיקר כ-reverse proxy ומאזן עומסים. בקר זה מפרש משאבי כניסה המוגדרים על ידי משתמשים ומתרגם אותם לתצורות NGINX ניתנות לפעולה כדי לנתב את זרימת התעבורה לתוך ובתוך האשכול.
סקירת קבלה ותפקידה
Ingress-nginx משתלב עם Kubernetes באמצעות שירות webhook בשם AdmissionReview. שירות זה חיוני לעיבוד אובייקטי Ingress מקוריים של Kubernetes ותרגומם לתצורות NGINX מאומתות ונכונות מבחינה תחבירית. בעוד AdmissionReview מבטיח דיוק תצורה, הוא פועל באופן עצמאי מבקר ingress-nginx ובדרך כלל חסר בקרות אימות מחמירות. חוסר אימות קפדני זה הוא גורם מפתח שתרם לניצול CVE-2025-1974.

ניצול פגיעויות וניתוח טכני
מנגנון ניצול
בליבתה, ניצול ה-CVE-2025-1974 מתחיל בבקשה זדונית. תוקפים יוצרים בקשה זדונית ל-webhook של AdmissionReview, מה שמאלץ את NGINX לטעון באופן דינמי ספרייה משותפת בזמן ריצה. בהתבסס על מנגנון זה, עמיתינו ניתחו הן את webhook של AdmissionReview והן את זרימת העבודה של NGINX כדי להבין את נתיב הניצול.

פגיעות בהזרקת תבנית
ב-webhook של AdmissionReview, בעת עיבוד בקשות נכנסות, הפונקציה CheckIngress הופכת אובייקטי Kubernetes Ingress לקבצי תצורה חוקיים של NGINX. הזרימה מתנהלת באופן הבא:

- כל תצורה מנותחת ומועברת ל- generateTemplate כדי שתעוצב בהתאם לתבניות NGINX מוגדרות מראש.
- לאחר מכן, testTemplate מאמת את התצורה שנוצרה מול הקובץ הבינארי NGINX הבסיסי.
כל תצורות NGINX מבוססות על תבניות מוגדרות מראש הנמצאות בקובץ nginx.tmpl בתוך קוד המקור ingress-nginx:

בתוך הפונקציה generateTemplate , התצורה מעובדת כפי שמוצג בקטע הקוד הבא:

עם זאת, אימות וחיטוי הקלט אינם מספיקים. באופן ספציפי, שדה ה- uid מאובייקט Ingress מוכנס ישירות לתבנית התצורה של NGINX, ויוצר נקודת הזרקה. תוקף יכול לנצל זאת על ידי אספקת קלט מעוצב כגון uid="1234#;\n\n}\n}\n}\n injection_value" .
קלט זדוני זה מאפשר הזרקת היקף גלובלי לתבנית NGINX, מה שמאפשר לתוקפים להפעיל הנחיות NGINX שרירותיות ואולי להשיג RCE.

מהזרקת תבנית לביצוע קוד מרחוק
הסבר על הפונקציה testTemplate()
לאחר יצירת תצורת NGINX על ידי הפונקציה generateTemplate , הפונקציה testTemplate יוצרת קובץ תצורה זמני ומפעילה את ספריית NGINX באמצעות הפקודה nginx -c {config_file} -t . פעולה זו מאלצת את הקובץ הבינארי של NGINX לנתח ולאמת את התצורה.

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


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

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

בעת ההפעלה, NGINX טוען את מצבו ההתחלתי, ולאחר מכן מנתח את קבצי התצורה שורה אחר שורה. כל הוראה מטופלת על ידי המבנה nginx_command_t , כאשר ההנחיה ssl_engine מפעילה ישירות את ngx_openssl_commands .


לאחר ניתוח הפונקציה ngx_openssl_commands , גילו עמיתינו שהיא מסתמכת על תמיכה ב-OpenSSL, ובפרט על הפונקציה ENGINE_by_id המשמשת עבור מודולי SSL המואצים על ידי חומרה.

ובזמן ניתוח הפונקציה ENGINE_by_id , קבענו שהיא מאפשרת טעינה דינמית של ספריות משותפות. יתר על כן, אם הספרייה עוברת קומפילציה עם הסיומת __attribute__((constructor)) , ניתן לבצע את הפונקציה המשויכת מיד לאחר הטעינה. משמעות הדבר היא שעל ידי ניצול הוראת ssl_engine , תוקף יכול לטעון ספריות משותפות שרירותיות על המארח, מה שעלול להוביל ל-RCE.
מיקוד בספריות משותפות ואסטרטגיית תקיפה
כדי להקל על ביצוע קוד בצורה אמינה, השלב הבא כרוך בזיהוי ספרייה משותפת. במקום להסתמך על ספריות חיצוניות, גישה בת קיימא ומבוקרת יותר נובעת מההתנהגות של NGINX עצמה: מנגנון buffer של גוף הלקוח. תכונה זו מאפשרת ל-NGINX לפרוק בקשות נכנסות גדולות לקבצים זמניים, ובכך לפתוח הזדמנויות לניצול המבוססות על התנהגות טיפול קבצים צפויה.

כברירת מחדל, כאשר בקשה נכנסת עולה על 8KB, NGINX כותב את גוף הבקשה לקובץ זמני הממוקם בכתובת /tmp/nginx/client-body, תוך שימוש בשם קובץ בפורמט cfg-{random_value}. קבצים זמניים אלה נשמרים עד 60 שניות בין קטעים מוצלחים של הודעה שהתקבלה.

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

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

This code opens a file and keeps it in an active state, closely mimicking the behavior of NGINX's client body buffer mechanism. Using /proc/{pid}/fd directory, attackers can find symbolic links created by the Linux kernel that map open file descriptors to their corresponding file paths. This route allows attackers to reduce the brute-force space to only two variables: the process ID (pid) and the file descriptor (fd).

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

יצירת המטען המכיל את הספרייה המשותפת
כדי להבטיח ביצוע קוד בעת הטעינה, פונקציית נקודת כניסה (entrypoint function) עם סיומת בנאי מוגדרת בתוך הספרייה המשותפת הזדונית. פונקציה זו מבוצעת בעת הטעינה על ידי NGINX ונועדה ליצור חיבור shell הפוך (reverse shell) למארח מרוחק.

לאחר הקומפילציה, גודל הספרייה המשותפת שהתקבלה עלה בנוחות על 8KB, מה שאפשר לאחסן אותה במאגר על ידי NGINX ללא צורך בריפוד נוסף.

לאחר מכן, חברינו יצרו בקשה עם ערך Content-Length מנופח (למשל, 1MB) כדי ליצור אי-התאמה בגודל. זה גרם ל-NGINX לאחסן במאגר את כל הגוף במקום לעבד אותו באופן מיידי, מה שהבטיח שהאובייקט המשותף ייכתב למיקום צפוי.

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

RCE מוצלח דורש שההוראת ssl_engine תפנה לנתיב הנכון לקובץ המאוחסן במאגר. ניתן להשיג זאת באמצעות סקריפט Brute Force אוטומטי שמבצע איטרציות שיטתיות על צירופים אפשריים של מזהי תהליכים ותיאורי קבצים כדי לזהות את הקישור הסמלי התקף המצביע על האובייקט המשותף המאוחסן במאגר.

להלן דוגמה לתבנית NGINX שנוצרה שהפעילה את הניצול לרעה.

לאחר ניצול מוצלח, התוקף יכול היה לקבל גישה ל-shell תחת ההקשר של pod ingress-nginx, אשר כברירת מחדל הייתה לו גישה לסודות אשכול רגישים של Kubernetes.

הפחתה ותיקון
כדי למתן ביעילות את הסיכונים הקשורים ל-CVE-2025-1974, ארגונים זקוקים לפתרון המספק נראות ושליטה על רכיבי הקוד הפתוח שלהם.
OPSWAT SBOM , טכנולוגיה בסיסית בפלטפורמת MetaDefender® , עונה על צורך זה על ידי מתן רשימה של כל רכיבי התוכנה, הספריות, מכולות Docker והתלויות הנמצאות בשימוש. היא מאפשרת לארגונים לעקוב, לאבטח ולעדכן את הרכיבים שלהם באופן יזום.

בדוגמה לעיל, טכנולוגיית SBOM ב MetaDefender Core™ סרק את חבילת nginx-ingress-controller שהכילה את הפגיעות CVE-2025-1974. המערכת סימנה אוטומטית את הבעיה כקריטית וסיפקה הנחיות לגבי גרסאות מתוקנות זמינות, מה שאפשר לצוותים לתעדף ולתקן במהירות את הפגיעות לפני שניתן יהיה לנצל אותה.
OPSWAT SBOM זמין ב MetaDefender Core ו MetaDefender Software Supply Chain™, המאפשר לצוותי אבטחה לזהות ולפעול על פגיעויות מהר יותר . עם OPSWAT SBOM, צוותי אבטחה יכולים:
- איתור מהיר של רכיבים פגיעים - זהה באופן מיידי את רכיבי הקוד הפתוח שנפגעו מהתקפות ביטול סריאליזציה. זה מבטיח פעולה מהירה בתיקון או החלפת הספריות הפגיעות.
- ודא תיקון ועדכונים פרואקטיביים - ניטור רציף של רכיבי קוד פתוח באמצעות OPSWAT SBOM כדי להקדים את תהליכי ביטול הסידורים בנוגע לפגיעויות. OPSWAT SBOM יכול לזהות רכיבים מיושנים או לא מאובטחים, מה שמאפשר עדכונים בזמן והפחתת החשיפה להתקפות.
- לשמור על תאימות ודיווח – OPSWAT SBOM מסייעת לארגונים לעמוד בדרישות התאימות, כאשר מסגרות רגולטוריות מחייבות יותר ויותר שקיפות בשרשראות אספקה של תוכנה.
