✕ סגור 
צור קשר
תודה על ההתעניינות .

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form

יצירת ערוץ תקשורת חשאי בין שני קונטיינרים - חלק א'

יהודה כורסיה
|
Jun 20, 2019
alt="blogs"
Events
alt="blogs"
title="Google"
alt="blogs"
Event

הקדמה

בשנים האחרונות אנו שומעים עוד ועוד על מקרי פריצה לרשתות והדלפת מאגרי מידע עצומים בגודלם (2.2 מיליארד רשומות בינואר השנה, עוד כמעט 700 מיליון רשומות בפברואר, ועוד אינסוף רשומות במהלך השנים האחרונות), במקרים כאלה, נראה שלמצוא SQL Injection בשדה מסוים זה החלק הקל, בעוד שהמשימה של להדליף מאגר ששוקל מעל 800 GB מרשת מאובטחת מבלי שירגישו - נשמעת כמו משימה בלתי אפשרית.

"בתחום אבטחת המחשבים, Covert Channel היא סוג של מתקפה אשר מאפשרת לתוקף להעביר מידע בין שני תהליכים אשר אינם אמורים להיות מסוגלים לתקשר ביניהם על פי מדיניות האבטחה של המערכת" (תרגום חופשי מויקיפדיה).

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

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

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

פרק ראשון - הסבר קצר על Docker:


לא ארחיב פה על הנושא, מפני שאני מניח שאתם מכירים את התחום או שכבר קראתם את ההקדמה למאמר של יגאל אלפנט ותומר זית (ובעיקר כי הם כבר כתבו עליו בצורה מעולה), אך ממש על רגל אחת:
Docker מאפשר לנו להריץ תהליכים רגילים של מערכת ההפעלה בתוך קונטיינרים - בצורה מבודלת, כלומר באופן שבו התהליך יכיר אך רק את הסביבה הקשורה אליו. לדוגמא רק את המערכת קבצים שלו, רק את התהליכים שהוא יצר וכדומה וכן לאפשר הגבלה של משאבים לאותו תהליך, כגון הגבלה של RAM הגבלה של כמות CPU וכדומה.
Docker עושה זאת באמצעות שימוש במודולים הקיימים כבר ב-Linux כדוגמת
Namespace, UnionFS, Cgroupsו-LXC.


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

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

פרק שני - הסבר על המתקפה שמצאתי.

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

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

לצורך ההדגמה נניח והקונטיינרים הוגדרו עם ה-Flag
- network none שבעצם משאיר את הקונטיינר רק עם כרטיס רשת לביצוע Loopback.

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


הוא אמר לי שהם בדקו ושזה כנראה קשור לעובדה המעניינת:


כאשר קונטיינר בודק כמה RAM פנוי יש לו, הוא לא מקבל תשובה לגבי כמה RAM פנוי יש ל-instance שלו אלא כמה RAM פנוי יש ל-Host שעליו הוא יושב.
בהתחלה העניין היה נשמע לי ממש מוזר, ואמרתי לו שאני אחקור על הנושא. חקרתי ומסתבר שהוא צדק ובאמת כאשר קונטיינר עולה אז Docker Engine מבצע mount לקובץ proc/meminfo/ של ה-Host בלינוקס.
התיקיה proc/ הינה "פסודו" מערכת קבצים (היא לא באמת קיימת על הדיסק עצמו), והמערכת הפעלה דואגת לייצר אותה ולשמור אותה בזיכרון. היא נועדה לספק מידע על המערכת (בדגש על processes במערכת ומכאן נובע השם שלה). הקובץ /proc/meminfo הוא "קובץ מיוחד" שמטרתו היא להציג סטטיסטיקה רלוונטית על הזיכרון של מערכת ההפעלה. זאת ההגדרה שלו לפי man page:

This file reports statistics about memory usage on the system.
It is used by free(1) to report the amount of free and used
memory (both physical and swap) on the system as well as the
shared memory and buffers used by the kernel.


למתעניינים: ניתן לקרוא עליו בהרחבה כאן.


נחזור לסיפור שלנו - בצוות של חבר שלי, היו מרימים קונטיינרים עם הגבלת זיכרון כלשהי: ה-JVM לא היה מודע להגבלה שביצעו על הקונטיינר, ולכן הוא לא היה קורא ל-garbage collector. הבעיה הייתה שמבחינת Docker engine הוא כן היה מוגבל בכמות הזיכרון, ולכן הוא היה מקריס אותו!

לקריאה מורחבת על הנושא כולל פתרון של הבעיה מוזמנים לקרוא כאן:
https://developers.redhat.com/blog/2017/03/14/java-inside-docker/

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

ואז נפל לי בעצם האסימון שקיים פה פוטנציאל להתקפה מסוג Covert Channel attack שתאפשר לנו ליצור ערוץ תקשורת סמוי בין שני קונטיינרים.

הרי אם קונטיינר A ירצה להעביר מידע כלשהו לקונטיינר B הוא יכול פשוט להפוך את המידע לצורה בינארית, ואז באמצעות הקצאה או אי הקצאה של כמות קבועה מראש של זיכרון הוא יוכל להעביר "0" ו-"1" שמייצגים את הבינארי ל-B

הסיפור לא נגמר כאן! תוך כדי מחקר על הבעייתיות בשיתוף המידע הקיים באמצעות הקובץ meminfo נתקלתי במאמר המעולה הזה שמזכיר את הנקודה החשובה שקיים עוד מקום שבו קונטיינר יכול לראות מידע הניתן לשינוי ב-Host, המקום הזה הוא ה-Syscall sysinfo זה בעצם קריאה למערכת הפעלה ובקשה לקבל ממנה מידע לגבי משאבי המערכת.

הקטע המעניין במאמר

מה שבעצם אומר שקיים עוד מרחב שבו אפשר להעביר מידע, הסתכלתי קצת על ה-Syscall sysinfo

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

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

פרק שלישי - מימוש.

“Talk is cheap. Show me the code.”
― Linus Torvalds


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

ההדגמה מנסה להמחיש את היכולת של קונטיינר אחד לשלוח פקודה לקונטיינר השני על מנת שהוא יבצע אותה (C&C). ההדגמה מכילה שני קבצים:
קובץ אחד נקרא sender.py זה הקובץ שהקונטיינר שרוצה לשלוח מידע כלשהו מריץ.
וקובץ נוסף שנקרא receiver.py שזה הקובץ שהקונטיינר שרוצה לקבל את המידע מריץ.

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

את הקוד המלא אפשר למצוא כאן.

החלק המרכזי ביותר של sender.py זה:

1. מקבלים מהמשתמש פקודה שאותה נרצה לבקש מהקונטיינר השני לבצע.
2. אנחנו מעבירים אותה לפורמט בינארי.
3. אנחנו מחכים עד לזמן שהגדרנו מראש בשני הצדדים, כרגע זה סתם המתנה של עד הרגע שבו השניות מתחלקות ב-20 בלי שארית. הסיבה להמתנה היא שאנו רוצים לוודא ששני הקונטיינרים מסונכרנים בדיוק על השנייה שבה הם מתחילים להעביר ביניהם את המידע.
4. אנחנו עוברים על הערך הבינארי שאנחנו רוצים להעביר, לדוגמה אנחנו רוצים להעביר את הערך '1' אז נקצה בזיכרון משתנה בגודל קבוע מראש, נחכה גם כן זמן שקבוע מראש עם הקונטיינר הנוסף.
ואם נרצה להעביר את הבינארי '0' אז נחכה רק את הזמן אשר קבוע בין שני הקונטיינרים.


במקביל, הקטע קוד המרכזי ב-receiver.py הינו:

1. מסנכרנים את השעה בדיוק כמו ב-sender
2. נקבל עוד ועוד ערכים בינאריים עד שנקבל null (שמונה אפסים) שמסמן עצירה
3. כל פעם משרשרים ל-string שנוצר
4. בסוף מריצים את זה בתור פקודה
אני ממליץ לעבור על הקוד המלא ב-Github ומי שרוצה מוזמן גם להרחיב אותו או לכתוב שם המלצות לשיפור (יש המון) וגם לפרגן ב-Star לא יזיק ;-)

מי שרוצה לבדוק את זה אצלו מוזמן להשתמש ב-Images המוכנים כלומר פשוט להריץ את הפקודות הבאות
בשני terminals שונים.

docker run --network=none -it yehudacorsia/docker-covert-channel-sender
docker run --network=none -it yehudacorsia/docker-covert-channel-receiver

ולראות את הקסם קורה.

ומי שרוצה מוזמן לבנות את ה-Images בעצמו מה-Dockerfiles המופיעים ב-Github.

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

בנוסף הכנתי גם סרטון שמראה את ההדגמה, ואפשר לראות אותו כאן:

בחלק הבא נדון בתגובת ה-Docker ובמגוון נושאים נוספים.

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

המאמר פורסם לראשונה במגזין אבטחת המידע הישראלי DigitalWhisper

הקדמה

בשנים האחרונות אנו שומעים עוד ועוד על מקרי פריצה לרשתות והדלפת מאגרי מידע עצומים בגודלם (2.2 מיליארד רשומות בינואר השנה, עוד כמעט 700 מיליון רשומות בפברואר, ועוד אינסוף רשומות במהלך השנים האחרונות), במקרים כאלה, נראה שלמצוא SQL Injection בשדה מסוים זה החלק הקל, בעוד שהמשימה של להדליף מאגר ששוקל מעל 800 GB מרשת מאובטחת מבלי שירגישו - נשמעת כמו משימה בלתי אפשרית.

"בתחום אבטחת המחשבים, Covert Channel היא סוג של מתקפה אשר מאפשרת לתוקף להעביר מידע בין שני תהליכים אשר אינם אמורים להיות מסוגלים לתקשר ביניהם על פי מדיניות האבטחה של המערכת" (תרגום חופשי מויקיפדיה).

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

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

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

פרק ראשון - הסבר קצר על Docker:


לא ארחיב פה על הנושא, מפני שאני מניח שאתם מכירים את התחום או שכבר קראתם את ההקדמה למאמר של יגאל אלפנט ותומר זית (ובעיקר כי הם כבר כתבו עליו בצורה מעולה), אך ממש על רגל אחת:
Docker מאפשר לנו להריץ תהליכים רגילים של מערכת ההפעלה בתוך קונטיינרים - בצורה מבודלת, כלומר באופן שבו התהליך יכיר אך רק את הסביבה הקשורה אליו. לדוגמא רק את המערכת קבצים שלו, רק את התהליכים שהוא יצר וכדומה וכן לאפשר הגבלה של משאבים לאותו תהליך, כגון הגבלה של RAM הגבלה של כמות CPU וכדומה.
Docker עושה זאת באמצעות שימוש במודולים הקיימים כבר ב-Linux כדוגמת
Namespace, UnionFS, Cgroupsו-LXC.


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

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

פרק שני - הסבר על המתקפה שמצאתי.

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

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

לצורך ההדגמה נניח והקונטיינרים הוגדרו עם ה-Flag
- network none שבעצם משאיר את הקונטיינר רק עם כרטיס רשת לביצוע Loopback.

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


הוא אמר לי שהם בדקו ושזה כנראה קשור לעובדה המעניינת:


כאשר קונטיינר בודק כמה RAM פנוי יש לו, הוא לא מקבל תשובה לגבי כמה RAM פנוי יש ל-instance שלו אלא כמה RAM פנוי יש ל-Host שעליו הוא יושב.
בהתחלה העניין היה נשמע לי ממש מוזר, ואמרתי לו שאני אחקור על הנושא. חקרתי ומסתבר שהוא צדק ובאמת כאשר קונטיינר עולה אז Docker Engine מבצע mount לקובץ proc/meminfo/ של ה-Host בלינוקס.
התיקיה proc/ הינה "פסודו" מערכת קבצים (היא לא באמת קיימת על הדיסק עצמו), והמערכת הפעלה דואגת לייצר אותה ולשמור אותה בזיכרון. היא נועדה לספק מידע על המערכת (בדגש על processes במערכת ומכאן נובע השם שלה). הקובץ /proc/meminfo הוא "קובץ מיוחד" שמטרתו היא להציג סטטיסטיקה רלוונטית על הזיכרון של מערכת ההפעלה. זאת ההגדרה שלו לפי man page:

This file reports statistics about memory usage on the system.
It is used by free(1) to report the amount of free and used
memory (both physical and swap) on the system as well as the
shared memory and buffers used by the kernel.


למתעניינים: ניתן לקרוא עליו בהרחבה כאן.


נחזור לסיפור שלנו - בצוות של חבר שלי, היו מרימים קונטיינרים עם הגבלת זיכרון כלשהי: ה-JVM לא היה מודע להגבלה שביצעו על הקונטיינר, ולכן הוא לא היה קורא ל-garbage collector. הבעיה הייתה שמבחינת Docker engine הוא כן היה מוגבל בכמות הזיכרון, ולכן הוא היה מקריס אותו!

לקריאה מורחבת על הנושא כולל פתרון של הבעיה מוזמנים לקרוא כאן:
https://developers.redhat.com/blog/2017/03/14/java-inside-docker/

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

ואז נפל לי בעצם האסימון שקיים פה פוטנציאל להתקפה מסוג Covert Channel attack שתאפשר לנו ליצור ערוץ תקשורת סמוי בין שני קונטיינרים.

הרי אם קונטיינר A ירצה להעביר מידע כלשהו לקונטיינר B הוא יכול פשוט להפוך את המידע לצורה בינארית, ואז באמצעות הקצאה או אי הקצאה של כמות קבועה מראש של זיכרון הוא יוכל להעביר "0" ו-"1" שמייצגים את הבינארי ל-B

הסיפור לא נגמר כאן! תוך כדי מחקר על הבעייתיות בשיתוף המידע הקיים באמצעות הקובץ meminfo נתקלתי במאמר המעולה הזה שמזכיר את הנקודה החשובה שקיים עוד מקום שבו קונטיינר יכול לראות מידע הניתן לשינוי ב-Host, המקום הזה הוא ה-Syscall sysinfo זה בעצם קריאה למערכת הפעלה ובקשה לקבל ממנה מידע לגבי משאבי המערכת.

הקטע המעניין במאמר

מה שבעצם אומר שקיים עוד מרחב שבו אפשר להעביר מידע, הסתכלתי קצת על ה-Syscall sysinfo

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

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

פרק שלישי - מימוש.

“Talk is cheap. Show me the code.”
― Linus Torvalds


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

ההדגמה מנסה להמחיש את היכולת של קונטיינר אחד לשלוח פקודה לקונטיינר השני על מנת שהוא יבצע אותה (C&C). ההדגמה מכילה שני קבצים:
קובץ אחד נקרא sender.py זה הקובץ שהקונטיינר שרוצה לשלוח מידע כלשהו מריץ.
וקובץ נוסף שנקרא receiver.py שזה הקובץ שהקונטיינר שרוצה לקבל את המידע מריץ.

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

את הקוד המלא אפשר למצוא כאן.

החלק המרכזי ביותר של sender.py זה:

1. מקבלים מהמשתמש פקודה שאותה נרצה לבקש מהקונטיינר השני לבצע.
2. אנחנו מעבירים אותה לפורמט בינארי.
3. אנחנו מחכים עד לזמן שהגדרנו מראש בשני הצדדים, כרגע זה סתם המתנה של עד הרגע שבו השניות מתחלקות ב-20 בלי שארית. הסיבה להמתנה היא שאנו רוצים לוודא ששני הקונטיינרים מסונכרנים בדיוק על השנייה שבה הם מתחילים להעביר ביניהם את המידע.
4. אנחנו עוברים על הערך הבינארי שאנחנו רוצים להעביר, לדוגמה אנחנו רוצים להעביר את הערך '1' אז נקצה בזיכרון משתנה בגודל קבוע מראש, נחכה גם כן זמן שקבוע מראש עם הקונטיינר הנוסף.
ואם נרצה להעביר את הבינארי '0' אז נחכה רק את הזמן אשר קבוע בין שני הקונטיינרים.


במקביל, הקטע קוד המרכזי ב-receiver.py הינו:

1. מסנכרנים את השעה בדיוק כמו ב-sender
2. נקבל עוד ועוד ערכים בינאריים עד שנקבל null (שמונה אפסים) שמסמן עצירה
3. כל פעם משרשרים ל-string שנוצר
4. בסוף מריצים את זה בתור פקודה
אני ממליץ לעבור על הקוד המלא ב-Github ומי שרוצה מוזמן גם להרחיב אותו או לכתוב שם המלצות לשיפור (יש המון) וגם לפרגן ב-Star לא יזיק ;-)

מי שרוצה לבדוק את זה אצלו מוזמן להשתמש ב-Images המוכנים כלומר פשוט להריץ את הפקודות הבאות
בשני terminals שונים.

docker run --network=none -it yehudacorsia/docker-covert-channel-sender
docker run --network=none -it yehudacorsia/docker-covert-channel-receiver

ולראות את הקסם קורה.

ומי שרוצה מוזמן לבנות את ה-Images בעצמו מה-Dockerfiles המופיעים ב-Github.

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

בנוסף הכנתי גם סרטון שמראה את ההדגמה, ואפשר לראות אותו כאן:

בחלק הבא נדון בתגובת ה-Docker ובמגוון נושאים נוספים.

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

המאמר פורסם לראשונה במגזין אבטחת המידע הישראלי DigitalWhisper

לפרטים נוספים ויצירת קשר עם נציג אורקל

תודה הודעתך התקבלה

הודעתך לא התקבלה - נסה שוב מאוחר יותר

יהודה כורסיה

הירשם לרשימת הדיוור של IsraelClouds

Thank you! Your submission has been received!

Oops! Something went wrong while submitting the form

מילון מונחיםהשירותים שלנו תנאי שימושהרשמה לניוזלטרמדיניות פרטיות