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¶
התחברו לאתגר:
הסתכלו ב-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¶
התחברו:
הסתכלו ב-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¶
התחברו:
הסתכלו ב-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, איך הייתם מתקנים את החולשה נכון - לא על ידי הוספת עוד מילים לרשימה השחורה?