מדוע הפרצה באפליקצית רמיני היא כל כך מזעזעת

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

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

רמיני
רמיני

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

כשל ראשון – קוד בצד הלקוח שלא עבר uglification.

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

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

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

// "https://www.remini.me/XXXXLiadTestWP.php",
// "https://www.remini.me/XXXXLiadTestWP.php",
// : "https://www.remini.me/XXXXLiadTest.php",
// "https://www.remini.me/XXXXLiadTest.php",
// "https://www.remini.me/XXXXZviTest.php",
// "https://www.remini.me/XXXXZviTest.php",

כשל שני – שרת שלא השתמש באותנטיקציה כלל

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

כשל שלישי – API שחשוף ל-SQL injection

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

כשל רביעי – מסד נתונים שלא השתמש בסיסמה ושם המשתמש שלו היה root

כדי להתחבר למסד נתונים עם תוכנה לניהול מסד נתונים, צריך להכניס שם משתמש וסיסמה. בכל המקומות (גם באתר שלי), שם המשתמש והסיסמה הם מסובכים על מנת להקשות על תוקפים. על מנת לא להקשות על התוקף, ברמיני בחרו לא להכניס סיסמה בכלל ושם המשתמש של מסד הנתונים היה root. כך שכל תוקף היה יכול להתחבר למסד הנתונים בקלות רבה אפילו בלי לעשות sql injection.

כל מה שנותר לתוקף לעשות הוא להוריד את כל הטבלאות. – הנה רשימה חלקית שלהן:

+-------------------------------------+
| AdoptedPost                         |
| Adult                               |
| AdultDevices                        |
| AdultLoginDevices                   |
| AdultToAffiliateCampaign            |
| AdultToChild                        |
| AdultWithFrame                      |
| Affiliate                           |
| AffiliateCampaign                   |
| AllWeeksNumPosts                    |
| Attendance                          |
| AttendanceStatuses                  |
| BadEmailsInvitations                |
| Child                               |
| ChildAttendance                     |
| ContactUs                           |
| Conversations                       |
| DailySheetActivitiesTypes           |
| DailySheetDetails                   |
| DailySheetDetailsTypes              |
| DailySheetMealTypes                 |
| DigitalContent                      |
| DigitalContentPost                  |
| Email                               |
| EmailElementsText                   |
| Entity                              |
| EventFrames                         |
| EventReminder                       |
| Events                              |
| FamilyUsers                         |
| Followers                           |
| FollowersOfTheAdultChildren         |
| Frame                               |
| FramePeriod                         |
| FramePeriodChildren                 |
| FramePeriodChildren_neta081117      |
| FramePeriodTeacher                  |
| GroupDataFiles                      |
| Invitation                          |
| InvitationChildren                  |
| KindergartenTeacherPerformance      |
| LastTwoWeeksNumPosts                |
| LogCampaign                         |
| LogFunctionTime                     |
| LogPushNotifToViewedProfile         |
| LogTaggingEntities                  |
| LogTries                            |
| LogUser                             |
| LogUserFunction                     |
| LogUserPressSignUpButton            |
| MeasureKindergarten                 |
| Messages                            |
| MessagesViews                       |
| Notification                        |
| Settings                            |
| Tag                                 |
| TagMilestones                       |
| TagName                             |
| TagToEntity                         |
| UrlDirectory                        |
| 1weekAdultPost                      |
| 1weekFramePosts                     |
| Language                            |
| assessmentDetails                   |
| assessmentFrames                    |
| assessmentHeaders                   |
| assessmentLevels                    |
| assessmentObservations              |
| tmp_UsersFromKindergartenInvitation |
| tmp_UsersWhoInvitedFamily           |
| tmp_UsersWhoWhereInvited            |
| tmp_invitedKindergarten             |
| usersUsage                          |
| usersUsageHelp                      |
| usersUsageHelpWithFrame             |
| usersUsageWithFrame                 |
| wizoFrameChildren                   |
| wizoPosts                           |
+-------------------------------------+

לסיכום

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

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

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

מרגיזה. ״מאמצים כבירים״?!? לא מדובר בפירצה מחוכמת. מדובר ב׳פירצה׳ שלא אמורה להתקיים גם באתרים פשוטים. אם יש מומחה אבטחה אחד שאישר את הבדיחה הזו – צריך לקבל את השם שלו כי אסור לו לעבוד במקצוע הזה. כי אני לא ראיתי איש אבטחת מידע אחד שהיה מאשר API שפגיע ל -SQL Injection שהוא הפרצה הכי מוכרת שיש ברשת בערך.

פוסטים נוספים שכדאי לקרוא

בינה מלאכותית

Safeguards על מודל שפה גדול (LLM)

פוסט בשילוב עם פודקאסט וסרטון על ההגנות שאפשר להציב על LLM בסביבת פרודקשן

רספברי פיי

התקנת OpenCanary על רספברי פיי

מה זה OpenCanary ואיך אפשר להתקין אותה על רספברי פיי ולשדרג את אבטחת הרשת הביתית או המשרדית.

תמונת תצוגה של מנעול על מחשב
פתרונות ומאמרים על פיתוח אינטרנט

הגנה מפני XSS עם Trusted Types

תכונה ב-CSP שמאפשרת מניעה כמעט הרמטית להתקפות XSS שכל מפתח ווב צריך להכיר וכדאי שיכיר.

גלילה לראש העמוד