לדלג לתוכן

1.6 הזרקת פקודות תרגול

תרגול - command injection ועקיפת מסננים

בתרגול הזה תעקפו בעצמכם שלושה מסננים בשלושה אתגרים מ-pwnable.kr: cmd1, cmd2 ו-shellshock. המטרה בכל אחד היא לקרוא את קובץ ה-flag למרות המגבלות. אל תסתכלו בפתרון לפני שניסיתם - כל טעות כאן מלמדת אתכם משהו על איך המעטפת חושבת.

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

// vuln.c - local dummy in the style of cmd1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int bad(char *s) {
    return (strstr(s, "flag") != 0) ||
           (strstr(s, "sh")   != 0) ||
           (strstr(s, "tmp")  != 0);
}

int main(int argc, char **argv) {
    if (argc < 2) return 1;
    putenv("PATH=/thisisnotapath");
    if (bad(argv[1])) { puts("nope"); return 0; }
    system(argv[1]);
    return 0;
}
gcc -o vuln vuln.c
echo "SECRET{local-test}" > flag
./vuln 'cat flag'      # will be rejected - the word flag is blocked
./vuln '/bin/cat fl?g' # try to bypass

תרגיל 1 - cmd1

התחברו לאתגר:

ssh cmd1@pwnable.kr -p2222

הסתכלו ב-cmd1.c. המסנן חוסם את תת-המחרוזות flag, sh, tmp, ולפני ה-system() יש putenv("PATH=/thisisnotapath").

המטרה: לקרוא את הקובץ flag שנמצא בתיקיית הבית.

  • רמז 1: אתם מעבירים ארגומנט אחד לתוכנית: ./cmd1 '<הפקודה שלכם>'. הפקודה מגיעה ל-/bin/sh -c.
  • רמז 2: ה-PATH שבור, אז cat flag לא ימצא את cat. איך מריצים תוכנית בלי PATH תקין?
  • רמז 3: המילה flag חסומה כתת-מחרוזת. איך מבקשים מהמעטפת לקרוא קובץ ששמו flag בלי לכתוב את הרצף f-l-a-g ברציפות? חשבו על גלוב, או על ציטוט ריק באמצע המילה.
  • רמז 4: היזהרו גם מהמילה sh - היא חבויה במקומות מפתיעים.

בדקו: אילו מבין /bin/cat fl?g, /bin/cat fl''ag, PATH=/bin cat f* עובדים, ולמה? האם כולם עוקפים גם את sh וגם את flag?


תרגיל 2 - cmd2

התחברו:

ssh cmd2@pwnable.kr -p2222

הסתכלו ב-cmd2.c. המסנן כאן הרבה יותר מחמיר. הוא חוסם את =, PATH, export, את התו /, את התו backtick, ואת flag. גם כאן ה-PATH מושבת.

המטרה: לקרוא את flag.

  • רמז 1: עכשיו אי אפשר לכתוב /bin/cat כי / חסום. אי אפשר PATH=/bin כי =, PATH ו-/ כולם חסומים. צריך ליצור את התו / מבלי להקליד אותו.
  • רמז 2: יש משתני סביבה שהערך שלהם כבר מכיל /. למשל, מה הערך של $PWD? האם תוכלו לחתוך ממנו רק את התו הראשון? (בדקו: echo ${PWD:0:1} - האם זה עובד תחת המעטפת של האתגר?)
  • רמז 3: אם הרחבת התת-מחרוזת לא נתמכת (dash מול bash), יש דרך אחרת לגמרי: המסנן בודק רק את argv, לא את הסביבה. מה אם תגדירו משתנה סביבה לפני ההרצה, שמכיל את כל התווים האסורים, ותעבירו לתוכנית רק את שם המשתנה?
  • רמז 4: גם כאן המילה flag חסומה. השתמשו בגלוב (* או f*) כדי לא לכתוב אותה.

נסו לפחות שתי שיטות שונות ותבינו במה כל אחת תלויה.


תרגיל 3 - shellshock

התחברו:

ssh shellshock@pwnable.kr -p2222

הסתכלו ב-shellshock.c. הבינארי מקבע הרשאות (setresuid/setresgid) ואז מריץ bash vulnerable ששמור בתיקייה, עם -c 'echo shock_me'.

המטרה: לנצל את CVE-2014-6271 כדי להריץ פקודה משלכם בהרשאות של הבינארי ולקרוא את flag.

  • רמז 1: הזכרו בבדיקה הקלאסית: env x='() { :;}; echo vulnerable' bash -c "echo test". מה החלק בתוך המשתנה שגורם להרצה מיידית?
  • רמז 2: אתם לא מריצים את ה-bash הvulnerable ישירות - הבינארי shellshock מריץ אותו בשבילכם. מה שאתם שולטים בו הוא הסביבה. אילו משתני סביבה יורשו לתהליך שהבינארי יוצר?
  • רמז 3: הגדירו משתנה סביבה עם payload בסגנון Shellshock שהקוד הנגרר שלו קורא את flag, ואז הריצו ./shellshock. זכרו שהקוד ירוץ בהרשאות שהבינארי קיבע, אז הוא יוכל לקרוא את הדגל.
  • רמז 4: אפשר לעשות את זה בשורה אחת עם env VAR=... ./shellshock, או קודם export VAR=... ואז ./shellshock.

בדקו לעצמכם: מדוע ה-payload חייב לרוץ דרך ה-bash הvulnerable ולא דרך ה-dash שמפעיל אותו? מה היה קורה אם /home/shellshock/bash היה גרסה מתוקנת?


שאלות להעמקה

  • למה execve("/bin/cat", {"cat","flag",NULL}, envp) לא היה vulnerable לאף אחת מהעקיפות האלה?
  • אילו מהטריקים (ציטוט ריק, גלוב, ${PWD:0:1}, משתנה סביבה) תלויים בכך שהמעטפת היא דווקא bash, ואילו יעבדו גם תחת dash?
  • אם הייתם המפתחים של cmd1/cmd2, איך הייתם מתקנים את החולשה נכון - לא על ידי הוספת עוד מילים לרשימה השחורה?