אבטחה בקרנל לינוקס - למה זה היעד הכי יקר לתקיפה, ואיך מגנים עליו¶
אם תוקף מצליח להריץ קוד בהרשאות הקרנל עצמו, המשחק נגמר - הוא לא כפוף לאף אחת מההגנות שחלות על תוכנית רגילה במרחב המשתמש. בגלל זה, אבטחת הקרנל היא לא עוד רכיב באבטחת המערכת, היא הבסיס שעליו כל שאר ההגנות נשענות. אם הקרנל נפגע, אין באמת "עוד קו הגנה" מתחתיו.
למה הקרנל בדיוק כזה יעד מפתה¶
הקרנל רץ בהרשאה הגבוהה ביותר במערכת, ומקבל גישה מלאה לכל הזיכרון הפיזי ולכל החומרה. חולשה בו לא נשארת מוגבלת לתהליך בודד - היא יכולה לתת לתוקף שליטה על המערכת כולה, לעקוף כל הרשאה של משתמש, ולהסתיר את עצמה מתחת לכל כלי ניטור שרץ במרחב המשתמש. משטח התקיפה כולל כל דבר שקוד מרמת משתמש יכול לגעת בו - קריאות מערכת, דרייברים, ומודולי קרנל שנטענים דינמית.
Capabilities - פירוק ההרשאה המוחלטת של root¶
בעבר, המערכת הכירה רק בשתי רמות - משתמש רגיל, או root, עם שליטה מוחלטת על הכל. מודל ה-capabilities מפרק את ההרשאות המוחלטות האלה לחתיכות נפרדות וממוקדות - יכולת לפתוח פורטים נמוכים ברשת, יכולת לשנות בעלות על קבצים, יכולת לטעון מודולי קרנל, ועוד עשרות capabilities ספציפיות. תהליך יכול לקבל בדיוק ה-capability שהוא צריך, בלי כל השאר - כך שגם אם הוא נפרץ, הנזק האפשרי מוגבל בהרבה מאשר אם היה רץ עם הרשאת root מלאה.
seccomp - הגבלת אילו קריאות מערכת בכלל מותרות¶
seccomp מאפשר לתהליך להגביל את עצמו מראש לרשימה מצומצמת של קריאות מערכת שהוא בכלל מרשה לעצמו לבצע. אם תהליך יודע מראש שהוא לעולם לא צריך לפתוח קבצים חדשים או ליצור סוקטים, הוא יכול לחסום את הקריאות האלה מראש. אם קוד זדוני יצליח להשתלט על התהליך, הוא עדיין יהיה כבול לרשימת הקריאות המורשית - כל ניסיון לבצע קריאת מערכת שלא ברשימה יגרום לחסימה מיידית או לסיום התהליך. זה בדיוק מנגנון ההגנה שדפדפנים ומכולות רבות משתמשים בו כדי לצמצם את הנזק הפוטנציאלי מכל תהליך בודד.
LSM - מסגרת מודולי האבטחה¶
Linux Security Modules היא מסגרת בתוך הקרנל שמאפשרת להוסיף בדיקות הרשאה נוספות, מעבר להרשאות הבסיסיות של קבצים. SELinux ו-AppArmor, שתי המימושים הנפוצים ביותר, מגדירים מדיניות מפורטת - איזה תהליך מותר לו לגשת לאילו קבצים, להתחבר לאילו רשתות, ולהריץ אילו פעולות, גם אם הרשאות הקבצים הרגילות היו מרשות זאת. כך, גם אם תהליך נפרץ ומריץ קוד בשם המשתמש שהריץ אותו, מדיניות ה-LSM עדיין יכולה לחסום אותו מלגעת בקבצים שלא קשורים בכלל לתפקיד הרגיל שלו.
Namespaces ו-cgroups - הבסיס של בידוד מכולות¶
namespaces מאפשרים לקרנל לתת לתהליך "תפיסה" מוגבלת של המערכת - הוא רואה רשימת תהליכים משלו, מערכת קבצים משלו, ורשת משלו, גם אם בפועל הוא רץ על אותו קרנל בדיוק כמו כל תהליך אחר. cgroups, לעומת זאת, מגבילים כמה משאבים, מעבד, זיכרון, רוחב פס, תהליך מסוים מותר לו לצרוך. יחד, שני המנגנונים האלה הם הבסיס הטכני שעליו בנויות טכנולוגיות מכולות כמו Docker - לא וירטואליזציה מלאה, אלא בידוד חכם בתוך אותו קרנל משותף.
KASLR ו-חתימת מודולים¶
בדיוק כמו שתהליכים במרחב משתמש נהנים מ-ASLR שמערבב את כתובות הזיכרון שלהם, גם הקרנל עצמו נהנה ממנגנון מקביל - KASLR, שמערבב את הכתובת שבה הקרנל עצמו נטען לזיכרון בכל אתחול, ומקשה על תוקף שרוצה לנצל חולשה בקרנל לדעת מראש היכן בזיכרון למצוא פונקציות ומבנים ספציפיים. בנוסף, מערכות מודרניות דורשות שמודולי קרנל יהיו חתומים דיגיטלית, כדי למנוע טעינה של קוד זדוני כמודול קרנל לגיטימי למראית עין.
למה שכבות מרובות, ולא הגנה אחת¶
אף אחד מהמנגנונים האלה לא מונע לבד כל תקיפה. הרעיון הוא הגנה בשכבות - גם אם תוקף עוקף מנגנון אחד, המנגנונים האחרים עדיין מגבילים אותו. זה בדיוק ההיגיון שמאחורי עיצוב אבטחה טוב בכל מערכת, ולא רק בקרנל.
זה בדיוק סוג ההבנה שאנחנו בונים בקורס ליבת המחשב - איך שכבות ההגנה של הקרנל בנויות, לא רק מה קורה כשהן נכשלות.
ואם אתם מתחילים להתעניין במחקר חולשות בקרנל, יש קהילה שכבר צועדת בדרך הזאת.
לסיכום¶
הקרנל הוא היעד עם התגמול הכי גבוה לכל תוקף, בדיוק כי הוא רץ בהרשאה שאין מעליה כלום. capabilities מפרקות את הרשאת root לחתיכות ממוקדות, seccomp מגביל אילו קריאות מערכת תהליך בכלל מרשה לעצמו, LSM מוסיף מדיניות הרשאה מפורטת, ו-namespaces עם cgroups בונים בידוד שעליו נשענות מכולות שלמות. אף מנגנון בודד לא מספיק לבד, אבל יחד הם מצמצמים משמעותית את מה שתוקף יכול לעשות גם אם הוא כבר הצליח לחדור פנימה.