הפקודה הגיעה מכתובת של גוגל, והוסתרה בשדה שאמור להיות מקרי: הבוטנט Cling מנהל נתבים ומצלמות דרך תעבורת STUN
חבילת UDP אחת מגיעה לנתב ביתי. היא באה מהכתובת 74.125.250.129, אותה כתובת שאליה מתורגם stun.l.google.com, אחד משרתי ה-STUN המוכרים ביותר באינטרנט. בשדה שלפי התקן אמור להכיל 12 בייטים של מספר מקרי יש הפעם הוראה: הצף יעד מסוים בתעבורת UDP למשך זמן נתון.
חוקרי Nozomi Networks Labs פרסמו ב-1 באוקטובר את הניתוח של הבוטנט שעומד מאחורי החבילה הזאת, Cling. הם הגיעו אליו דרך הדלת האחורית: בטלמטריה מחיישנים שפרוסים ברשתות לקוחות הם זיהו גל ניסיונות לנצל חולשה ותיקה במכשירי IoT, ורוב הניסיונות נראו כמו סריקה אופורטוניסטית שגרתית. חלק קטן מהם הוביל למשהו אחר, לנוזקה שלא מדברת כמו הבוטנטים ממשפחת Mirai שהחוקרים רגילים לראות. בהמשך נראה איך Cling נכנס למכשיר, איך הוא נשאר שם, ולמה דווקא פרוטוקול שנועד לשיחות וידאו הפך לערוץ הפיקוד שלו.
למה דווקא STUN
נתחיל מהפרוטוקול, כי כל שאר הסיפור נשען עליו. מחשב שיושב מאחורי נתב ביתי לא יודע באיזו כתובת ציבורית הוא נראה מבחוץ, וגם לא לאיזה פורט הנתב מיפה אותו. בשיחת וידאו זה נתון קריטי, כי בלעדיו הצד השני לא יודע לאן בדיוק לשלוח את הווידאו. הפתרון פשוט: המחשב שולח שאלה לשרת חיצוני, והשרת עונה לו "ראיתי אותך מגיע מהכתובת הזאת ומהפורט הזה". לפרוטוקול שעושה את זה קוראים STUN, והוא מוגדר ב-RFC 8489.
מכאן אפשר להבין למה הוא מקום מחבוא טוב. ברשת ארגונית ממוצעת תעבורת STUN היא רעש רקע: Teams, Zoom, Webex וכל אפליקציה שמבוססת WebRTC בדפדפן מייצרות אותה כל הזמן, מול שרתים ציבוריים מפוזרים בעולם. מכשיר שמדבר STUN לא מעלה גבה. החוקרים מציינים שראו בעבר נוזקות שמשתמשות ב-STUN כדי לברר את הכתובת הציבורית של המכשיר הנגוע, שימוש הגיוני ומוכר. Cling הפך את החילופים האלה לחלק מתהליך הרישום וקבלת הפקודות עצמו.
הדרך פנימה, והדרך להישאר
ווקטור החדירה המרכזי הוא CVE-2021-35394, חולשה בציון CVSS 9.8 ברכיב אבחון של Realtek Jungle SDK שמקומפל בדרך כלל כבינארי בשם UDPServer. לפי הרישום ב-NVD מדובר בשורת חולשות של שיבוש זיכרון יחד עם חולשת הזרקת פקודות, וכולן נגישות לתוקף מרוחק בלי שם משתמש וסיסמה. הרכיב הפגיע מוטמע בשורה ארוכה של נתבים, נקודות גישה, מרחיבי טווח ומכשירים משובצים, מהסוג שלא מקבל עדכונים אחרי שיצא מהמדף.
הניצול עצמו אינו מורכב. התוקף שולח למכשיר דטגרמת UDP שפותחת בקידומת קבועה קצרה, ומיד אחריה פקודות shell שהמכשיר הפגיע מריץ. בניסיון שהחוקרים תפסו, התוקף השתמש ב-wget של BusyBox להוריד בינארי משרת HTTP, סימן אותו כקובץ הרצה והפעיל אותו, כשהארגומנט הראשון מציין באיזו שיטת שכפול המכשיר נדבק. (וזאת, אגב, חולשה שנכנסה לקטלוג ה-KEV של CISA בדצמבר 2021, וחמש שנים אחר כך היא עדיין ווקטור חדירה פעיל.)
הדגימה שהחוקרים ניתחו לעומק היא בינארי MIPS, ובתוכו מצאו לוגיקת ניצול מוטמעת לשבע חולשות הזרקת פקודות נוספות במכשירים של יצרנים אחרים: CVE-2014-8361 ב-Realtek SDK, שגם היא בקטלוג ה-KEV מספטמבר 2023, CVE-2023-26801 בנתבי LB-LINK, CVE-2024-3721 ב-DVR של TBK, CVE-2025-34037 בציוד Linksys, CVE-2016-10372 בנתב Eir D1000, CVE-2023-41011 בנתבי FiberHome SR1041F ו-China Mobile HG6543C4, ו-CVE-2016-20016 ב-DVR למצלמות אבטחה של MVPower.
לפני שהיא עושה משהו על המכשיר, הנוזקה בודקת שאין עוד עותק שלה רץ. היא עושה את זה בדרך שמוכרת לכל מי שכתב service ב-Linux: תופסת socket עם SO_REUSEADDR על פורט 33957, ואם זה נכשל היא יוצאת בשקט. אחר כך היא מעתיקה את עצמה לשני מקומות, /root/.cling ו-/usr/local/bin/.cling, ומוסיפה את שני העותקים לקבצי האתחול /etc/inittab, /etc/init.d/rcS ו-/etc/rc.d/rc.boot. כך היא שורדת אתחול גם על מערכות עם init מסורתי וגם על BusyBox.
מנגנון ההישרדות השני מתוחכם יותר, והוא זה שמחזיר אותה גם אחרי ניקיון חלקי. הנוזקה מאתרת את הבינארי של wget, מעבירה אותו לשם wget.r, רושמת את המקום החדש שלו בקובץ wget.p, ומחליפה את wget המקורי בעצמה. גם symlink-ים, כמו ב-BusyBox, מטופלים. מכאן כל קריאה ל-wget על המכשיר, בין אם היא באה מ-cron job, מפעולת תחזוקה או מתוקף אחר שעובר באזור, מריצה בעצם את הנוזקה. היא בודקת באיזה שם קראו לה, ואם זה wget היא קוראת לבינארי האמיתי לפי מה שכתוב ב-wget.p ומעבירה לו את הארגומנטים שקיבלה. בפועל, מי שמנסה להוריד קובץ למכשיר מתקין את הנוזקה מחדש.
עד כאן איך Cling מגיע ונשאר. החלק המעניין יותר הוא איך הוא מקבל פקודות.
ארבעה צעדים שנראים כמו NAT traversal רגיל
התהליך בנוי מארבעה שלבים. ראשית, הבוטנט שולח Binding Request, בקשת ה-STUN הרגילה, לרשימה של 13 שרתי STUN מקודדים בתוך הדגימה, ועושה את זה כל חמש שניות בערך. כאן מופיעה החריגה הראשונה מהתקן: שדה ה-transaction ID, שאמור להיות מספר מקרי, מאופס לחלוטין. החוקרים בדקו את 13 הכתובות וכתבו שבזמן הפרסום כולן נקיות ב-VirusTotal, ורובן מופיעות ברשימות ציבוריות של שרתי STUN פתוחים.
בשלב השני כל שרת עונה כמצופה, ב-Binding Success Response שמכיל את הכתובת הציבורית של המכשיר ואת הפורט שממנו הוא נראה. בשלב השלישי הבוטנט שולח לכל אחד מ-13 השרתים דטגרמת UDP משלו, הודעת רישום שמכילה את כל הפורטים שהוחזרו לו ואת התג שמסמן באיזו שיטה הוא נדבק, ערכים כמו realtek.selfrep או selfrep.router. ההודעה הזאת כבר לא עומדת בהגדרת STUN, והיא נשלחת לפורטים של השרתים. שרת תקני פשוט מתעלם ממנה, ובבדיקות של Nozomi אף שרת לא ענה. בשלב הרביעי הבוטנט מתחיל להמתין לחבילות UDP שיגיעו לאותם פורטים שמופו עבורו, ובהן הפקודות.
מה אפשר לבקש ממנו? להוריד מטען מכתובת ופורט שנמסרים בפקודה ולהריץ אותו, לסרוק את מרחב הכתובות של IPv4 ולנצל את החולשות המוטמעות כדי להדביק עוד מכשירים, לעצור את הסורק, לפתוח מנהרת TCP שמאזינה על פורט מסוים ומקבלת חיבורים מהאינטרנט, לסגור אותה, להתחבר לשרת relay ולתפקד כ-proxy שמעביר תעבורה דרך המכשיר הפרוץ, לעצור את ה-proxy, ולבצע מתקפת מניעת שירות נגד יעד למשך זמן מוגדר. את כל אלה אפשר לקודד בתוך 12 הבייטים של ה-transaction ID.
איך מזהים איזה שרת מתוך 13 משתף פעולה
כאן המחקר נעשה מעניין. אם הודעת הרישום נשלחת לכל 13 השרתים, המפעיל חייב שליטה או נראות לפחות באחד מהם, אחרת הוא לא יודע לאן לשלוח פקודות לבוטים חדשים. השאלה היא איזה. החוקרים התחילו מהתנהגות: שרת STUN תקני מחזיר בתשובה את אותו transaction ID שקיבל בבקשה. הם שלחו ל-13 השרתים בקשות מבוקרות, ואחד מהם, 145.249.115.184, ענה בשדה מאופס במקום להחזיר את מה שנשלח אליו. זה בדיוק מתאים למימוש שהותאם לתעבורה המאופסת של הבוט עצמו.
את האישוש הם קיבלו בניסוי יפה. הם כתבו לקוח שמתחזה למכשיר שנדבק הרגע ומנסה להצטרף לבוטנט, אבל במקום לדווח לכל השרתים את אותה רשימת פורטים, הם דיווחו לכל שרת קבוצת פורטים נפרדת לגמרי. ההיגיון: אם השרת החשוד אכן עובד עם המפעיל, הפקודות יגיעו לפורט שרק הוא שמע עליו. כמה שעות אחרי שהתחילו לשלוח הודעות רישום, אחד הפורטים שהוכרזו רק מול 145.249.115.184 קיבל פקודות מה-C2, שרת השליטה והבקרה שדרכו המפעיל שולח הוראות לבוטים. הפקודה הראשונה הורתה לבוט להשתמש באותו מכשיר כשרת טעינה לנוזקה.
"Cling בולט לא כי הוא מציג טכניקת הפצה חדשה, אלא כי הוא ממחזר התנהגות STUN רגילה לערוץ שליטה ובקרה מעשי", כתבו החוקרים. במהלך כמה ימים של האזנה לערוץ הם קיבלו שורת מטלות לסרוק את האינטרנט ולהתפשט, וגם בקשות הצפה נגד ספק אינטרנט בקוריאה הדרומית, נגד אשכול חישוב באוניברסיטת שיקגו ונגד שני שרתי Minecraft.
הפקודות שנראות כאילו גוגל שלח אותן
ועכשיו לחלק שבאמת חורג מהשגרה. החבילות שנשאו את הפקודות הגיעו מהכתובת 74.125.250.129, שאליה מתורגם stun.l.google.com. כלומר המפעיל לא רק מסתיר פקודה בתוך חבילה שנראית כמו STUN, הוא גורם לה להיראות כמו תשובה לגיטימית מאחד משירותי ה-STUN המזוהים ביותר ברשת.
החוקרים זהירים בהסבר, וכדאי לשמור על אותה זהירות. הם כותבים שאינם מכירים שום דרך חוקית לאלץ שרת STUN להעביר transaction ID מסוים, ושמכיוון שמדובר בתעבורת UDP חד-כיוונית, ההסבר הסביר בעיניהם הוא שהמפעיל יושב ב-ASN שלא אוכף כראוי בדיקת כתובת מקור, אותה פרקטיקה שמוגדרת ב-RFC 2827 ומיועדת למנוע בדיוק את זה. ספק שלא בודק שהחבילות שיוצאות ממנו נושאות כתובת מקור שבאמת שייכת לו מאפשר לזייף את השדה. את ההשערה הזאת מחזק פרט נוסף: הפרש עקבי בערך ה-TTL בין תשובות STUN אמיתיות לבין החבילות שנשאו את הפקודות.
למה זה משנה בפועל? כי הרבה תהליכי טריאז', חוקים בחומת אש ואנליסטים מתייחסים אחרת לתעבורה שבאה מתשתית בעלת מוניטין. חבילת UDP מכתובת לא מוכרת, שבתוכה בייטים מוזרים, היא חשודה. אותה חבילה בדיוק, כשהיא נראית כמו תשובה משרת STUN של גוגל והמכשיר ממילא התחיל לפני רגע תעבורת STUN משלו, נבלעת ברעש הרקע של NAT traversal. המוניטין של כתובת המקור הפסיק להיות סימן מועיל.
מה זה אומר בפועל
מי שמחזיק נתבים, נקודות גישה, מרחיבי טווח או מקליטי מצלמות שחשופים לאינטרנט צריך לבדוק קודם כל אם הם מכילים רכיבי Realtek Jungle SDK פגיעים או אחת מהחולשות האחרות שמופיעות ברשימה למעלה, ולעדכן. במכשירים שאין להם עדכון, וזה המצב של חלק גדול מהציוד הזה, הפתרון הוא לסגור את החשיפה לאינטרנט ולהעביר אותם לאזור רשת מופרד.
בצד הזיהוי, החוקרים מצביעים על כמה חתימות שנוחות לציד: בקשות Binding חוזרות במרווחים של שניות ספורות עם transaction ID מאופס, ודטגרמות UDP שאינן STUN תקני שנשלחות אל פורטים של שרתי STUN. על המכשיר עצמו כדאי לחפש עותקים בשם .cling, שורות שנוספו לקבצי האתחול, ובינארי wget שלצידו יושבים קבצים בשם wget.r ו-wget.p. בציוד משובץ, שבו ממילא כמעט אין טלמטריה מהמכשיר, הניטור ברמת הרשת הוא בדרך כלל מה שנשאר.
תגובות