1 of 26

T1 – SQL INJECTION (SQLI)

Slides by Michael Itzhaki

Based on Pinchas Weisberg work

2 of 26

מה בסדר היום

  • What is SQL?
    • אבסטרקציה זה לפרופסורים באקדמיה
  • What is SQLi? What is an injection?
  • Basic SQL injection.
  • Advanced SQL injections.
  • Blind SQL injections.
  • Protecting against injections.

Secure Programming, 2021 BIU

3 of 26

מה זה SQL?

  • שפת שאילתות עבור טיפול במסדי נתונים רלציוניים – דהיינו, מסדי נתונים המאורגנים בצורת טבלאות.
  • השאילתות העיקריות מחולקות לקבוצות הבאות –
    • SELECT – הסוג הנפוץ ביותר – שאילתה עבור ערכים בטבלה.
    • INSERT – הכנסת ערך חדש לטבלה
    • UPDATE/DELETE – עריכת או מחיקת ערך בטבלה

Secure Programming, 2021 BIU

4 of 26

הרצת MySQL

  • ניתן להשתמש בשרתי SQL חינמיים שיש ברשת, וניתן להתקין שרת חינמי על המחשב שלכם. שרת חינמי לדוגמא הוא MySQL.
  • הגרסה הלינוקסית שלו לרוב מגיעה ללא GUI, ובתמורה היא יותר קלת משקל. הגרסה הWindowsית מגיעה עם הרבה מאוד תכולות נחמדות, וכמובן ממשק משתמש נוח.
  • בתרגול זה אנחנו נתמקד בסינטקס של MySQL.
  • למרות ש-SQL היא שפה שלא תלויה במימוש, עדיין יש הבדלים מהותיים בין שרתי SQL שונים – MariaDB, MySQL, MSSql. ההבדלים נמצאים כמעט באופן גורף בתכולות שהן לא מהותיות בשפה – לדוגמא, פונקציות עזר, טבלאות metadata וכו'.
  • אבל, בתור תוקפים, פרטי מימוש הם לרוב המעניינים ביותר, ולכן אנחנו לא נתעלם מהם אף פעם – לעיתים לא נדירות, פרטי מימוש הם מה שמפריד בין אתר תקיף לאתר בטוח.

Secure Programming, 2021 BIU

5 of 26

דוגמא לשימוש ב-SQL באתר אינטרנט פשוט

  • • In a shopping website:
  • .../article.php?id=1 will show the picture of article1 �.../article.php?id=2 will show the picture of article2 �.../article.php?id=3 will show the picture of article3
  • The code behind this page is:

Secure Programming, 2021 BIU

6 of 26

SQLi – basics

  • נניח לרגע שיש לנו טבלת משתמשים
  • בנוסף, נניח שאימות כניסה לאתר נראה ככה:

  • כעת, אנחנו יכולים לבחור לדוגמא
  • user=NonExsitentUser' OR 1=1;--

Secure Programming, 2021 BIU

7 of 26

SQLi – basics

  • לא חייבים להיות נחמדים...

  • כעת, אנחנו יכולים לבחור לדוגמא
  • user=NonExsitentUser'; DROP TABLE Users; --

Secure Programming, 2021 BIU

8 of 26

SQLi – basics

  • התגוננות מאוד מקובלת נגד ההתקפה הקודמת היא הגבלה של query אחד בכל קריאה, כלומר, כאשר אני אבצע את הפעולה
  • SELECT COUNT(*) FROM Users where UserName='NonExistentUser'; DROP TABLE Users; -- ' AND Password='asdf‘
  • רק החלק של ה-SELECT יתבצע. החלק השני לא יתבצע, ויכול לזרוק שגיאה או להחזיר אזהרה. לא טוב.

Secure Programming, 2021 BIU

9 of 26

Secure Programming, 2021 BIU

10 of 26

Secure Programming, 2021 BIU

11 of 26

ממש מהשבוע

Secure Programming, 2021 BIU

12 of 26

Secure Programming, 2021 BIU

13 of 26

SQLi – advanced

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

SELECT database(), version(), current_user();

  • בMySQL נוכל ללמוד על שמות הטבלאות ושמות העמודות בהן ע"י ההזרקה:

UNION SELECT "anything" ,table_name, column_name FROM

information_schema.columns;s

ואז נוכל להזריק:

UNION SELECT "anything", username, password FROM users; --assuming

we learned from the previous query that there's a 'users' table with

.'username', 'password' columns

Secure Programming, 2021 BIU

14 of 26

SQLi – advanced

  • לעיתים, אנחנו לא יודעים כמה עמודות בדיוק חוזרות משאילתה. מכיוון שיש דרישה ש-UNION יקבל טבלאות עם מספר עמודות זהה, ניתן לחפש את כמות העמודות על ידי ניסוי וטעיה – כלומר ננסה להזריק את השאילתות הבאות:
  • SELECT 1
  • SELECT 1,2
  • SELECT 1,2,3
  • ברגע שלא נקבל שגיאה, נדע שהגענו למספר השורות הראוי

Secure Programming, 2021 BIU

15 of 26

SQLi – advanced

  • השיטה הקודמת עם UNION היא נחמדה, אבל עשוייה להיחסם בשרתים עם Execution Policy מחמיר מכיוון שיש בה שתי שאילתות, ובנוסף היא דורשת כמות לינארית של שאילתות – עשוי להוביל לחסימת המשתמש, או לתעבורה חשודה, וכמובן, לוקח יותר זמן.
  • בשאילתת SQL ישנו פיקוד בשם ORDER BY, שממיין את הטבלה החוזרת לפי עמודות. לדוגמא, על מנת למיין משתמשים על פי סדר אלפבתי של השם שלהם, ומיון משני על ידי ID, נוכל להריץ
  • SELECT * FROM Users ORDER BY UserName, UserId
  • בנוסף למיון על פי שמות של שדות, ניתן למיין על פי אינדקס של שדות. כלומר, ניתן לכתוב
  • SELECT * FROM MyTable ORDER BY 1,4;
  • כעת אנחנו יכולים מהר מאוד לחפש את כמות העמודות בטבלה.
  • ORDER BY 1
  • ORDER BY 2
  • ORDER BY 4 -- ERROR! Number of columns is 2<=, <4

Secure Programming, 2021 BIU

16 of 26

דוגמא – פרטי מימוש

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

Secure Programming, 2021 BIU

Server

Query

Microsoft

SELECT @@version

PostgreSQL

SELECT version()

Oracle

SELECT version FROM v$instance

17 of 26

Database passwords

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

Secure Programming, 2021 BIU

18 of 26

Database passwords

  • הפתרון היחסית פשוט לכל הבעיות שהועלו בשקופית הקודמת, היא שמירת Hash של הסיסמא בטבלה במקום שמירת הסיסמא עצמה.
  • תנאי חשוב שה-Hash שלנו צריך לקיים הוא חד-כיווניות – בהינתן ערך hash, צריך להיות מאוד קשה למצוא ערך שמתגבב אליו. זאת על מנת שבן אדם לא יצליח להתאמת בעזרת סיסמא לא נכונה.
  • האמירה לעיל נכונה בפרט עבור ה"ערך המקורי" ממנו נגזר ה-hash.
  • דוגמא טובה ל-hash נפוץ מאוד היום, היא SHA256.
  • sha256(“hello”)=2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
  • sha256(“hello!”)=ce06092fb948d9ffac7d1a376e404b26b7575bcc11ee05a4615fef4fec3a308b

Secure Programming, 2021 BIU

19 of 26

Database passwords

  • יתרון שהרווחנו בלי לשים לב – עבור פונקציות hash סטנדרטיות, אורך הפלט הוא בלתי תלוי באורך הקלט, ולכן אין צורך להגביל את המשתמש באורך הסיסמא שלו, ואפשר לאחסן את ההאשים בטבלאות באופן יותר יעיל.
  • אבל, ישנן שתי בעיות נוספות.
  • אם לשני יוזרים יש את אותה סיסמא, לדוגמא “Password1”, אזי יופיע להם אותו ה-hash בטבלה.
  • אפשר לחשב את ה-hashים של 100 מיליארד הסיסמאות הנפוצות ביותר, ואז לבדוק מי בטבלה שלנו משתמש באחת מהן.�כנראה שהרבה מאוד משתמשים ייבחרו סיסמאות כמו תאריך לידה ומספר טלפון...
  • פתרון – Salting. הוספת ערך אקראי, פומבי, וייחודי לכל משתמש לסיסמא. ניתן לשרשר אותו לסוף הסיסמא, ואפשר להתחלה. נשים לב שחשוב שה-salt מספיק ארוך, ושיהיה ייחודי עבור כל משתמש.
  • בשביל בטיחות נוספת, משתמשים היום ב-KDF במקום בפונקציות תמצות רגילות, בשביל למנוע מנייה על סיסמאות. שימוש ב-KDF נחשב היום לשימוש הנכון ביותר.

Secure Programming, 2021 BIU

20 of 26

התקפות לא מיודעות

  • לעיתים, יש לנו שאילתות SQL שמתבצעות ברקע, מבלי לתת לנו תוצאה ישירה.
  • תקיפה של שאילתות כאלה יכולה להיות הרבה יותר מסובכת, מכיוון שאי אפשר לשלוח אותן באופן אדפטיבי, ואי אפשר לקבל מהן מידע באופן ישיר.
  • אבל בכל זאת, במידה ויש לנו יכולת הרצת קוד (=SQLi) על השרת, אנחנו יכולים ללמוד עליו דברים
  • אם יש לנו יכולת לראות אם השאילתא הצליחה (כלומר, החזירה תוצאות) או נכשלה (קרסה או לא החזירה תוצאות), אנחנו יכולים ללמוד הרבה על הנתונים.
    • אפשר לנסות לבצע שאילתא לא חוקית ולצפות להודעת שגיאה (500 לדוגמא)
    • אפשר לבצע פעולות המתנה, ולראות כמה זמן לוקח לתשובה להגיע
  • בשורה התחתונה, גם במקרה של הזרקה לא מיודעת, אנחנו מצפים לקבל איזה שהיא אינדיקציה להצלחה או כישלון של השאילתא שלנו.
  • גם בלי אינדיקציה אפשר לתקוף, אבל במקרה כזה צריך לאסוף הרבה מידע מקדים על השרת. (איך?)

Secure Programming, 2021 BIU

21 of 26

תנאים ב-SQL

  • כפי שהזכרנו קודם, אנחנו רוצים מידע כלשהו מהשרת על מנת להתקדם בתקיפה סטנדרטית.
  • במידה והשרת מחזיר לנו הודעת שגיאה אם הוא נכשל, אנחנו נרצה להריץ שאילתא שתיכשל באופן מותנה – כלומר, תיכשל רק אם תנאי שהזרקנו מתקיים.
  • ניתן בדרכים מתוחכמות יותר לבצע את אותן הפעולות ללא תנאים, על ידי הזרקות מסדר שני – כלומר, הזרקת קוד שמזריק קוד בעצמו.
  • אבל ב-SQL ניתן להכניס תנאים, כמו בכל שפת תכנות רגילה. לכן, אם השרת מחזיר לנו הודעת שגיאה כשהוא נכשל, ניתן להריץ את השאילתא הבאה:
  • SELECT IF(SUBSTR(Password, 1, 1) >= 'a', (select Password from Users), NULL) AS Blue FROM Users WHERE UserId = 4;
  • השאילתא תיכשל אך ורק אם התנאי מתקיים (למה?), ועל ידיה ניתן למנות על סיסמא.
  • השאילתא תיכשל מכיוון שחייבת לחזור רק שורה אחת, ואצלנו יש יותר.

Secure Programming, 2021 BIU

22 of 26

תזמונים

  • שימוש נוח נוסף, במידה והשרת לא מזליג שום מידע בעל ערך, היא תקיפת תזמון.
  • בשרתי SQL, אפשרי לבקש מהשרת להמתין על ידי פעולות SLEEP או WAIT.
  • באופן זה, ניתן להבין האם תנאי שהזרקנו התקיים על ידי מדידת זמן התגובה של השרת. זוהי שיטה סטטיסטית, אבל עם המתנה מספיק ארוכה הסיכויים שלה להציג מידע שגוי הם אפסיים.
  • התנאי הבא ימתין אך ורק אם התו הראשון של הסיסמא גדול או שווה ל-a. אחרת, יחזור מייד.
  • SELECT IF(SUBSTR(Password, 1, 1) >= 'a', SLEEP(2), NULL) FROM Users WHERE UserId = 4;
  • זוהי דרך נוחה מאוד למנות על סיסמא, אם כי מעט קשה למקבל אותה.

Secure Programming, 2021 BIU

23 of 26

Secure Programming, 2021 BIU

24 of 26

הגנות על שרתים

  • לאחר שלמדנו את הדרך הנפוצה ביותר לתקוף שרתי SQL, ואולי את התקיפה הנפוצה ביותר שקיימת, נכיר את השיטות הבסיסיות להגנה.
  • Prepared statements
  • Access restriction
    • Host
    • Privileges
    • Databases
  • Single Query Policy
  • וכמובן, יש דברים שהם טובים תמיד, לדוגמא:
    • Logging and monitoring
    • Backup
    • Encryption

Secure Programming, 2021 BIU

25 of 26

SQL prepared statements

  • הדרך הטובה והבטוחה ביותר היום להגן על שרתים באופן שורשי, היאprepared statements, ו-quoting. שיטות אלה פעמים רבות נקראות PDO – PHP Data Objects לזכר השפה PHP שמשמחת תוקפים עד היום.
  • Prepared Statements הן שאילתות מוכנות מראש – כלומר, השאילתא מוכנה ללא הפרמטרים, ורק לאחר מכן הפרמטרים נכנסים. מכיוון שהשאילתא כבר "מקומפלת", הפרמטרים לא יכולים לשנות את ההתנהגות שלה. שיטה זו היא השיטה המועדפת כאשר אנחנו מריצים את אותה שאילתה פעמים רבות.
  • שיטה אחרת נחמדה לא פחות, היא שימוש ב-quoting. Quoting לוקח מחרוזת אקראית, והופך אותה למחרוזת בטוחה – כלומר מחרוזת ששרת ה-SQL בוודאות יתייחס אליה בתור נתון ולא בתור קוד.
  • לדוגמא:
  • $p = "' OR 1=1; --"
  • Quote($p) = '\' OR 1=1; --'
  • נראה קל, בפועל די מסובך לביצוע.

Secure Programming, 2021 BIU

26 of 26

Secure Programming, 2021 BIU

‘) DROP CLASS; --