לדלג לתוכן

מה זה הזרקת פקודות בהקשר בינארי, ולמה system היא פונקציה מפחידה

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

מאיפה זה מגיע

בשפת C יש פונקציה בשם system, שמקבלת מחרוזת ומריצה אותה כפקודת מעטפת (shell). מתכנתים משתמשים בה כדי "לקצר תהליכים" - במקום לכתוב קוד שמבצע פעולה מסוימת, פשוט קוראים לפקודת מערכת קיימת שכבר עושה את זה. הבעיה מתחילה כשחלק מהמחרוזת הזאת מגיע מקלט משתמש, בלי סינון.

לדוגמה, תוכנית שמבקשת מהמשתמש שם קובץ ומריצה system("cat " + filename) נראית תמימה לגמרי. אבל אם המשתמש יזין report.txt; rm -rf /home/user, הפקודה שתרוץ בפועל תהיה שתי פקודות נפרדות. ה-shell לא יודע ולא מנסה להבין את הכוונה של המתכנת, הוא פשוט מבצע כל מה שכתוב שם, כולל תווים כמו ;, |, && ו-` שמאפשרים שרשור פקודות.

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

בהזרקת פקודות ווב, לרוב יש שכבות הגנה בדרך - חומת אש, הרשאות מוגבלות של משתמש השרת, ולעיתים גם WAF. בתוכנה בינארית שרצה מקומית, לפעמים עם הרשאות גבוהות (למשל תוכנת ניהול מערכת, דרייבר, או שירות שרץ כ-root), הזרקת פקודה מוצלחת יכולה להיות הדרך הכי מהירה מקלט משתמש פשוט ועד להשתלטות מלאה על המערכת, בלי צורך בשום גלישת חוצץ, ROP, או ידע נמוך-רמה מתוחכם.

זו הסיבה שבבדיקות חדירות ובמחקר חולשות, כל קריאה ל-system, popen, exec עם ארגומנטים המורכבים מקלט חיצוני נחשבת דגל אדום מיידי.

דוגמאות למקומות שבהם זה מסתתר

  • כלי שורת פקודה שמקבלים נתיב קובץ ומעבירים אותו הלאה לתוכנית חיצונית כמו convert, ffmpeg או tar, בלי לבודד את הארגומנט.
  • קושחה (firmware) של ראוטרים ומכשירי IoT, שבהם ממשק הניהול קורא לפקודות מערכת ישירות מתוך שדות טופס.
  • תוכנות שמעבדות קבצים ומריצות סקריפט חיצוני על הקובץ שהועלה, כשחלק משם הקובץ עצמו לא מסונן.

איך מזהים חולשה כזאת בניתוח בינארי

כשחוקרים תוכנית מקומפלת, מחפשים ייבוא (import) של פונקציות כמו system, popen או execve, ואז עוקבים אחורה בזרימת הנתונים - האם הארגומנט שמגיע לפונקציה הזאת מכיל אי פעם קלט שהמשתמש שולט בו, בלי ולידציה שביניהם. כלים כמו Ghidra או IDA עוזרים לעקוב אחרי זרימת הנתונים הזאת בצורה חזותית, וזה בדיוק אחד התרגילים הראשונים שכדאי לתרגל כשלומדים הנדסה לאחור.

איך מונעים את זה נכון

ההגנה הטובה ביותר היא פשוט לא להשתמש ב-system כלל כשיש קלט משתמש מעורב. הפתרון הנכון הוא להשתמש בפונקציות ממשפחת exec (כמו execve) עם מערך ארגומנטים נפרד, כי אז אין שכבת shell שמפרשת תווים מיוחדים - כל ארגומנט מועבר כמות שהוא. אם חובה להשתמש ב-shell, יש לבצע escaping קפדני של כל תו מיוחד, ועדיף להריץ תהליכים כאלה בהרשאות המצומצמות ביותר האפשריות.

איך זה מתחבר לשאר עולם מחקר החולשות

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

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

נתקלתם בקוד שבו לא בטוח אם קריאה ל-system פגיעה או לא? בדיסקורד שלנו תמיד יש מישהו שישמח לעבור על זה איתכם.

הצטרפו לקהילה בדיסקורד

לסיכום

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