Host Header Injection, המדריך המלא

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

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

החל מHTTP/1.1, כאשר אנו מבצעים בקשה כלשהי לאתר אינטרנט (ולא משנה באיזו Method, בין אם זה POST, GET או כל Method אחרת), יחד עם הבקשה נשלח Header בשם Host. כותרת זו תכיל את דומיין האתר ממנו המשתמש שולח את הבקשה. לדוגמא, כאשר נכנסתם לפוסט שלי בבלוג המציג את כל הHeaders הנשלחים בבקשת HTTP ומשמעותם מבחינת אבטחת מידע, ביצעתם למעשה בקשת GET לכתובת: https://kodkodcyber/headers_http/. אם נעביר את הבקשה בBurp-Suit נוכל לראות שיחד עם בקשה זו נשלח Header בשם HOST שמציג את דומיין הבלוג, הלא הוא kodkodcyber.com:

מהו CDN? ר"ת של CDN הן Content Delivery Network והוא מונח המתאר רשת של שרתים שפזורים ברחבי העולם ושומרים עותק של אתר אינטרנט ספציפי שמשתמש בשירותיהם, כך שכאשר אני פונה לאתר האינטרנט אני מקבל את העותק מהשרת שקרוב אליי ולא מהשרת המקורי של אתר האינטרנט שהיה יכול להיות רחוק יותר והיה צריך יותר זמן בכדי להחזיר לי תשובה, כך שהאתר מרוויח שני דברים משמעותיים: א'. הגולשים שלו מקבלים את התוכן במהירות גדולה יותר. ב'. העומס עליו נמוך משמעותית, שכן הטיפול בגולשים מתחלק בין עשרות שרתים ולא נופל על שרת בודד. יתרון נוסף ומשמעותי שיש לאתר היא בשל כך שמטבע הדברים ככתובת הIP תופיע לי כתובת הIP של שרת הCDN שמשרת הרבה מאד אתרים ולא של השרת הספציפי של האתר, כך ששרת הCDN משמש כסוג של Proxy ביחס לשרת של האתר המקורי ומגן עליו ממתקפות DDOS (מתקפות מניעת שירות) שיגיעו אם יתבצעו לשרתי הCDN שהן שרתים חזקים ומאובטחים שלרוב יוכלו להתמודד איתם ולא לשרת המקורי של האתר.

אם-כן, באינטרנט המודרני, לHOST Header משמעות גדולה מאד ואם נבצע אנלוגיה לעולם האמתי, אם בעבר היו בישראל יותר ויותר בתים פרטיים, כך שמספר רחוב היה מספיק ע"מ להעביר דואר לנמען, כיום שבישראל יש יותר ויותר בניינים, מספר הרחוב לא מספיק ונדרש גם מספר בית. אותו הדבר באתרי אינטרנט, אם בעבר לכל אתר הייתה כתובת IP מוגדרת וספציפית, כך שלHost Header לא הייתה משמעות גדולה מבחינת ההפניה שלו, כיום עם עולם הCDN והShead Hosting, כתובת הIP לא מספיקה ונדרש גם Host Header.

אם-כן, לHOST Hedader משמעות גדולה מאד וכאשר אנו מבצעים פנייה לכתובת הIP של השרת המארח, הוא יידע לאיזה שרת ספציפי להפנות אותנו בעזרת הHost Header שלנו.

Host Header Injection

התקפת Host Header Injection עוסקת במקרים בהם השרת אינו מבצע אימות כראוי של הHost Header וסומך על תוכנו באופן בלעדי, כך ששינוי הHost Header לHost Header לא נכון יכול להוביל לשורה של ליקויי אבטחה. המתקפות המשמעותית והמוכרות שיכולות להיווצר כתוצאה מכך הן password resert posining, web cache posining, bypassing autentication, virutal-host brute forcing, routing based SSRF ועוד.

password reset posining

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

לדוגמא: https:///kodkodcyber.comreset?token=0ao1b2ms294e5f6g7h8i9j

כעת תתארו לעצמכם אתר שנכתב בשפת PHP ומציע את אותה פונקציה של שחזור סיסמא. את הלינק הוא שולח למשתמש בצורה הבאה: echo ה$_SERVER['HOST']/rest?token=random_key ולא בצורה של echo domain/rest?token=random_key. כלומר: הוא אינו מגדיר את הדומיין בעצמו, אלא מסתמך בעת לקיחת כתובת הדומיין על הערך HOST שבמשתנה המובנה SERVER_$ – ערך שנלקח מתוך הHost Header.

מאז ומתמיד שימוש בפונקציית ה'שכחתי את הסיסמא' היה נחשב למאובטח שכן הקישור לשחזור הסיסמא נשלח למייל של המשתמש ולאף אחד מלבד המשתמש לא אמורה להיות גישה למייל זה. אך מה יקרה כאשר התוקף ישנה את הHOST Header באותה בקשה שמבצעת את שליחת הלינק לשחזור הסיסמא למייל של המשתמש ויזין בHeader זה דומיין של אתר השייך לו? אם בקוד צד-השרת האתר מסתמך בעת שליחת הלינק על ה$_SERVER['HOST'], הלינק אשר יישלח למשתמש לשחזור סיסמא לא יהיה לינק המפנה אל האתר המקורי, אלא הלינק המפנה אל האתר של התוקף בצורה הבאה: attacker-web/rest?token=random_key. כך, כאשר המשתמש יילחץ על לינק זה (הוא או מנוע אנטי וירוס שיסרוק את הלינק) הוא יבצע בקשת GET לאתר של התוקף עם הrandom_key שמשמש כtoken לשחזור הסיסמא וכעת התוקף יוכל להיכנס לקובץ הlog באתרו, לראות את הtoken, להזדהות עמו באתר, לשנות את הסיסמא ולהשתלט על חשבונו של המשתמש.

דוגמא לקוד PHP בצד שרת אשר פגיע למתקפה זו (כמובן שיש לממש את פונקציית שליחת המייל):

<?php

function send_reset_email($email, $token) {

    $reset_url = "http://" . $_SERVER['HTTP_HOST'] . "/reset?token=" . $token;

    
    mail($email, "Password Reset", "Click here to reset your password: $reset_url");
    echo "mail send successfully";
}

$token = bin2hex(random_bytes(16));

$user_email = "user@example.com";

send_reset_email($user_email, $token);
?>

Web Cache Posining (או בעברית: הרעלת מטמון אינטרנט) היא מתקפה משמעותית נוספת שיכולה להיגרם בעקבות שינוי הHOST Header. על מנת להבין טוב יותר כיצד עובדת המתקפה, אסביר מהו מטמון. מטמון אינטרנט (Caching) זוהי טכניקה המשמשת דפדפני ויישומי אינטרנט ע"מ לשמור עותקים של דפי אינטרנט, תמונות וקטעי וידאו, כך שכאשר ישנו דף אינטרנט אותו מבקשים הגולשים שוב ושוב, במקום שבכל פעם הם יצטרכו לבצע פנייה לשרת ובכך יהיה עומס מיותר על השרת, דפים אלה נשמרים בשרתי מטמון (לצורך הדוגמא, שרת הCDN עליו דיברנו בעבר יכול לשמש גם כשרת מטמון) או בדפדפן עצמו ומגיעים ללקוח. כאשר משתמש מעביר בקשה לשרת האינטרנט, הבקשה עוברת קודם-כל בשרת המטמון שבודק האם יש עותק לתשובה מהרשת על בקשה זהה ואם כן חוסך את הצורך של השרת להתעסק עם בקשה זו ומטפל בה בעצמה עד לסיום פרק הזמן שהוא הגדיר מראש לשמירת המטמון או עד שהוא מקבל עדכון מהשרת המקורי שיש משאב חדש שעודכן והמטמון שמאוחסן אצלו (אצל שרת המטמון) כבר אינו תקף, עדכון זה נקרא cache invalidation request.

הנקודה היא שחלק גדול משרתי המטמון יזהו בקשות זהות על סמך מספר רכיבים בבקשה בדגש על תוכן בקשת הGET/POST שמאוגדים יחד לcache key אחד והם אינם מייחסים חשיבות לHeaders מסוימים כגון הHost Header (בפרט מטמונים מסוג application-level caches). היוצא מזה הוא שאם תוקף ביצע שינוי לHost Header שיפנה לאתר שלו ויאחזר קוד זדוני, שרת הCache יקבל מהשרת המקורי את התגובה עם הקוד הזדוני אותה הוא שולח לתוקף ויאחסן אותה במטמון (התוקף יצטרך לבצע את הבקשה מספר פעמים עד אשר התוקף של מפתח המטמון הקודם יסתיים ושרת המטמון יחכה לתשובה נוספת מהשרת ע"מ לעדכן את מפתח המטמון שלו). ממילא, כעת כל גולש תמים אשר ייכנס לאתר ויעשה בקשה זהה לזו שביצע התוקף (בלי לשנות את הHost Header) יקבל את אותה תשובה שקיבל התוקף והיא התשובה עם הקוד הזדוני, שכן כאמור שרת הCache לא מבצע בדיקה לHost Header ומבחינתו הבקשה של התוקף והבקשה של הגולש התמים היו בקשות זהות.

Routing-based SSRF

מתקפת SSRF (ר"ת של Server Side Request Forgery) היא מתקפה מוכרת וחשובה בפני עצמה ובפסקה זו אסקור את מימושה בעזרת Host Header Injection. באופן עקרוני, מתקפת SSRF עוסקת בפונקציונליות הממומשת באתר עבור ייבוא או שליחת נתונים לכתובת URL חיצונית ובדרכים בהן יכול תוקף לפגוע בפונקציונליות זו, לדוגמא: לגרום לשרת לפנות לכתובת פנימית שלו ולאחזר ממנה מידע. במקרה של Host Header Injection, מימוש SSRF יוכל להיות כאשר לצורך הדוגמא אנו עוברים במערכת Porxy לה יש גישה להמון שרתים ותפקידה להעביר להן את המידע. אם אותן Proxy מוגדרים בצורה לא מאובטחת להעביר בקשות על סמך הHost Header Injection, אם נזין להם בערך הHost Header כתובת IP פנימית, הם עלולים לבצע אליה בקשת GET ולהפנות אותנו אליה. מתקפה זו נקראת Routing-based SSRF.

ע"מ לבדוק אם השרת פגיע לפגיעות זו, התוקף יכניס לו בHost Header את כתובת הדומיין שלו. אם הוא מקבל בקשת DNS, זה אומר ששרת הProxy פגיע שכן הוא ניסה לבצע פנייה. כעת, ינסה התוקף להזין בHost Header כתובת IP פרטית, אליה הוא יוכל להגיע ע"י ביצוע Brute-force על ערך הHost Header כל הכתובת הנפוצות (כגון 192.168.0.1-256) בעזרת הIntruder.

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

בהקשר של שמות מארח, בעיה זו יכולה לגרום גם למתקפה רחבה יותר מSSRF אשר נקראת Virtual host brute-forcing ועוסקת במקרים בהם שרת הוא Sherad Hosting ומארח בתוכו מספר אתרים, בעזרת ביצוע Brute-Force לשמות מארח אפשריים התוקף יוכל להגיע לאתרים אלה שאולי הינם אתרים נסתרים שאינם זמינים לציבור.

מעקף מערכות הגנה בעת מימוש Host Header Injection:

  • פעמים רבות ההגנה היחידה של מערכות מפני Host Header Injection תהיה לנסות ולזהות דפוס מתאים בין הערך של הHost Header לבין הדומיין המקורי, לדוגמא בעזרת התווים האחרונים של הדומיין. כפועל יוצא מכך, כאשר נכניס לHost Header ערך שונה לחלוטין ניתקל בשגיאה. לעומת-זאת, אנו עדיין יכולים להכניס דומיין בעל תווים אחרונים זהים או דפוסים אחרים זהים, כדוגמא: אם הדומיין המקורי הוא cyber-website.com, אנו יכולים להכניס דומיין כמו cybers-website.com.
  • פעמים רבות מערכות ההגנה לא יבצעו בדיקה לשדה הפורט, אלא יבדקו רק את הדומיין שהוזן בHost Header. במקרים כאלו, במקום להזין את מספר הפורט לאחר הדומיין (כדוגמת website.com:443), תוקף יוכל להכניס את הדומיין שלו, לדוגמא: website.com:attacker-website.com.
  • יש פעמים שתגובת האתר כאשר נספק בערך הURL כתובת אתר מוחלטת (absolute URL, כולל פרוטוקול ודומיין, כמו https:website.com/api) עלולה להיות שונה מהתגובה שקיבלנו כאשר הזנו רק את הPATH אליו בוצעה בקשת הAPI (לדוגמא: api/, כלומר כתובת יחסית – relative URL) ועל כן ננסה גם דרך זו.
  • מטעמי ביצועים, אתרים רבים יכולים להיות מוגדרים שלא לבדוק את התוכן של כל בקשה ובקשה שמגיעה אליהם מאותה כתובת IP אלא לבדוק את הבקשה הראשונה ולהניח שהתוכן של הבקשות שהגיעו אחריהן זהה (הרי כל מארח יוצר את אותה תקשורת עם האתר מספר פעמים בשנייה). במקרים כאלה, אם נבצע זיוף של הHost Header בבקשה הראשונה צד השרת יזהה אותה וינטרל אותה, אך אם נבצע זיוף באחת מהבקשות הבאות שלא נבדקות נוכל לממש את המתקפות שדיברנו עליהן (נצטרך כמובן לבצע בקשה אחת טובה). פעולה זו של ניצול הפגיעויות בחיבורים הבאים נקראת Connection state attacks ואם הטכניקות שסקרנו לביצוע מתקפת Host Header Injection לא עלו בידינו, ננסה לבצע אותן גם בעזרתה.
  • דרך נוספת שכדאי לנסות תמיד היא להכניס את הHost Header לאחר הזחה, מה שעלול לבלבל את מערכות ההגנה שיסתכלו עליו כעל המשך של הHeader הקודם ולא כHeader נפרד.
  • ננסה תמיד לבצע גם מתקפות קלאסיות כמו SQL Injection בתוך הHost Header, על הצד שערך הHost מועבר להצהרת Sql, לצורך קריאה כלשהי.
  • אפשרות נוספת היא להכניס שני Host Headers, כאשר Host Header אחד יכיל את דומיין התוקף והHost Header שלאחריו יכיל את הדומיין המקורי. אומנם בחלק מן הפעמים הדבר יוביל לחסימת הבקשה, אך הדבר יכול גם להוביל לכך שצד השרת לא יזהה פגם אבטחתי בHost Header (שכן Host Header אחד כן מכיל את כתובת הדומיין המקורית ותנאי הבדיקה התמקד אך ורק בכך שיש Host Header עם הדומיין המקורי), אך בשל כך שהוא קיבל 2 ערכי Host Headers הוא יבחר את הHost Header הראשון שמכיל את דומיין התוקף. פעולה זו ננסה לבצע גם ע"י הזחת הHeader הראשון כמו בפסקה הקודמת.
  • אפשרות נוספת שננסה היא להכניס את דומיין התוקף תחת Headers אחרים שמבצעים פעולה דומה לזו של הHost Headers ונועדו למקרים בהם הערך HOST מכיל דומיין של שרת Porxy לדוגמא ועל כן קיים Header נוסף המפנה לאתר המקורי, כדוגמת X-Forwarded-Host או X-Host, X-Forwarded-Server, X-HTTP-Host-Override, Forwarded (כמובן שבמקרה שהפעולה לא מצליחה, ננסה לבצע גם על Header זה את מגוון טכניקות המעקף שנסקרו).
  • בדיקת תת דומיינים, דבר שנבצע כמובן גם בכל ניסיון תקיפה אחר.

דרכים למניעת מתקפת Host Header Injection:

ככלל, הדבר החשוב ביותר שעלינו להבין ועל פיו לפעול הוא שהHost Header ניתן לשליטה על ידי המשתמש והוא יכול לשנות אותו. האפשרות הטובה ביותר מבחינתנו תהיה לא לבצע שימוש בערך של הHost Header בקוד צד השרת. ככלל, כאשר אנו צריכים לבצע קריאה למשאב פנימי נעשה זאת בעזרת relative URL ולא absolute URL, כך ששינוי על הHost Header לא יהיה רלוונטי ולא יוכל להשפיע. אם בכל זאת אנו צריכים לבצע שימוש בכתובת URL מוחלטת, נוודא שאנו לוקחים אותה מתוך קובץ הconfiguration שלנו לדוגמא ולא מתוך הHost Header.

אם אנו נדרשים בכל זאת להשתמש בHost Header, עלינו לוודא את הערך הקיים בו מול רשימת הדומיינים שלנו ולדחות דומיינים לא מזוהים (כמובן, גם אם אנו לא מזהים כרגע שימוש בHeader זה, נקשיח אותו באמצעות פעולה זו). הדבר תקף כמובן גם לגבי שימוש בProxy שלגביו עלינו לוודא שהProxy מוגדר להפנות בקשת אך ורק לרשימת הדומיינים המותרים.

כמו-כן, יש לוודא שההגנות שנסקרו תקפות לגבי כל הHost Header הנתמכים (כגון X-Forwarded-Host), ולא לסגור את הפרצה בHost Headers, אך להשאיר אותה פתוחה בתחליפים שלו.

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *